Forum: Mikrocontroller und Digitale Elektronik STM32 Cortex Timer behindert I2C Interrupt


von Sylvia H. (sandy)


Lesenswert?

Hi,
ich habe folgendes Problem:
Auf meinem Cortex M3 ist I2C2 initialisiert. Der Cortex soll dort auf 
den General Call (0x00) lauschen, und immer wenn etwas hereinkommt, die 
Daten in den Buffer twi_buf[] schreiben.
Das Lauschen und hineinschreiben funktionierte auch.

Danach  habe ich einen Timer initialisiert, der alle 1,8s einen 
Interrupt verursacht. In seiner IR prüft er verschiedene Flags ab, und 
handelt entsprechend. Eines dieser  Flags (hier myUUID ) sollte 
eigentlich auf false gesetzt werden, wenn der Cortex eine ganz bestimmte 
Nachricht über den General Call erhält, und mann dann twi_buf[] 
auswertet.
Die Nachricht erscheint auch auf dem Bus (kann ich mit meinem Advaark 
sehen), doch der Cortex "hört" sie nicht, bzw geht erst gar nicht in den 
I2C2_EV_IRQHandler hinein. Also wird twi_buf[] nicht beschrieben und 
kann auch nicht ausgewertet werden.
 Nun dachte ich, vielleicht behindert die Timer NVICdie I2C2 
NVICeinstellung, und habe dem I2C2 eine höhere Priorität gegeben, als 
dem Timer NVIC. Doch genützt hat das auch nichts.

Hier meine NVIC Einstellungen für den Timer4:
1
NVIC_InitTypeDef NVIC_InitStructure;
2
3
          /* Enable the TIM4 global Interrupt */
4
          NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
5
          NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
6
          NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
7
          NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
8
          NVIC_Init(&NVIC_InitStructure);
und für I2c:
1
NVIC_SetPriority(I2C2_EV_IRQn, 0x02);
2
  NVIC_EnableIRQ(I2C2_EV_IRQn);
3
4
  NVIC_SetPriority(I2C2_ER_IRQn, 0x03);
5
  NVIC_EnableIRQ(I2C2_ER_IRQn);
Die IR Routine des Timer4:
1
void TIM4_IRQHandler(void) {
2
3
  TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
4
  // Adress Initialisierung TWI
5
  if (myUUID) {  
6
    if (myID == 0) {
7
      myID = TWI_START_ID;
8
    }
9
    twiBuffer1[0] = myID;
10
    TWI_Send(0x00, twiBuffer1, 1, TWI_MESSAGE_newID);
11
    maxID = myID;
12
13
  }
14
  twiAdressInit = false;
15
  TWI_IdChange(myID);
16
}

Da myUUID immer true ist, (weil twi_buf[] nicht im I2C2_EV_IRQHandler 
beschrieben wird), sendet mein Cortex alle 1,8s TWI_MESSAGE_newID.
Dies sollte er meiner Meinung nach doch auch selbst empfangen, da er 
diese Nachricht General Call verschickt.
Aber nichts tut sich, der I2C2_EV_IRQHandler  springt nicht an.
Kommentiere ich den Timer und seine NVIC aus, bekommt der Cortex wieder 
alles mit, und geht auch schön in seinen I2C2_EV_IRQHandler hinein.
Hat jemand ne Idee?
Gruß
Sylvia

von Arne (Gast)


Lesenswert?

Muss der Timer4 INT nicht auch im NVIC quittiert/zurückgesetzt werden?
Also ich mach das, weiss aber nicht, ob es absolut notwendig ist.
Könnte sonst sein, dass die TIM4 ISR dir die gesamte Rechenzeit 
wegfrisst, weil er immer wieder reingeht. Bin mir aber nicht sicher.

von Sylvia H. (sandy)


Lesenswert?

Arne schrieb:
> Muss der Timer4 INT nicht auch im NVIC quittiert/zurückgesetzt werden?

also in der ISR wird der Interrupt mit
> TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
zurückgesetzt.

was du mit "NVIC quittiert/zurückgesetzt" meinst verstehe ich nicht.....

von (prx) A. K. (prx)


Lesenswert?

Im Timer-Interrupt das I2C anzusprechen setzt natürlich voraus, dass die 
I2C-Routinen im Hauptprogramm gegen Timer-Interrupts abgesichert sind. 
Sonst droht ab und zu Chaos, wenn ein Timer-Interrupt mittendrin 
zuschlägt.

von (prx) A. K. (prx)


Lesenswert?

Arne schrieb:

> Muss der Timer4 INT nicht auch im NVIC quittiert/zurückgesetzt werden?

Bist du sicher, dass du den Cortex-M3 meinst, und nicht den VIC mancher 
ARM7? Das Interrupt-Flag des Timers hatten wir heute morgen schon in 
einem anderen Thread.

von Arne (Gast)


Lesenswert?

Ich arbeite nur mit STM32/NXP17xx. Ich schrieb ja, dass ich mir nicht 
sicher bin.

von Sylvia H. (sandy)


Lesenswert?

A. K. schrieb:
> Im Timer-Interrupt das I2C anzusprechen setzt natürlich voraus, dass die
> I2C-Routinen im Hauptprogramm gegen Timer-Interrupts abgesichert sind.

deshalb habe ich ja die Priorität des I2C Interrupt höher gestellt als 
die des Timers

von (prx) A. K. (prx)


Lesenswert?

Was das eigentliche Problem angeht: Da habe ich nur Bahnhof verstanden.

von Sylvia H. (sandy)


Lesenswert?

A. K. schrieb:
> Was das eigentliche Problem angeht: Da habe ich nur Bahnhof verstanden

ist kein Timer im Spiel, kann mein Cortex auf den General Call des I2C 
hören,
kommt der Timer dazu, wird der Cortex taub auf dem I2C Ohr

von (prx) A. K. (prx)


Lesenswert?

Sylvia H. schrieb:

> deshalb habe ich ja die Priorität des I2C Interrupt höher gestellt als
> die des Timers

Das nützt nichts, denn wenn im Hauptprogramm und im Timer-Interrupt 
nichttriviale I2C-Funktionen oder -Abläufe verwendet werden, dann kann 
der Timer immer reinschlüpfen, egal wie die Prioritäten stehen, solange 
der Timer höher priorisiert ist als der Teil des Hauptprogramms.

von (prx) A. K. (prx)


Lesenswert?

Sylvia H. schrieb:

> ist kein Timer im Spiel, kann mein Cortex auf den General Call des I2C
> hören, kommt der Timer dazu, wird der Cortex taub auf dem I2C Ohr

Was mir nicht gefällt ist das TWI_Send im Timer-Interrupt. Ich halte 
mich gern aus der STM32-Lib raus (hab grad auch weder Doku noch Source 
im direkten Zugriff), vermute aber, dass es da drin recht komplex zugeht 
und diese Aktion nicht in einem Interrupt-Handler gehört.

Sind die TWI-Funktionen der STM32-Lib überhaupt Interrupt-gesteuert?

von Sylvia H. (sandy)


Lesenswert?

A. K. schrieb:
> Was mir nicht gefällt ist das TWI_Send im Timer-Interrupt

das gefällt mir auch nicht, ich muss aber einen vorgegebenen Code 
benutzen, der für den ATXMEGA geschrieben wurde, und nun für den CORTEX 
M3 "Übersetzt" werde soll.

Ich sende mit POLLING und empfange mit INTERRUPT. Die Libs sind für 
Polling/Interrupt und dma ausgelegt

von (prx) A. K. (prx)


Lesenswert?

Sylvia H. schrieb:

> das gefällt mir auch nicht, ich muss aber einen vorgegebenen Code
> benutzen, der für den ATXMEGA geschrieben wurde, und nun für den CORTEX
> M3 "Übersetzt" werde soll.

Jo, aber trotzdem wäre die korrekte Vorgehensweise, im Hauptprogramm 
abhängig von einem Flag zu senden, und das im Timer-Interrupt zu setzen. 
Auch beim AVR.

von Sylvia H. (sandy)


Angehängte Dateien:

Lesenswert?

A. K. schrieb:
> Jo, aber trotzdem wäre die korrekte Vorgehensweise, im Hauptprogramm
> abhängig von einem Flag zu senden, und das im Timer-Interrupt zu setzen.
> Auch beim AVR.

habs erledigt, gefällt mir auch besser...
vielleicht stimmt ja auch etwas mit der I2C Senderoutine nicht, es wäre 
nicht das erste Mal, das bei den STM Libs was faul ist...
Hier mal die benutzte Senderoutine, damit sie nicht so lang ist, hab ich 
für das Posting die Interrupt und DMA Option rausgelöscht...
1
Status I2C_Master_BufferWrite(I2C_TypeDef* I2Cx, uint8_t* pBuffer,
2
    uint32_t NumByteToWrite, I2C_ProgrammingModel Mode,
3
    uint8_t SlaveAddress)
4
5
{
6
  __IO uint32_t temp = 0;
7
  __IO uint32_t Timeout = 0;
8
9
10
  if (I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY))
11
            I2C_GenerateSTOP(I2C2, ENABLE);
12
13
  /* Enable Error IT (used in all modes: DMA, Polling and Interrupts */
14
  I2Cx->CR2 |= I2C_IT_ERR;
15
  if (Mode == DMA) /* I2Cx Master Transmission using DMA */
16
  {
17
  ..........
18
  } else if (Mode == Polling) /* I2Cx Master Transmission using Polling */
19
  {
20
21
    Timeout = 0xFFFF;
22
    /* Send START condition */
23
    I2Cx->CR1 |= CR1_START_Set;
24
    /* Wait until SB flag is set: EV5 */
25
    while ((I2Cx->SR1 & 0x0001) != 0x0001) {
26
      if (Timeout-- == 0)
27
        return Error;
28
    }
29
30
    /* Send slave address */
31
    /* Reset the address bit0 for write*/
32
    SlaveAddress &= OAR1_ADD0_Reset;
33
    Address = SlaveAddress;
34
    /* Send the slave address */
35
    I2Cx->DR = Address;
36
    Timeout = 0xFFFF;
37
    /* Wait until ADDR is set: EV6 */
38
    while ((I2Cx->SR1 & 0x0002) != 0x0002) {
39
      if (Timeout-- == 0)
40
        return Error;
41
    }
42
43
    /* Clear ADDR flag by reading SR2 register */
44
    temp = I2Cx->SR2;
45
    /* Write the first data in DR register (EV8_1) */
46
    I2Cx->DR = *pBuffer;
47
    /* Increment */
48
    pBuffer++;
49
    /* Decrement the number of bytes to be written */
50
    NumByteToWrite--;
51
    /* While there is data to be written */
52
    while (NumByteToWrite--) {
53
      /* Poll on BTF to receive data because in polling mode we can not guarantee the
54
       EV8 software sequence is managed before the current byte transfer completes */
55
      while ((I2Cx->SR1 & 0x00004) != 0x000004)
56
        ;
57
      /* Send the current byte */
58
      I2Cx->DR = *pBuffer;
59
      /* Point to the next byte to be written */
60
      pBuffer++;
61
    }
62
    /* EV8_2: Wait until BTF is set before programming the STOP */
63
    while ((I2Cx->SR1 & 0x00004) != 0x000004)
64
      ;
65
    /* Send STOP condition */
66
    I2Cx->CR1 |= CR1_STOP_Set;
67
    /* Make sure that the STOP bit is cleared by Hardware */
68
    while ((I2Cx->CR1 & 0x200) == 0x200)
69
      ;
70
71
  }
72
73
  else /* I2Cx Master Transmission using Interrupt with highest priority in the application */
74
75
  {
76
  ........
77
  }
78
79
  return Success;
80
81
}
im Anhang ist der Interrupt Händler, der ist zum Posten zu Groß....

von Sylvia H. (sandy)


Lesenswert?

ich meine, ich komme dem Problem langsam näher:
es scheint nichts mit dem Timer zu tun zu haben, sondern mit dem senden.
Bevor ich die Sache mit dem Timer angefangen hatte, habe ich ja nur 
gelauscht, und nichts gesendet. Lauschen hatte ja funktioniert.
Nun aber wird auch gesendet, und da bleibt das Programm nach dem senden 
des letzten Byte in der Methode

Status I2C_Master_BufferWrite(I2C_TypeDef* I2Cx, uint8_t* pBuffer,
    uint32_t NumByteToWrite, I2C_ProgrammingModel Mode,
    uint8_t SlaveAddress)


an genau dieser Stelle hängen (warten auf Event 8):
1
while (NumByteToWrite--) {
2
      /* Poll on BTF to receive data because in polling mode we can not guarantee the
3
       EV8 software sequence is managed before the current byte transfer completes */
4
      while ((I2Cx->SR1 & 0x00004) != 0x000004) // HIER HÄNGS!!!
5
        ;
6
      /* Send the current byte */
7
      I2Cx->DR = *pBuffer;
8
      /* Point to the next byte to be written */
9
      pBuffer++;
10
    }

Danach kommt ja erst das Senden der Stop Bedingung, d.h. der Bus ist 
blokiert.

Wie bekomme ich nun den Bus wieder Frei, wenn mein Partnerboard das 
Event 8 nicht sendet?

von Sylvia H. (sandy)


Lesenswert?

hab gerade festgestellt, das


> while ((I2Cx->SR1 & 0x00004) != 0x000004) // HIER HÄNGS!!!

nicht das Even 8 sondern das Event 8_2 abfrägt.
Wenn ich das ändere, und auf Event 8 warte, bleibt der Cortex schon nach 
dem ersten Byte hängen, mein Partner schickt kein Event 8.
Lasse ich nach Event 8_2 ausschau halten, dann kann man bis zum letzten 
byte das Array auf den I2C Bus schicken, aber das letzte Byte wird dann 
nicht mehr vom Partnerboard mit Event 8_2 bestätigt.
Woran hängt das ?

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.