Forum: Mikrocontroller und Digitale Elektronik STM32F4 und LTC6803-4, Probleme mit dem SPI und der Antwort des LTC


von Max H. (Gast)


Angehängte Dateien:

Lesenswert?

Hallo uC-Community,

ich stehe vor einem Problem und sehe wahrscheinlich den Wald vor lauter 
Bäumen nicht. Ich versuche einen LTC6803-4 über (isolierten) SPI 
anzusprechen. Die Initialisierung vom SPI sollte passen denn auf dem 
Logic Analyser sehe ich SCLK, MOSI und CS sowohl vor als auch nach dem 
Optokoppler. Das Problem liegt in der Antwort vom LTC6803. Dieser sendet 
mir absolut keine Daten und macht auch sonst keinen Mucks.

Meine Deklarationen:
1
/** @defgroup STM32F4_HARDWARE_LTC6803
2
  * @{
3
  */
4
uint8_t LTC6803_RX[LTC6803_NBR_BYTES] = {0};
5
uint8_t LTC6803_PEC = 0x00;
6
static uint8_t CRC8_Table[256];    // 8-bit table for PEC calc
7
8
#define LTC6803_NBR_BYTES 16
9
10
#define LTC6803n                        4
11
12
/* PB12 - SPI2_NSS */
13
#define LTC6803_CS_PIN                  GPIO_Pin_12
14
#define LTC6803_CS_PIN_SOURCE           GPIO_PinSource12
15
#define LTC6803_CS_GPIO_PORT            GPIOB
16
#define LTC6803_CS_GPIO_CLK             RCC_AHB1Periph_GPIOB
17
18
/* PB14 - SPI2_MISO */
19
#define LTC6803_DOUT_PIN                 GPIO_Pin_14
20
#define LTC6803_DOUT_PIN_SOURCE         GPIO_PinSource14
21
#define LTC6803_DOUT_GPIO_PORT           GPIOB
22
#define LTC6803_DOUT_GPIO_CLK            RCC_AHB1Periph_GPIOB
23
  
24
/* PB15 - SPI2_MOSI */
25
#define LTC6803_DIN_PIN                  GPIO_Pin_15
26
#define LTC6803_DIN_PIN_SOURCE          GPIO_PinSource15
27
#define LTC6803_DIN_GPIO_PORT            GPIOB
28
#define LTC6803_DIN_GPIO_CLK             RCC_AHB1Periph_GPIOB
29
30
/* PB13 - SPI2_SCK */
31
#define LTC6803_SCLK_PIN                GPIO_Pin_13
32
#define LTC6803_SCLK_PIN_SOURCE         GPIO_PinSource13
33
#define LTC6803_SCLK_GPIO_PORT          GPIOB
34
#define LTC6803_SCLK_GPIO_CLK           RCC_AHB1Periph_GPIOB
35
36
/* LTC6803 address A0 A1 A2 and A3 are connected to GND */
37
#define LTC6803_ADDR                    0x80
38
/* Write Configuration Register Group */
39
#define WRCFG                            0x01
40
#define WPEC                            0xC7
41
/* Read Configuration Register Group */
42
#define RDCFG                            0x02
43
#define RCPEC                            0xCE
44
/* Poll Interrupt Status */
45
#define PLINT                            0x50
46
#define PIPEC                            0x77
47
/* Start Diagnose and Poll Status */
48
#define DAGN                            0x52
49
#define DAPEC                            0x79
50
/* Read Diagnostic Register */
51
#define RDDGNR                          0x54
52
#define RDAPEC                          0x6B
53
54
/* Read All Cell Voltage Group */
55
#define RDCV                            0x04
56
#define RVPEC                            0xDC
57
/* Start Cell Voltage ADC Conversions and Poll Status */
58
#define STCVAD                          0x10
59
#define SCVPEC                          0xB0
60
61
#define CFG1R0                          0x11
62
#define CFG1R1                          0x00
63
#define CFG1R2                          0x00
64
#define CFG1R3                          0x00
65
#define CFG1R4                          0x00
66
#define CFG1R5                          0x00
67
#define CFGPEC                          0x3B
68
69
/**
70
  * @}
71
  */

Meine Initialisierung des LTC6803 via SPI2 und die dazugehörigen 
Funktionen:
1
/**
2
  * @brief  Configures LTC6803 SPI
3
  * @param  None
4
  * @retval None
5
  */
6
void LTC6803_Init(void)
7
{
8
  GPIO_InitTypeDef GPIO_InitStructure;
9
  SPI_InitTypeDef SPI_InitStructure;
10
  
11
  SPI_I2S_DeInit(SPI2); 
12
  
13
  /* Enable the LTC6803_CS clock */
14
  RCC_AHB1PeriphClockCmd(LTC6803_CS_GPIO_CLK, ENABLE);
15
  /* Configure the LTC6803_CS as output */
16
  GPIO_InitStructure.GPIO_Pin = LTC6803_CS_PIN;
17
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
18
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
19
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
20
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
21
  GPIO_Init(LTC6803_CS_GPIO_PORT, &GPIO_InitStructure);
22
  /* LTC6803_!CS as high (not active) */
23
  GPIO_WriteBit(LTC6803_CS_GPIO_PORT, LTC6803_CS_PIN, Bit_SET);
24
  
25
  /* Enable the LTC6803_SCLK clock */
26
  RCC_AHB1PeriphClockCmd(LTC6803_SCLK_GPIO_CLK, ENABLE);
27
  /* Configure the LTC6801_NSOUT as input */
28
  GPIO_InitStructure.GPIO_Pin = LTC6803_SCLK_PIN;
29
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
30
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
31
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
32
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
33
  GPIO_Init(LTC6803_SCLK_GPIO_PORT, &GPIO_InitStructure);
34
  
35
  /* Enable the LTC6803_DOUT (MISO) clock */
36
  RCC_AHB1PeriphClockCmd(LTC6803_DOUT_GPIO_CLK, ENABLE);
37
  /* Configure the LTC6803_DOUT (MISO) as SPI2 DOUT */
38
  GPIO_InitStructure.GPIO_Pin = LTC6803_DOUT_PIN;
39
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
40
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
41
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
42
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
43
  GPIO_Init(LTC6803_DOUT_GPIO_PORT, &GPIO_InitStructure);
44
  
45
  /* Enable the LTC6803_DIN (MOSI) clock */
46
  RCC_AHB1PeriphClockCmd(LTC6803_DIN_GPIO_CLK, ENABLE);
47
  /* Configure the LTC6803_DIN (MOSI) as SPI2 DIN */
48
  GPIO_InitStructure.GPIO_Pin = LTC6803_DIN_PIN;
49
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
50
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
51
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
52
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
53
  GPIO_Init(LTC6803_DIN_GPIO_PORT, &GPIO_InitStructure);
54
  
55
  /* Connect LTC6803 SPI pins to AF5 */
56
  GPIO_PinAFConfig(LTC6803_DIN_GPIO_PORT, LTC6803_DIN_PIN_SOURCE, GPIO_AF_SPI2);
57
  GPIO_PinAFConfig(LTC6803_SCLK_GPIO_PORT, LTC6803_SCLK_PIN_SOURCE, GPIO_AF_SPI2);
58
  GPIO_PinAFConfig(LTC6803_DOUT_GPIO_PORT, LTC6803_DOUT_PIN_SOURCE, GPIO_AF_SPI2);
59
  
60
  /* Enable the SPI2 clock */
61
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
62
63
  /* Configure the SPI2 bus */
64
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
65
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
66
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
67
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
68
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
69
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128; // 42MHz/128 = 328kHz
70
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
71
  SPI_InitStructure.SPI_CRCPolynomial = 8;
72
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
73
  
74
  /* Initialize the SPI2 bus */
75
  SPI_Init(SPI2, &SPI_InitStructure);
76
  SPI_Cmd(SPI2, ENABLE);
77
  
78
  /* Calculates the table for PEC data */
79
  CRC8_Init();
80
}
81
82
/**
83
  * @brief  Sends data through the SPI interface and return the data received
84
  *         from the SPI bus.
85
  * @param  data: data to send.
86
  * @retval The value of the received data.
87
  */
88
uint8_t SPI2_SendReceive(uint8_t data)
89
{
90
  /*!< Loop while DR register in not empty */
91
  while ((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);
92
93
  /*!< Send data through the SPI2 peripheral */
94
  SPI2->DR = data;
95
96
  /*!< Wait to receive data */
97
  while ((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
98
  
99
  return SPI2->DR;
100
}
101
102
/**
103
  * @brief  CRC8_Init calculates the CRC8 table for PEC calc
104
  *         Cite: http://www.rajivchakravorty.com/source-code/uncertainty/multimedia-sim/html/crc8_8c.html
105
  * @param  None
106
  * @retval None
107
  */
108
static void CRC8_Init(void)
109
{
110
  uint16_t i, j;
111
  uint8_t crc;
112
  for (i = 0; i < 256; i++)
113
  {
114
    crc = i;
115
    for (j = 0; j < 8; j ++)
116
    {
117
      crc = (crc << 1) ^ ((crc & 0x80) ? 0x07 : 0);
118
    }
119
    CRC8_Table[i] = crc & 0xFF;
120
  }
121
}
122
123
/**
124
  * @brief  CalcPEC_Byte calculates the PEC for the given byte
125
  * @param  Data: byte for which the PEC should be calculated
126
  * @retval calculated PEC byte
127
  */
128
uint8_t CalcPEC_Byte(uint8_t Data)
129
{
130
  /* Initialize PECbyte */
131
  uint8_t crc = 0x41;
132
  return CRC8_Table[(crc) ^ Data];
133
}
134
135
/**
136
  * @brief  CalcPEC_Packet calculates the PEC for the data in LTC6803_RX
137
  * @param  Len: number of bytes which are relevant in LTC6803_RX
138
  * @retval calculated PEC byte
139
  */
140
uint8_t CalcPEC_Packet(uint8_t Len)
141
{
142
  uint16_t i;
143
  /* Initialize PECbyte */
144
  uint8_t crc = 0x41;
145
  for (i = 0; i < Len; i++)
146
  {
147
    crc = CRC8_Table[(crc) ^ LTC6803_RX[i]];
148
  }
149
  return crc;
150
}

Meine main-Routine:
1
/* Write Configuration Registers (Broadcast Write) */
2
/* Set LTC6803_!CS to low (activate) */
3
GPIO_WriteBit(LTC6803_CS_GPIO_PORT, LTC6803_CS_PIN, Bit_RESET);
4
5
/* Send board address */
6
SPI2_SendReceive(LTC6803_ADDR);
7
SPI2_SendReceive(CalcPEC_Byte(LTC6803_ADDR));
8
9
/* Fill buffer for PEC calculation */
10
LTC6803_RX[0] = CFG1R0;
11
LTC6803_RX[1] = CFG1R1;
12
LTC6803_RX[2] = CFG1R2;
13
LTC6803_RX[3] = CFG1R3;
14
LTC6803_RX[4] = CFG1R4;
15
LTC6803_RX[5] = CFG1R5;
16
  
17
/* Send WRCFG command and its PEC byte */
18
SPI2_SendReceive(WRCFG);
19
SPI2_SendReceive(WPEC);
20
21
/* Send CFGR0 byte, then CFGR1, …CFGR5, PEC byte */
22
SPI2_SendReceive(CFG1R0);
23
SPI2_SendReceive(CFG1R1);
24
SPI2_SendReceive(CFG1R2);
25
SPI2_SendReceive(CFG1R3);
26
SPI2_SendReceive(CFG1R4);
27
SPI2_SendReceive(CFG1R5);
28
SPI2_SendReceive(CalcPEC_Packet(6));
29
30
/* Set LTC6803_!CS to high (deactivate) */
31
GPIO_WriteBit(LTC6803_CS_GPIO_PORT, LTC6803_CS_PIN, Bit_SET);
32
33
/* Insert 10us delay */
34
Delay_1us(10);
35
36
/* Read Configuration Registers (Broadcast Write) */
37
/* Set LTC6803_!CS to low (activate) */
38
GPIO_WriteBit(LTC6803_CS_GPIO_PORT, LTC6803_CS_PIN, Bit_RESET);
39
40
SPI2_SendReceive(LTC6803_ADDR);       // send board address
41
SPI2_SendReceive(CalcPEC_Byte(LTC6803_ADDR));          // send PECbyte of board address
42
43
/*  Send RDCFG command and its PEC byte */
44
SPI2_SendReceive(RDCFG);
45
SPI2_SendReceive(RCPEC);
46
47
/* Read CFGR0 byte, then CFGR1, ... CFGR5 and PEC byte */
48
for (i = 0; i < 6; i++)
49
{
50
   LTC6803_RX[i] = SPI2_SendReceive(0x00);
51
} 
52
LTC6803_PEC = SPI2_SendReceive(0x00);
53
54
/* Set LTC6803_!CS to high (deactivate) */
55
GPIO_WriteBit(LTC6803_CS_GPIO_PORT, LTC6803_CS_PIN, Bit_SET);
56
57
/* Insert 20us delay */
58
Delay_1us(20);
59
60
/* Print data when PEC is correct */
61
if (LTC6803_PEC == CalcPEC_Packet(6))
62
{
63
  printf("LTC6803 Address: %d\n", LTC6803_ADDR);
64
  printf("Config[0...5]: %d %d %d %d %d %d\n", LTC6803_RX[0], LTC6803_RX[1], LTC6803_RX[2], LTC6803_RX[3], LTC6803_RX[4], LTC6803_RX[5]);
65
}

Ich sende zuerst die Addresse 0x80 (alle A0..3 liegen auf GND) und das 
entsprechende PEC byte, dann das Command zum schreiben der CFG und 
anschliessend will ich die CFG auslesen, genau hier passiert, wie im 
Analyser zu sehen, einfach gar nichts.
Die Taktrate mit etwa 330kHz sollte der LTC locker mitmachen, laut 
Datenblatt ist bis 1Mhz alles möglich. Ich bin echt am verzweifeln, 
vielleicht sieht einer den Fehler auf die Schnelle, vielleicht kann ein 
anderer auch etwas mit den Codeschnipseln anfangen.
Vielen Dank auf jeden Fall fürs lesen und die Mühen.

Grüße #Max

von Peter L. (Gast)


Lesenswert?

vielleicht drehen die Optokoppler deine Signale um (Schaltplan wäre 
hifreich)

von Funko B. (funkobongrip)


Lesenswert?

hab schon viel mit den ics gemacht, also mal geraten.
ohne jetzt den ganzen post gelesen zu haben:
pull-up an sdo vom ltc?
die pec berechnung sieht irgendwie anders aus als ich sie gemacht habe
ansonsten: schaltplan her

von Jürgen Liegner (Gast)


Lesenswert?

Ich habe mir mal deine Signale angesehen. Was sich zu meiner 
Implementierung auf einem LPC1114 unterscheidet ist die SPI Betriebsart. 
Die Register beim LPC sind z.B.

CPOL Clock Out Polarity. This bit is only used in SPI mode. 0
   0 SPI controller maintains the bus clock low between frames.
   1 SPI controller maintains the bus clock high between frames.

CPHA Clock Out Phase. This bit is only used in SPI mode. 0
   0 SPI controller captures serial data on the first clock transition
     of the frame, that is, the transition away from the inter-frame
     state of the clock line.
   1 SPI controller captures serial data on the second clock
     transition of the frame, that is, the transition back to the
     inter-frame state of the clock line.

Bei mir sind beide Bits auf 1.

Bei dir dürfte das die Stelle sein:

  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;

Ich weigere mich aber bis jetzt standhaft mich mit dieser unsäglichen 
Stm32lib auseinander zu setzen. Deshalb musst du selbst rausfinden was 
bei dir richtig ist. Auf alle Fälle hast du erst mal einen Ansatzpunkt.

Was hast du da für Optokoppler verbaut? Die sollten schon schnell sein. 
Ich habe dafür den ADUM1401 benutzt. Der kostet bei Reichelt zwar 3.85€, 
ist aber besser als jeder Optokoppler für diesen Zweck.

von Jürgen (jliegner)


Lesenswert?

Die PECs sind definitiv richtig, auch wenn ich sie ebenfalls anders 
berechne.
1
uint8_t CalcPec(uint8_t *pIn, int len)
2
{
3
  uint8_t pec = 0x41;
4
  for (int i=0; i<len; i++) 
5
    {
6
    pec=pec^pIn[i];
7
    for (int j=0; j<8; j++) 
8
      {
9
      if (pec & 0x80) 
10
        pec = (pec << 1) ^ 0x07; // if leftmost (most significant) bit is set
11
      else 
12
        pec = pec << 1;
13
      }
14
    }
15
  return pec;
16
}

: Bearbeitet durch User
von Max H. (Gast)


Angehängte Dateien:

Lesenswert?

Holla die Waldfee, das ging schnell!

Jürgen Liegner schrieb:
> Bei mir sind beide Bits auf 1.
>
> Bei dir dürfte das die Stelle sein:
>
>   SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
>   SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;

Bei mir sind auch beide Bits auf 1 so wie es der LTC laut Datenblatt 
will. Hier steht auf Seite 17:
> The  LTC6803  SPI  compatible interface is configured to operate in a
> system using CPHA = 1 and CPOL = 1. Consequently, data on SDI must be
> stable during the rising edge of SCKI.

Habe trotzdem die anderen Kombinationen ausprobiert, leider ohne Erfolg.

Jürgen Liegner schrieb:
> Die PECs sind definitiv richtig, auch wenn ich sie ebenfalls anders
> berechne.

Das glaube ich auch, vielleicht ist die CFGPEC in den Deklarationen 
nicht mehr ganz richtig aber die nutze ich auch nicht mehr.

David Brandt schrieb:
> pull-up an sdo vom ltc?

Nein dummerweise nicht, habe nachträglich einen 220pF an die isolierte 
Seite der MISO gelegt (blau) jedoch ohne Verbesserung. Die relevanten 
Schaltpläne sind angehängt, hoffentlich einigermaßen übersichtlich.

Vielen Dank für eure Hilfe!!

von Funko B. (funkobongrip)


Lesenswert?

Max H. schrieb:
> David Brandt schrieb:
>> pull-up an sdo vom ltc?
>
> Nein dummerweise nicht, habe nachträglich einen 220pF an die isolierte
> Seite der MISO gelegt (blau) jedoch ohne Verbesserung. Die relevanten
> Schaltpläne sind angehängt, hoffentlich einigermaßen übersichtlich.

Da zitier ich mal das Datenblatt:
"SDO (Pin 43): Serial Data Output. The SDO pin is an NMOS open-drain 
output. A pull-up resistor is needed on SDO. See Serial Port in the 
Applications Information section."

Ohne Pull-Up wird das also nix.

Ansonsten hat Jürgen glaube ich was richtiges angesprochen.
SCK sollte HIGH sein im Idle Zustand, dann kommt Flanke runter und dann 
das erste Bit bei Flanke hoch. So ist es im Datenblatt gezeigt und bei 
mir macht das der uC auch so.

Beim SCK Verhalten bin ich mir nicht sicher, aber der Pull-Up verhindert 
die Funktion ganz sicher.

Das mit dem SCK könnte nur ungewolltes Verhalten erzeugen, weil der LTC 
dann ggfs. in einem Wartezustand hängt. Weil ihn nur SCK=High Idle ist.

: Bearbeitet durch User
von Jürgen (jliegner)


Lesenswert?

David Brandt schrieb:
> aber der Pull-Up verhindert
> die Funktion ganz sicher.

Das ist wohl richtig. Meinen Prototypen ziert auch ein zusätzlicher R 
direkt an Beinen des ADUM....

von Max H. (Gast)


Angehängte Dateien:

Lesenswert?

David Brandt schrieb:
> Da zitier ich mal das Datenblatt:
> "SDO (Pin 43): Serial Data Output. The SDO pin is an NMOS open-drain
> output. A pull-up resistor is needed on SDO. See Serial Port in the
> Applications Information section."
>
> Ohne Pull-Up wird das also nix.

Oh man, die üblichen Fehler :) Ihr hattet natürlich recht, 5k Pull-Up 
und schon bekomm ich eine tolle Antwort! Jetzt kann ich beruhigt in den 
Feierabend.
Morgen teste ich die restlichen Funktionen, wenn alles klappt schick ich 
euch meine "Lösung".
Viele Grüße und Danke euch Allen!

von Joe A. (joe1057)


Lesenswert?

Hallo zusammen,

ich weiß dass der Post schon sehr alt ist, ich habe aber genau das 
gleiche Problem. Fakt ist nur dass ich den 5k Pullup schon eingebaut 
habe. Dennoch sendet mir der LTC nichts zurück. SDO ist immer auf 5V.
Habt ihr eventuell noch weitere Ideen was man da machen kann.
Also mC benutze ich einen XC2000 von Infineon. Problem ist hier dass 
direkt nach dem senden der Config und PEC Bytes das CS wieder auf high 
geht. Ich habe noch keine möglichkeit gefunden dieses von Hand zu 
steuern.
Sitze hier schon lange an dem Problem und würde mich freuen wenn einer 
von euch Ideen hierzu hat.

Gruß Joe

von Joe A. (joe1057)


Angehängte Dateien:

Lesenswert?

Anbei eine Aufzeichnung vom Oszi.
CLK
MISO
MOSI
CS

von Joe A. (joe1057)


Lesenswert?

Problem gelöst. I musste noch 18 Dummy-Bytes anhängen da sonst das CLK 
Signal für die Antwort vom Slave nicht mehr gegeben war. Jetzt 
funktioniert es.

von Stani (Gast)


Lesenswert?

"Morgen teste ich die restlichen Funktionen, wenn alles klappt schick 
ich
euch meine "Lösung"."

Hi, könntest du deine Lösung bereitstellen? Ich nutze den identischen 
Chip für ein Bastelprojekt.

Besten Dank

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.