Forum: Mikrocontroller und Digitale Elektronik I2C mit dem STM32: Nach Stop-Enable direkt ein Disable notwendig?


von mh (Gast)


Lesenswert?

Hallo,

ich kämpfe nun schon seit geraumer Zeit mit dem I2C-Modul des STM32 
unter Verwendung der Standard-Lib.
Mir ist aufgefallen, dass bei TX nach einem
1
I2C_GenerateSTOP(I2C2, ENABLE);
direkt ein
1
I2C_GenerateSTOP(I2C2, DISABLE);
eingefügt werden muss, falls dies nach dem letzten gesendeten Byte 
erfolgt. Macht man dies nicht, so wird direkt nach dem nächsten Start, 
was man sendet ein Stop gesendet.

Bei RX wird Stop vor Empfang des letzten Bytes gesetzt, es ist kein 
DISABLE notwendig, alles funktioniert.

Weiß jemand woran das liegt, bzw. hat jemand die gleichen Erfahrungen 
gemacht?

Viele Grüße!

von mh (Gast)


Lesenswert?

Achja, was mir noch aufgefallen ist:
Falls der I2C mit 100 kHz betrieben wird braucht man das DISABLE, für 
400 kHz funktionierts auch so.

von aSma>> (Gast)


Lesenswert?

Servus,
welchen stm32 hast du genau???

Generell ist der i2c bus beim stm32f1x etwas vermurkst. Ich empfehle 
dafür folgende Appnote: AN2824. Weiterhin empfehle ich dir DMA zu 
nutzen, weil man sonst bei den zeitkritischen bus ohne ein LA/Oszi 
aufgeschmissen ist!

Außerdem würde ich mir in der Appnote genannten I2CRoutines.c mal 
anschauen. Diese hält sich strickt an das Reference Manual/AN2824.

Damit müssest du schnell etwas zum blinken bringen :)

PS: Pullup bei 400khz und 3,3V sollte 1k und bei 5V 1,7k betragen!

mfg

von mh (Gast)


Lesenswert?

Hallo,
ich hab nen STM32F103. Das mit dem vermurkst hab ich auch schon 
festgestellt...
Danke für den Tipp mit der Appnote, werd mir die mal angucken.

Also einmal senden geht. Falls ich danach noch was mit dem I2C machen 
will, so geht nix mehr. Auf dem Oszi sieht man wie nach dem Start direkt 
ein Stop gesendet wird und das wars dann.
Falls ich z.B. ein Register eines Slaves lesen will, also:
Addresse+W, Registeradresse, RepStart, Addresse+R, Registerinhalt, ... , 
Nack, Stop
So kann ich auch danach dasselbe nochmal machen, ohne dass irgendwas 
hängt.

von mh (Gast)


Lesenswert?

Hab glaub ich meinen Fehler gefunden.
Man sollte sich peinlichst genau an das Diagramm auf Seite 752 des 
Reference Manual (RM0008) halten.
Man sollte immer beim Senden bei einem TXE-Flag Daten in das 
Sende-Register laden und nur falls zusätzlich ein BTF-Flag auftritt, ein 
Stop senden.

von mh (Gast)


Lesenswert?

Habs damit doch nicht gelöst, aber folgendes festgestellt.
Der Fehler tritt nur bei exakt 100 kHz auf.
Stell ich eine Bus-Frequenz von 99 kHz, 101 kHz oder 400 kHz ein, so 
funktioniert alles wunderbar.
Hat jemand schonmal etwas ähnliches festgestellt?

von mh (Gast)


Lesenswert?

Hier noch mein Programm-Code, falls es jemand nachvollziehen will.
Init-Funktion:
1
void i2c2_init(void)
2
{
3
  GPIO_InitTypeDef GPIO_InitStructure;
4
  NVIC_InitTypeDef NVIC_InitStructure;
5
  I2C_InitTypeDef I2C_InitStructure;
6
7
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
8
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
9
10
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
11
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
12
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
13
  GPIO_Init(GPIOB, &GPIO_InitStructure);
14
15
  NVIC_InitStructure.NVIC_IRQChannel = I2C2_EV_IRQn;
16
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
17
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
18
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
19
  NVIC_Init(&NVIC_InitStructure);
20
21
  NVIC_InitStructure.NVIC_IRQChannel = I2C2_ER_IRQn;
22
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
23
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
24
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
25
  NVIC_Init(&NVIC_InitStructure);
26
27
  I2C_DeInit(I2C2);
28
29
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
30
  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
31
  I2C_InitStructure.I2C_ClockSpeed = 400000;
32
  I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
33
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
34
  I2C_InitStructure.I2C_OwnAddress1 = 0;
35
  I2C_Init(I2C2, &I2C_InitStructure);
36
37
  I2C_ITConfig(I2C2, I2C_IT_EVT, ENABLE);
38
  I2C_ITConfig(I2C2, I2C_IT_BUF, ENABLE);
39
  I2C_ITConfig(I2C2, I2C_IT_ERR, ENABLE);
40
41
  I2C_Cmd(I2C2, ENABLE);
42
43
  i2c_BusyFlag = 0;
44
}

Funktionen um auf Slave-Register zugreifen zu können:
1
/*
2
 * Schreibt Daten in ein Register des Slaves.
3
 * Zuerst wird die Addresse+W geschickt, dann die Registeraddresse,
4
 * gefolgt vom Daten-Byte.
5
 * Parameter 1: Slaveaddresse links ausgerichtet
6
 * Parameter 2: Registeraddresse
7
 * Parameter 3: Daten
8
 */
9
void i2c2_write_register(uint8_t address, uint8_t registeraddress, uint8_t data)
10
{
11
  while(i2c_BusyFlag);
12
  i2c_BusyFlag = 1;
13
14
  i2c_deviceAddress = address;
15
  i2c_tx_buffer[0] = registeraddress;
16
  i2c_tx_buffer[1] = data;
17
18
  i2c_DirectionWrite = 1;
19
  i2c_rx_MsgSize = 0;
20
  i2c_tx_MsgSize = 2;
21
  I2C_GenerateSTART(I2C2, ENABLE);
22
}
23
24
25
/*
26
 * Liest Daten aus einem Register des Slaves.
27
 * Zuerst wird die Addresse+W geschickt, dann die Registeraddresse,
28
 * als naechstes ein Repeated-Start und die Addresse+R.
29
 * Nun sendet der Slave den Registerinhalt.
30
 * Parameter 1: Slaveaddresse links ausgerichtet
31
 * Parameter 2: Registeraddresse
32
 * Parameter 3: Anzahl der zu lesenden Bytes
33
 */
34
void i2c2_read_register(uint8_t address, uint8_t reg_address, uint8_t size)
35
{
36
  while(i2c_BusyFlag);
37
  i2c_BusyFlag = 1;
38
39
  i2c_deviceAddress = address;
40
  i2c_tx_buffer[0] = reg_address;
41
42
  i2c_DirectionWrite = 1;
43
  i2c_rx_MsgSize = size;
44
  i2c_tx_MsgSize = 1;
45
  I2C_GenerateSTART(I2C2, ENABLE);
46
}

IRQs:
1
void I2C2_EV_IRQHandler(void)
2
{
3
  // STM32 Transmitter
4
  if(i2c_DirectionWrite)
5
  {
6
    if(I2C_GetFlagStatus(I2C2, I2C_FLAG_SB) == SET)      // Start bit gesendet
7
    {
8
      I2C_ReadRegister(I2C2, I2C_Register_SR1);
9
      I2C_Send7bitAddress(I2C2, i2c_deviceAddress, I2C_Direction_Transmitter);
10
      i2c_ByteCounter = 0;
11
    }
12
    else if(I2C_GetFlagStatus(I2C2, I2C_FLAG_ADDR) == SET)  // Address sent
13
    {
14
      I2C_ReadRegister(I2C2, I2C_Register_SR1);
15
      I2C_ReadRegister(I2C2, I2C_Register_SR2);
16
17
      I2C_SendData(I2C2, i2c_tx_buffer[i2c_ByteCounter++]);
18
    }
19
    else if(I2C_GetFlagStatus(I2C2, I2C_FLAG_TXE) == SET)  // Data register empty
20
    {
21
      if(I2C_GetFlagStatus(I2C2, I2C_FLAG_BTF) == SET)  // Byte transfer finished
22
      {
23
        if(i2c_rx_MsgSize == 0)
24
        {
25
          I2C_GenerateSTOP(I2C2, ENABLE);
26
          i2c_BusyFlag = 0;
27
        }
28
        else
29
        {
30
          I2C_GenerateSTART(I2C2, ENABLE);      // Repeated Start
31
          i2c_DirectionWrite = 0;
32
        }
33
      }
34
      else
35
      {
36
        if(i2c_ByteCounter < i2c_tx_MsgSize)
37
          I2C_SendData(I2C2, i2c_tx_buffer[i2c_ByteCounter++]);
38
      }
39
    }
40
  }
41
42
  // STM32 Receiver
43
  else
44
  {
45
    if(I2C_GetFlagStatus(I2C2, I2C_FLAG_SB) == SET)      // Start bit gesendet
46
    {
47
      I2C_ReadRegister(I2C2, I2C_Register_SR1);
48
      I2C_Send7bitAddress(I2C2, i2c_deviceAddress, I2C_Direction_Receiver);
49
      i2c_ByteCounter = 0;
50
    }
51
    else if(I2C_GetFlagStatus(I2C2, I2C_FLAG_ADDR) == SET)  // Address sent
52
    {
53
      I2C_ReadRegister(I2C2, I2C_Register_SR1);
54
      I2C_ReadRegister(I2C2, I2C_Register_SR2);
55
56
      if (i2c_rx_MsgSize == 1)
57
      {
58
        I2C_AcknowledgeConfig(I2C2, DISABLE);
59
        I2C_GenerateSTOP(I2C2, ENABLE);
60
      }
61
      else
62
      {
63
        I2C_AcknowledgeConfig(I2C2, ENABLE);
64
      }
65
    }
66
    else if(I2C_GetFlagStatus(I2C2, I2C_FLAG_RXNE) == SET)  // Data register not empty
67
    {
68
      if (i2c_ByteCounter + 2 < i2c_rx_MsgSize)
69
      {
70
        i2c_rx_buffer[i2c_ByteCounter++] = I2C_ReceiveData(I2C2);
71
      }
72
      else if (i2c_ByteCounter + 2 == i2c_rx_MsgSize)    // bei vorletztem Byte NACK und STOP aktivieren
73
      {
74
        i2c_rx_buffer[i2c_ByteCounter++] = I2C_ReceiveData(I2C2);
75
        I2C_AcknowledgeConfig(I2C2, DISABLE);
76
        I2C_GenerateSTOP(I2C2, ENABLE);
77
      }
78
      else                        // letztes Byte empfangen
79
      {
80
        i2c_rx_buffer[i2c_ByteCounter] = I2C_ReceiveData(I2C2);
81
        i2c_BusyFlag = 0;
82
      }
83
    }
84
  }
85
}
86
87
88
/*
89
 * Error-ISR
90
 */
91
void I2C2_ER_IRQHandler(void)
92
{
93
  I2C_GenerateSTOP(I2C2, ENABLE);
94
95
  I2C_ClearFlag(I2C2, I2C_FLAG_AF);
96
  I2C_ClearFlag(I2C2, I2C_FLAG_ARLO);
97
  I2C_ClearFlag(I2C2, I2C_FLAG_BERR);
98
99
  i2c_BusyFlag = 0;
100
}

von aSma>> (Gast)


Lesenswert?

Servus,
also ohne dir zu nahe zu treten, bitte benutze die DMA. Sonst empfehle 
ich dir den "Diller STM32 Tutorial", da hat er es ganz elegant gelösst.

Beim stm32 sollte man die Flags sofort nach dem Eintritt in den Interupt 
löschen, da der nächste Interrupt zur Störung führt. Wo ist es bei dir. 
Nicht gut. Auch in der Appnote/.c Datei wird geschildert, dass zum Teil 
ein Delay erstellt werden muss, da sonst alles sich aufhängt.

Mein Tipp:
-DMA nutzen,
-STM32 Tutorial
-Timeout erstellen

hier siehst du den Vorteil:
http://letanphuc.net/2014/06/stm32-mpu6050-dma-i2c/

Ich dachte anfangs auch scheiß auf DMA, aber net beim i2c bus!!! Der 
Code halbiert sich. Garantiert!

mfg

von mh (Gast)


Lesenswert?

aSma>> schrieb:
> Servus,
> also ohne dir zu nahe zu treten, bitte benutze die DMA. Sonst empfehle
> ich dir den "Diller STM32 Tutorial", da hat er es ganz elegant gelösst.
>
> Beim stm32 sollte man die Flags sofort nach dem Eintritt in den Interupt
> löschen, da der nächste Interrupt zur Störung führt. Wo ist es bei dir.
> Nicht gut. Auch in der Appnote/.c Datei wird geschildert, dass zum Teil
> ein Delay erstellt werden muss, da sonst alles sich aufhängt.
>
> Mein Tipp:
> -DMA nutzen,
> -STM32 Tutorial
> -Timeout erstellen
>
> hier siehst du den Vorteil:
> http://letanphuc.net/2014/06/stm32-mpu6050-dma-i2c/
>
> Ich dachte anfangs auch scheiß auf DMA, aber net beim i2c bus!!! Der
> Code halbiert sich. Garantiert!
>
> mfg


Danke für die konstruktive Kritik!
Hab mir das mit DMA mal angeschaut, sieht wirklich wesentlich einfacher 
aus. Werde das bei Gelegenheit dann so umsetzen.

Viele Grüße!

von mh (Gast)


Lesenswert?

Habs nun in DMA umgebaut.
Ich hab den Code aus dem genannten STM32-Tutorial, bzw. leicht 
angepasst. Ich brauch Funktionen um einmal in ein Slave-Register zu 
schreiben (Adr+W, 2 Bytes) und zu lesen (Adr+W, Reg-Adr., Adr+R, Daten).
Der Code aus dem Tutorial hat im Prinzip letzteres fertig implementiert 
gehabt.
Habs nun so erweitert, dass auch reines schreiben gehen soll.
Nur ist mir da was komisches aufgefallen. Bei der Schreibfunktion will 
ich insgesamt 2 Bytes schreiben. Es werden bei:
1
DMA_SetCurrDataCounter(DMA1_Channel4, 2);
aber nur 1 Byte gesendet, also Adr+W und 1 Byte.
Setz ich:
1
DMA_SetCurrDataCounter(DMA1_Channel4, 3);
so werden Adr+W und 2 Bytes gesendet. Weiß jemand woran das liegt??
Das würde sich ja mit dem Lesebefehl widersprechen (dieser funktioniert 
wie er soll).

Hier noch der Code (die Init hab ich mal wegelassen, hier wurde einfach 
ergänzt, dass durch DMA1_Channel4 IRQs ausgelöst werden können):

Slave-Zugriffsfunktionen:
1
/*
2
 * Schreibt Daten in ein Register des Slaves.
3
 * Zuerst wird die Addresse+W geschickt, dann die Registeraddresse,
4
 * gefolgt vom Daten-Byte.
5
 * Parameter 1: Slaveaddresse links ausgerichtet
6
 * Parameter 2: Registeraddresse
7
 * Parameter 3: Daten
8
 */
9
/*static*/ void i2c2_write_register(uint8_t address, uint8_t registeraddress, uint8_t data)
10
{
11
  while(i2c_BusyFlag);
12
  i2c_BusyFlag = 1;
13
14
  i2c_deviceAddress = address;
15
  i2c_tx_buffer[0] = registeraddress;
16
  i2c_tx_buffer[1] = data;
17
18
  i2c_DirectionWrite = 1;
19
  i2c_read_enable = 0;
20
  DMA_SetCurrDataCounter(DMA1_Channel4, 3);    // TX
21
  DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);
22
  I2C_GenerateSTART(I2C2, ENABLE);
23
}
24
25
26
/*
27
 * Liest Daten aus einem Register des Slaves.
28
 * Zuerst wird die Addresse+W geschickt, dann die Registeraddresse,
29
 * als naechstes ein Repeated-Start und die Addresse+R.
30
 * Nun sendet der Slave den Registerinhalt.
31
 * Parameter 1: Slaveaddresse links ausgerichtet
32
 * Parameter 2: Registeraddresse
33
 * Parameter 3: Anzahl der zu lesenden Bytes
34
 */
35
/*static*/ void i2c2_read_register(uint8_t address, uint8_t reg_address, uint8_t size)
36
{
37
  while(i2c_BusyFlag);
38
  i2c_BusyFlag = 1;
39
40
  i2c_deviceAddress = address;
41
  i2c_tx_buffer[0] = reg_address;
42
43
  i2c_DirectionWrite = 1;
44
  i2c_read_enable = 1;
45
  DMA_SetCurrDataCounter(DMA1_Channel4, 1);    // TX
46
  DMA_SetCurrDataCounter(DMA1_Channel5, size);  // RX
47
  I2C_GenerateSTART(I2C2, ENABLE);
48
}


IRQ-Routinen:
1
void I2C2_EV_IRQHandler(void)
2
{
3
  if (I2C_GetFlagStatus(I2C2, I2C_FLAG_SB) == SET)
4
  {
5
    if(i2c_DirectionWrite)    // STM32 Transmitter
6
      I2C_Send7bitAddress(I2C2, i2c_deviceAddress, I2C_Direction_Transmitter);
7
    else            // STM32 Receiver
8
      I2C_Send7bitAddress(I2C2, i2c_deviceAddress, I2C_Direction_Receiver);
9
  }
10
  else if(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == SUCCESS)
11
  {
12
    if (i2c_DirectionWrite)    // STM32 Transmitter
13
      DMA_Cmd(DMA1_Channel4, ENABLE);
14
  }
15
  else if(I2C_GetFlagStatus(I2C2, I2C_FLAG_BTF))
16
  {
17
    if(i2c_DirectionWrite)    // STM32 Transmitter
18
    {
19
      I2C_ClearFlag(I2C2, I2C_FLAG_BTF);
20
21
      if(i2c_read_enable)
22
      {
23
        I2C_DMALastTransferCmd(I2C2, ENABLE);
24
        DMA_Cmd(DMA1_Channel5, ENABLE);
25
        I2C_GenerateSTART(I2C2, ENABLE);
26
        i2c_DirectionWrite = 0;
27
      }
28
    }
29
  }
30
}
31
32
33
void DMA1_Channel4_IRQHandler(void)
34
{
35
  DMA_ClearFlag(DMA1_FLAG_TC4);
36
  I2C_GenerateSTOP(I2C2, ENABLE);
37
  DMA_Cmd(DMA1_Channel4, DISABLE);
38
  DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, DISABLE);
39
40
  i2c_BusyFlag = 0;
41
42
  if(imu_communication_enable)
43
    imu_communication_flow();
44
}
45
46
47
void DMA1_Channel5_IRQHandler(void)
48
{
49
  DMA_ClearFlag(DMA1_FLAG_TC5);
50
  I2C_GenerateSTOP(I2C2, ENABLE);
51
  DMA_Cmd(DMA1_Channel4, DISABLE);
52
  DMA_Cmd(DMA1_Channel5, DISABLE);
53
54
  i2c_BusyFlag = 0;
55
56
  if(imu_communication_enable)
57
    imu_communication_flow();
58
}

von mh (Gast)


Lesenswert?

Hab eine Erklärung für das verloren gegangen Byte gefunden.
DMA löst einen IRQ aus, falls alles an die Peripherie übergeben wurde, 
was logischerweise vor dem letzten über I2C versendeten Byte der Fall 
ist.

von aSma>> (Gast)


Lesenswert?

mh schrieb:
> Hab eine Erklärung für das verloren gegangen Byte gefunden.
> DMA löst einen IRQ aus, falls alles an die Peripherie übergeben wurde,
> was logischerweise vor dem letzten über I2C versendeten Byte der Fall
> ist.

Ja, also die slave_addr zählt man nicht mit. Nur, bei dir "Registerdata 
und Data".

mh schrieb:
> void i2c2_write_register(uint8_t address, uint8_t registeraddress,
> uint8_t data)

Ich würde das lieber als Buffer versenden oder wenigstens als Pointer.
1
uint8_t *buffer_p = {0xaa, 0xff};
2
void i2c2_write(uint8_t address, uint8_t *buffer_p, uint8_t num2write)
3
4
//Aufruf 
5
i2c2_write(address, *buffer_p, num2write){
6
...
7
memcpy( &i2c_tx_buffer, buffer_p,  num2write);
8
...
9
}

Das Problem ist, es gibt z.B LCD Bildschirme, PCF8574, HD44780, da 
sprichst du den slave_addr + Data an.

Viel Spaß mit DMA :)

mfg

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.