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


von Stefan M. (picmic)


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!
1
#include <pic.h>
2
3
4
__CONFIG(INTIO & WDTDIS & PWRTEN & MCLRDIS & UNPROTECT \
5
  & UNPROTECT & BORDIS & IESODIS & FCMDIS);
6
7
8
// Variables
9
long int whatIwant;
10
11
#define FOSC 8000000L      // Internal Oscillator to 8Mhz
12
#define CS RA4          // AD Converter Pin
13
#define SDI RB4          // AD Converter Pin
14
#define SCK RB6          // AD Converter Pin
15
16
17
18
19
20
// Read AD Converter Function
21
long int Read3551(void)
22
{ 
23
  short i;
24
  long int result=0;
25
  char byte[3], TrashByte;      // Temp Bytes for reading
26
27
  CS=0;                         // AD Chip Select low to read
28
  while (SDI==1);                // Wait for SDO/RDY to become low (AD internal conversion complete)
29
  for (i=0; i<3; i++)          // Loop for reading Byte 1, 2 and 3
30
  {
31
    SSPBUF=0b00001111;          // Garbage send to start clocking in AD Data Byte 3
32
    while (BF==0);            // Wait for Buffer Full Status Bit to be set 
33
    byte[i]=SSPBUF;              // get byte
34
  }
35
36
  if (byte[2] < 32)            // Last 3 Bits must be zero (no overflow and no negative sign)
37
   result = ((byte[0] * 65536) + (byte[1] * 256) + (byte[2] * 1));    //Add the Bytes to get the whole Value
38
39
  CS=1;                    // AD Chip select high to stop AD sending
40
  return result;              // return the Value
41
}  
42
43
44
45
46
47
main()                // ------- MAIN -------
48
{
49
    
50
// declaration
51
52
  OSCCON = 0x75;        // Oscillator to 8 Mhz
53
  ANSEL = 0;                  // Turn off ADC
54
  TRISB6= 0;          // SCK (AD) is Output (Master Mode)
55
  TRISA4= 0;          // RA4 (CS from AD) is Output
56
  CS=1;            // CS high (AD stops sending)
57
  SSPCON= 0b00110000;      // 00, Enable SPI (SSPEN) , Idle State for SDI clock is high level (CKP), Fosc:4 
58
  SMP= 0;            // SPI Input Data sampled at middle of data output time
59
  CKE= 0;            // SDI Data transmitted on rising Edge of SCK
60
  
61
62
63
// Infinite loop with main program
64
65
for (;;)                     
66
 whatIwant=Read3551();
67
          
68
}                      //  End of MAIN

von Master S. (snowman)


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
1
result = ((byte[0] * 65536) + (byte[1] * 256) + (byte[2] * 1));
würde ich somit so schreiben
1
result = ((unsigned long int)byte[0])<<16 + ((unsigned long int)byte[1])<<8 + (unsigned long int)byte[2];

von Stefan M. (picmic)


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!

von Lampe (Gast)


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 ;

}

von /devnull (Gast)


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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.