Forum: Mikrocontroller und Digitale Elektronik ADE7758 via SPI


von Jan H. (janiiix3)


Lesenswert?

Hallo Community,

ich bekomme es einfach nicht hin ein Register auszulesen.
Normalerweise sollte ich bei Register "0x48" einen default Wert von 
"0x3F" bekommen.

Bekommen tue ich aber 255.
Es scheint als ob ich meine SPI Initalisierung nicht im Griff habe.

Woher weiß ich denn mit welchem "Mode" ich arbeiten muss für den Chip?

Hier die Initalisierung (Arbeiten tue ich mit einem MEGA32U4) :
1
void spi_master_init(uint8_t prescaler, uint8_t clock_option)
2
{  
3
  /* ONLY FOR MEGA32! */
4
  #if defined(__AVR_ATmega32__)
5
  {
6
    /* set MOSI and SCK output, all others input */
7
    MOSI_DDR |= (1<<MOSI_BIT);
8
    SCK_DDR   |=  (1<<SCK_BIT);
9
    SS_DDR   |= (1<<SS_BIT);
10
  
11
    /* enable SPI, Master */
12
    SPCR |= (1<<SPE)|(1<<MSTR);
13
    
14
    /* clear the bit´s (for multi init) */
15
    SPCR &= ~((1<<SPI2X) | (1<<SPR0) | (1<<SPR1)); // fclk/4
16
  
17
    /* config SPI Bus Speed fclk/prescaler */
18
    switch (prescaler)
19
    {
20
      case 2:   {SPCR |=  (1<<SPI2X);               };break;
21
      case 8:   {SPCR |=  ((1<<SPI2X) | (1<<SPR0));       };break;
22
      case 32:  {SPCR |=  ((1<<SPI2X) | (1<<SPR1));       };break;
23
      case 64:  {SPCR |=  ((1<<SPI2X) | (1<<SPR1) | (1<<SPR0));};break;
24
      default:  {SPCR |=  (1<<SPI2X);               };break;
25
    }
26
  }
27
  #endif  
28
  
29
  /* ONLY FOR MEGA32U4! */
30
  #if defined (__AVR_ATmega32U4__)
31
  {
32
      /* set MOSI and SCK output, all others input */
33
      MOSI_DDR |= (1<<MOSI_BIT);
34
      SCK_DDR   |=  (1<<SCK_BIT);
35
      SS_DDR   |= (1<<SS_BIT);
36
      
37
      /* enable SPI, Master */
38
      SPCR |= ((1<<SPE) | (1<<MSTR));      
39
      
40
      /* config "Clock Polarity and "Clock Phase" */
41
      switch(clock_option)
42
      {
43
        case 0 : {SPCR &= ~((1<<CPOL) | (1<<CPHA)); }break; // mode 1
44
        case 1 : {SPCR |=  (1<<CPHA);        }break; // mode 2
45
        case 2 : {SPCR |=  (1<<CPOL);        }break; // mode 3
46
        case 3 : {SPCR |=  ((1<<CPOL) | (1<<CPHA));  }break; // mode 4
47
      }
48
      
49
      /* config SPI Bus Speed fclk/prescaler */
50
      switch (prescaler)
51
      {
52
        case 2:   {SPSR |= (1<<SPI2X);                  }break; // prescaler 2
53
        case 8:   {SPSR |= (1<<SPI2X); SPCR |= (1<<SPR0);        }break; // prescaler 8
54
        case 32:  {SPSR |= (1<<SPI2X); SPCR |= (1<<SPR1);        }break; // prescaler 32
55
        case 64:  {SPSR |= (1<<SPI2X); SPCR |= (1<<SPR1) | (1<<SPR0); }break; // prescaler 64
56
        default:  {SPSR |= (1<<SPI2X);                  }break; // default = 2
57
      }
58
  }
59
  #endif
60
61
}

von Joe F. (easylife)


Lesenswert?

Die verschiedenen SPI Modes sind hier ganz anschaulich erklärt:
https://de.wikipedia.org/wiki/Serial_Peripheral_Interface#/media/File:SPI_timing_diagram2.svg

SCLK soll im nicht aktiven Zustand low sein
-> CPOL=0

Aus Figure 3 und Figure 4 des ADE7758 Datenblattes siehst du, dass die 
Daten immer auf steigender Clock Flanke wechseln, also auf fallender 
Flanke übernommen werden.
-> CPHA=1

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Jan H. schrieb:
> Bekommen tue ich aber 255.
Brücke mal MOSI und MISO am Master. Dann solltest du bei inaktivem oder 
ausgebautem Slave genau das zurück bekommen, was du sendest.

Insgesamt ist für die Fehlersuche an einem solchen seriellen Bus ein 
Oszilloskop sehr brauchbar. Hast du sowas?

von Jan H. (janiiix3)


Lesenswert?

Lothar M. schrieb:
> Jan H. schrieb:
>> Bekommen tue ich aber 255.
> Brücke mal MOSI und MISO am Master. Dann solltest du bei inaktivem oder
> ausgebautem Slave genau das zurück bekommen, was du sendest.
>
> Insgesamt ist für die Fehlersuche an einem solchen seriellen Bus ein
> Oszilloskop sehr brauchbar. Hast du sowas?

Hallo Lothar,

das mit dem Brücken ist eine sehr gute Idee.
Vermutlich liegt das Problem wirklich nur an der Initalisierung.
Werde es direkt mal mit dem Oszi nachvollziehen.
Vielen lieben dank schonmal ;)

von Jan H. (janiiix3)


Lesenswert?

Also wenn ich es Brücke, bekomme ich genau den Wert zurück den ich auch 
sende.
1
int main(void)
2
{
3
  
4
  DDRD |= (1<<PD5);  
5
  lcd_init(LCD_DISP_ON);
6
  spi_master_init(64,3);
7
  
8
  sei();  
9
10
11
    while (1) 
12
    {
13
    spi_master_transmit(cnt);
14
    cnt = spi_receive();
15
    
16
    _delay_ms(250);    
17
    
18
    if (cnt == 63)
19
    {
20
      PORTD ^= (1<<PD5);  
21
    }
22
    
23
    lcd_gotoxy(2,0);
24
    lcd_puts("");
25
    lcd_gotoxy(1,1);
26
    sprintf(Buffer,"read : %d ", cnt);
27
    lcd_puts(Buffer);
28
    
29
    cnt++;
30
        
31
        
32
    }
33
}

: Bearbeitet durch User
von Joe F. (easylife)


Lesenswert?

Na, dann versuche es doch mal mit der langsamsten Geschwindigkeit 
(prescaler=64) und CPOL0/CPHA1 (clock_option=1)...

also

spi_master_init(64, 1);

: Bearbeitet durch User
von Jan H. (janiiix3)


Lesenswert?

Joe F. schrieb:
> Na, dann versuche es doch mal mit der langsamsten Geschwindigkeit
> (prescaler=64) und CPOL0/CPHA1 (clock_option=1)...
>
> also
>
> spi_master_init(64, 1);

Funzt natürlich auch nicht.
Am Chip (also Slave) muss ich doch nur Versorgungsspannung anlegen, 
DOUT, DIN, SCLK und SS = Masse schalten oder?

von Joe F. (easylife)


Lesenswert?

Jan H. schrieb:
> SS = Masse

SS heist "Slave Select" und darf nicht dauerhaft auf GND sein...

Im Datenblatt ist dieses Signal mit "CS" bezeichnet.

von Jan H. (janiiix3)


Lesenswert?

Joe F. schrieb:
> Jan H. schrieb:
>> SS = Masse
>
> SS heist "Slave Select" und darf nicht dauerhaft auf GND sein...
>
> Im Datenblatt ist dieses Signal mit "CS" bezeichnet.

Ich habe es auch schon Probiert mit einer "fallenden Flanke".

von Joe F. (easylife)


Lesenswert?

Jan H. schrieb:
> Ich habe es auch schon Probiert mit einer "fallenden Flanke".

Verstehe nicht, was das heissen soll.

CS (SS) ist ein Signal, das zu jeder SPI Übertragung dazugehört.

Du selektiert den entsprechenden Slave, bestimmst mit der fallenden 
Flanke den Anfang der Übertragung, und mit steigender Flanke wird das 
Ende der Übertragung angezeigt, und der Slave deselektiert.

Vermutlich fehlt dies alles bei dir.

Guck mal ins Datenblatt, Figure 3 und 4 (Serial Write Timing, Serial 
Read Timing), da siehst du genau, wie sich CS verhalten muss.

Vor dem ersten transmit muss also CS von high auf low gesetzt werden, 
und nach dem letzten write oder read wieder auf high.

: Bearbeitet durch User
von Jan H. (janiiix3)


Lesenswert?

Joe F. schrieb:
> Jan H. schrieb:
>> Ich habe es auch schon Probiert mit einer "fallenden Flanke".
>
> Verstehe nicht, was das heissen soll.
>
> CS (SS) ist ein Signal, das zu jeder SPI Übertragung dazugehört.
>
> Du selektiert den entsprechenden Slave, bestimmst mit der fallenden
> Flanke den Anfang der Übertragung, und mit steigender Flanke wird das
> Ende der Übertragung angezeigt, und der Slave deselektiert.
>
> Vermutlich fehlt dies alles bei dir.
>
> Guck mal ins Datenblatt, Figure 3 und 4 (Serial Write Timing, Serial
> Read Timing), da siehst du genau, wie sich CS verhalten muss.

Okay. Ist mir neu. Habe ich bis jetzt noch bei keinem gesehen.
Meinste das in etwa so ?
1
char ADE7758_read8bit(char reg)
2
{
3
  char ret=0;
4
  
5
  _delay_us(10);
6
  SS_PORT |= (1<<SS_BIT);
7
  SS_PORT &= ~(1<<SS_BIT);
8
  spi_master_transmit(reg);
9
  _delay_us(10);
10
  ret = spi_receive();
11
  SS_PORT |= (1<<SS_BIT);
12
  _delay_us(10);
13
  
14
  return ret;
15
}

von Joe F. (easylife)


Lesenswert?

Jan H. schrieb:
> Okay. Ist mir neu. Habe ich bis jetzt noch bei keinem gesehen.

Tja, man lernt immer etwas dazu.

> Meinste das in etwa so ?

Ja. Der Master muss allerdings auch beim Read die Clock treiben, 
insofern weiss ich nicht, ob spi_receive() die geeignete Funktion ist.

Hast du ein header-filer der verfügbaren SPI Funktionen?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Jan H. schrieb:
> Habe ich bis jetzt noch bei keinem gesehen.
Sieh dir das Datenblatt des ICs an. Wenn dort im Timingdiagramm der SS 
(höchstwahrscheinlich) zum Beginn einer Übertragung au Low und danach 
auf High geht, dann ist der für die Übertragungssynchronisierung nötig.

Oder andersrum:
Der SCLK ist der Schiebetakt für die SPI Flipflops, und der SS ist das 
Übernahmesignal für ein ganzes Wort.

Sieh dir das mal an:
http://www.lothar-miller.de/s9y/archives/15-SPI.html

: Bearbeitet durch Moderator
von Jan H. (janiiix3)


Lesenswert?

Joe F. schrieb:
> Jan H. schrieb:
>> Okay. Ist mir neu. Habe ich bis jetzt noch bei keinem gesehen.
>
> Tja, man lernt immer etwas dazu.
>
>> Meinste das in etwa so ?
>
> Ja. Der Master muss allerdings auch beim Read die Clock treiben,
> insofern weiss ich nicht, ob spi_receive() die geeignete Funktion ist.
>
> Hast du ein header-filer der verfügbaren SPI Funktionen?

Ja, habe ich mir selber erstellt. Das meiste habe ich aus dem 
Datenblatt.
Da wird es auch nicht gemacht mit dem Synchronisationsbit.
Was aber eigentlich eine gute Sache ist.

Werde es morgen direkt mal ausprobieren.


Also wenn ich etwas übertragen will... Von "High" --> "Low" und der 
Slave schickt dann doch direkt die Daten raus, da muss ich den "SS" 
nicht mehr schalten oder?

von Joe F. (easylife)


Lesenswert?

Die unendlichen Weiten des Interwebs verraten mir, dass du folgendes tun 
solltest:
1
WRITE:
2
3
SS_PORT &= ~(1<<SS_BIT); // slave select low
4
spi_master_transmit(0x80 | reg);
5
spi_master_transmit(data_msb);
6
(...)
7
spi_master_transmit(data_lsb);
8
SS_PORT |= (1<<SS_BIT); // slave select high
9
10
11
READ:
12
13
SS_PORT &= ~(1<<SS_BIT); // slave select low
14
spi_master_transmit(reg);
15
data_msb = spi_master_transmit(0);
16
(...)
17
data_lsb = spi_master_transmit(0);
18
SS_PORT |= (1<<SS_BIT); // slave select high

von Joe F. (easylife)


Lesenswert?

Jan H. schrieb:
> Also wenn ich etwas übertragen will... Von "High" --> "Low" und der
> Slave schickt dann doch direkt die Daten raus, da muss ich den "SS"
> nicht mehr schalten oder?

Nein.
Du hast weder das Prinzip von SPI (dass es ein Bus ist, und jeder Slave 
per SS selektiert werden muss, und auch nur dann antworten darf, wenn er 
selektiert ist),

noch das Protokoll deines ICs verstanden.

Lies dich bitte in SPI ein, und dann in das Datenblatt deines ICs.

Dein IC hat Register, die mit einem Write addressiert werden müssen, und 
erst dann kannst du die entsprechenden Registerdaten auslesen.
Zu beachten ist noch, dass bei der Adressierung das oberste Bit dem 
Slave sagt, ob du das Register beschreiben willst, oder den Inhalt 
auslesen möchtest (siehe auch Code oben).

spi_master_transmit() schreibt ein Byte zum Slave und liest auch 
gleichzeitig vom Slave (das ist dann der Rückgabewert).
Bei einem Read muss der Master allerdings die Clock treiben, daher macht 
man üblicherweise einen Write mit einem Null-Byte, und interessiert sich 
dann für die Daten, die der Slave gleichzeitig gesendet hat.

: Bearbeitet durch User
von Jan H. (janiiix3)


Lesenswert?

Joe F. schrieb:
> spi_master_transmit() schreibt ein Byte zum Slave und liest auch
> gleichzeitig vom Slave (das ist dann der Rückgabewert).
> Bei einem Read muss der Master allerdings die Clock treiben, daher macht
> man üblicherweise einen Write mit einem Null-Byte, und interessiert sich
> dann für die Daten, die der Slave gleichzeitig gesendet hat.

Ich denke das übernimmt die Hardware? Wie würde das konkret aussehen?

von Joe F. (easylife)


Lesenswert?

so
Beitrag "Re: ADE7758 via SPI"

guck mal unter READ

für jedes Byte, das du (der Master) vom Slave lesen möchtest, sendest du 
gleichzeitig eine "0", damit die Clock getrieben wird.
Was der Slave während des Sendens der "0" auf MISO zurückliefert, 
liefert die Funktion spi_master_transmit() dann als Rückgabewert.

Die Register deines ICs sind unterschiedlich "breit". Es gibt welche mit 
8, andere mit 16, andere mit 24 Bit (1, 2, 3 Bytes).
Daher musst du die Funktionen zum Beschreiben und Auslesen der Register 
entsprechend flexibel machen, oder du schreibst entsprechend 3 
verschiedene Funktionen.
Ich habe das mit den "(...)" angedeutet.

von Jan H. (janiiix3)


Lesenswert?

Okay, dass hat geklappt. Es war wirklich nur das "dummy" Byte was 
gefehlt hat.

Eine Frage habe ich jedoch noch. Wenn ich jetzt meine Phasen (A,B,C) 
einlesen und berechnen möchte, wo steht das im DB beschrieben? Ich finde 
dazu nichts.

Weiß nur das es ein "24 Bit" Register ist.

von Joe F. (easylife)


Lesenswert?

Im Datenblatt 
http://www.analog.com/media/en/technical-documentation/data-sheets/ADE7758.pdf
sind in Table 17 alle Register aufgeführt.
Ich weiss nicht, welche Information über die Phasen dich interessiert.
Angenommen es wäre "Phase A Current RMS", also Register 0x0a (=10)

Da dieses Register 24 bit breit ist:
1
uint8_t reg;
2
3
uint8_t data_2;
4
uint8_t data_1;
5
uint8_t data_0;
6
7
uint32_t data;
8
9
reg = 0x0a;
10
11
SS_PORT &= ~(1<<SS_BIT); // slave select low
12
spi_master_transmit(reg);
13
data_2 = spi_master_transmit(0);
14
data_1 = spi_master_transmit(0);
15
data_0 = spi_master_transmit(0);
16
SS_PORT |= (1<<SS_BIT); // slave select high
17
18
data = ((uint32_t)(data_2) << 16) | 
19
       ((uint32_t)(data_1) <<  8) |
20
       ((uint32_t)(data_0));

: Bearbeitet durch User
von Jan H. (janiiix3)


Lesenswert?

Joe F. schrieb:
> Im Datenblatt
> http://www.analog.com/media/en/technical-documenta...
> sind in Table 17 alle Register aufgeführt.
> Ich weiss nicht, welche Information über die Phasen dich interessiert.
> Angenommen es wäre "Phase A Current RMS" ist das Register 0x0a (=10)
>
> Da das Register 24 bit breit ist:
> uint8_t reg;
>
> uint8_t data_2;
> uint8_t data_1;
> uint8_t data_0;
>
> uint32_t data;
>
> reg = 0x0a;
>
> SS_PORT &= ~(1<<SS_BIT); // slave select low
> spi_master_transmit(reg);
> data_2 = spi_master_transmit(0);
> data_1 = spi_master_transmit(0);
> data_0 = spi_master_transmit(0);
> SS_PORT |= (1<<SS_BIT); // slave select high
>
> data = ((uint32_t)(data_2) << 16) |
>        ((uint32_t)(data_1) <<  8) |
>        ((uint32_t)(data_0));




Ich möchte gerne mal die Spannung von "Phase_A" lesen was muss da noch 
gerechnet werden?

von Joe F. (easylife)


Lesenswert?

Jan H. schrieb:
> Ich möchte gerne mal die Spannung von "Phase_A" lesen was muss da noch
> gerechnet werden?

Naja, also sorry.
Das steht auch im Datenblatt, ich habe jetzt aber echt keine Lust, ein 
72-seitiges Datenblatt durchzuarbeiten, weil du zu faul dazu bist.
Im Unterschied zu dir werde ich nicht für diese Arbeit bezahlt.

Gut zu hören, dass die SPI Kommunikation jetzt funktioniert.

: Bearbeitet durch User
von Jan H. (janiiix3)


Lesenswert?

Joe F. schrieb:
> Jan H. schrieb:
>> Ich möchte gerne mal die Spannung von "Phase_A" lesen was muss da noch
>> gerechnet werden?
>
> Naja, also sorry.
> Das steht auch im Datenblatt, ich habe jetzt aber echt keine Lust, ein
> 72-seitiges Datenblatt durchzuarbeiten, weil du zu faul dazu bist.
> Im Unterschied zu dir werde ich nicht für diese Arbeit bezahlt.
>
> Gut zu hören, dass die SPI Kommunikation jetzt funktioniert.

Von wem werde ich denn bezahlt???
Ich finde halt nirgends im Datenblatt wie ich die Werte verrechnen muss 
für die Spannung.

von Joe F. (easylife)


Lesenswert?

Jan H. schrieb:
> Von wem werde ich denn bezahlt???

Weiss ich nicht, war nur eine Vermutung, da du offensichtlich tagsüber 
dran arbeitest...

> Ich finde halt nirgends im Datenblatt wie ich die Werte verrechnen muss
> für die Spannung.

S. 29, "Voltage Channel RMS Calculation"

Da steht:
"The equivalent rms value of a full-scale ac signal is approximately 
1,639,101 (0x1902BD) in the VRMS register."

Full-scale AC Signal ist 0.5V, die Zahl oben bezieht sich auf 60Hz.
Aus Figure 64 rechts oben kann man dann aber auch den Wert für 50Hz 
herauslesen:

0x193504 = 1,651,972

D.h. du teilst den Registerwert durch 3303944 (=2*1651972) und erhältst 
Volt.
Oder durch 3304, ergibt dann mV.

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.