www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik PIC und 22 Bit AD Wandler ueber SPI, kriege es nicht gebacken


Autor: Stefan Meier (picmic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen
Ich beschaeftige mich erst seit kurzem mit PICs und habe nun folgendes 
Problem bei dem ich einfach nicht weiterkomme.
Ich habe einen PIC16F690 und einen seperaten MCP3551 AD Wandler mit 22 
Bit, beides vom Hersteller Microchip.
Verbunden ist das ganze ueber die SPI Schnittstelle. Also CLK, CS und 
SDO des AD Wandlers mit den entsprechenden uC Pins verbunden, Vref vom 
AD Wandler liegt direkt parallel mit Vdd an +5V.

Leider liefert mein Programm einfach keinen Wert zurueck, egal was ich 
am AD Wandler fuer eine Spannung messe, der Rueckgabewert ist immer 
null.
Vermutlich habe ich einen Fehler bei der Programmierung der SPI 
Schnittstelle gemacht, oder etwas falsch initialisiert?!
ich hoffe es faellt jemandem etwas ein...

Vielen Dank!!
Gruesse!
#include <pic.h>


__CONFIG(INTIO & WDTDIS & PWRTEN & MCLRDIS & UNPROTECT \
  & UNPROTECT & BORDIS & IESODIS & FCMDIS);


// Variables
long int whatIwant;

#define FOSC 8000000L      // Internal Oscillator to 8Mhz
#define CS RA4          // AD Converter Pin
#define SDI RB4          // AD Converter Pin
#define SCK RB6          // AD Converter Pin





// Read AD Converter Function
long int Read3551(void)
{ 
  short i;
  long int result=0;
  char byte[3], TrashByte;      // Temp Bytes for reading

  CS=0;                         // AD Chip Select low to read
  while (SDI==1);                // Wait for SDO/RDY to become low (AD internal conversion complete)
  for (i=0; i<3; i++)          // Loop for reading Byte 1, 2 and 3
  {
    SSPBUF=0b00001111;          // Garbage send to start clocking in AD Data Byte 3
    while (BF==0);            // Wait for Buffer Full Status Bit to be set 
    byte[i]=SSPBUF;              // get byte
  }

  if (byte[2] < 32)            // Last 3 Bits must be zero (no overflow and no negative sign)
   result = ((byte[0] * 65536) + (byte[1] * 256) + (byte[2] * 1));    //Add the Bytes to get the whole Value

  CS=1;                    // AD Chip select high to stop AD sending
  return result;              // return the Value
}  





main()                // ------- MAIN -------
{
    
// declaration

  OSCCON = 0x75;        // Oscillator to 8 Mhz
  ANSEL = 0;                  // Turn off ADC
  TRISB6= 0;          // SCK (AD) is Output (Master Mode)
  TRISA4= 0;          // RA4 (CS from AD) is Output
  CS=1;            // CS high (AD stops sending)
  SSPCON= 0b00110000;      // 00, Enable SPI (SSPEN) , Idle State for SDI clock is high level (CKP), Fosc:4 
  SMP= 0;            // SPI Input Data sampled at middle of data output time
  CKE= 0;            // SDI Data transmitted on rising Edge of SCK
  


// Infinite loop with main program

for (;;)                     
 whatIwant=Read3551();
          
}                      //  End of MAIN

Autor: Master Snowman (snowman)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich vermute mal, 1. dass du deinen SPI nicht richtig inizialisierst und 
2. muss der 3551 vielleicht auch mit einem befehl "motiviert" werden 
eine messung durchzuführen (letzteres weiss ich nicht). leider kenne ich 
den von dir verwendete compiler nicht, schau dir aber am besten ein 
funktionierendes SPI-beispiel an (wird es sicher geben).

weiter fällt mir auf / habe folgende vorschläge:
- "long int": schreib zur sicherheit "unsigned long int"
  (funktion ebenfalls dann mit "unsigned" vornedran)
- ebenfalls "unsigned" vor "char"
- wieso nur dann ausführen wenn "if (byte[2] < 32)"
- anstatt z.B. "(byte[1] * 256)" schreibe "byte[1]<<8"
  der PIC ist so viel schneller (die 16er-PICs haben noch
  kein hardware-multiplier)
- "(byte[1] * 256)" wird er wahrscheinlich als 8-bit ausrechnen,
  folglich wird es null

die zeile
result = ((byte[0] * 65536) + (byte[1] * 256) + (byte[2] * 1));
würde ich somit so schreiben
result = ((unsigned long int)byte[0])<<16 + ((unsigned long int)byte[1])<<8 + (unsigned long int)byte[2];

Autor: Stefan Meier (picmic)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi
Danke fuer die Verbesserungsvorschlaege.
"if (byte[2] < 32)" hat den Hintergrund dass der AD Wandler seine Daten 
in drei Bytes hintereinander sendet (also 24 Bit Daten).
Die ersten 21 Bit ist die gemessene Zahl, dann kommt das Vorzeichenbit 
sowie zwei "Input out of Range" Bits.
Da ich das Ergebnis nicht gebrauchen kann wenn es negativ oder Out of 
range ist pruefe ich ob die letzten drei Bits gleich null sind (darf 
nicht groesser 128, 64, 32 sein sonst waere eines der Bits eins).

Hauptproblem ist dass die SPI einfach garnicht funktioniert, wenn ich 
die einzelnen Bits auslese z.B. nur byte[1] so ist dieses immer null, 
unabhaengig von der Rechnung die darauf folgt.
Irgendwas laeuft also schief, vermutlich bei der Kommunikation.
Vref direkt auf +5V ist doch in Ordnung oder?

Beispiele von SPI Schnittstellen habe ich mir alle angesehen die ich 
gefunden habe, ich beschaeftige mich seit Tagen mit diesem Problem, 
meiner Meinung nach sollte es funktionieren, tut es aber nicht.

Bin fuer jede Hilfe Dankbar
Liebe Gruesse!

Autor: Lampe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Stefan,
dein MCP3551 braucht nach dem starten (!Chip select für 10 msec auf Low) 
70-80 msec für die Wandlung.
Erst dann kannst Du die Daten per SPI auslesen.

Ich arbeite mit mikroC PRO, Version: 2.5    http://www.mikroe.com/
MCU = PIC18F6720 und 2 Stk. MCP3551

ad_wert1[3] ;
ad_wert2[3] ;
unsigned short  buffer

void read_adc_s ()
{                    // conversation der 2 ADC's startet gleichzeitig
                     // danach wird sequenziell ausgelesen
  PORTC &= 0xF8 ;    // !Chip select  aktiv      RC 0,1,2   -> null
  Delay_ms(10)  ;
  PORTC |= 0x07 ;    // !Chip select  inaktiv    RC 0,1,2   -> eins

  Delay_ms(70) ;

  PORTC.F1 = 0 ;     // !Chip select  ADC 1
  Delay_ms(1)  ;
  for (i = 0; i <= 3; i++)                // Auslesen 1. ADC
       {ad_wert1[i] = Spi_Read(buffer);}
  PORTC.F1 = 1 ;

  PORTC.F2 = 0 ;      // !Chip select  ADC 2
  Delay_ms(1)  ;
  for (i = 0; i <= 3; i++)                // Auslesen 2. ADC
       {ad_wert2[i] = Spi_Read(buffer);}
  PORTC.F2 = 1 ;

}

Autor: /devnull (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Aaahhhhhhhhhhh. Ein Delay ... hilfe.
Man macht eine Procedure fuers Starten und eine fuers Lesen. Dazwischen 
macht man was anderes. Ich lass dazwischen ein paar Timerticks zu 10ms 
vergene, das wird aber im Main gezaehlt.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.