Forum: Mikrocontroller und Digitale Elektronik STM32 UART Rx IRQ verliert Daten


von John W. (halfpastseven)


Lesenswert?

Hallo allerseits,

ich habe ein seltsames Problem mit einem STM32F407ZG
(168MHz Takt, 8MHz Quarz)

Ich bin seit einiger Zeit bei einem Gerät programmieren welches ständig 
um neue ICs etc erweitert wird.

Mittlerweile habe ich folgende Peripherie im Einsatz:

USART2, 115200 8N1, RXNE IRQ
SPI1, Master 1,32MHz
SPI3, Master 1,32MHz
I2C1, I2C 400kHz

TIM1, 100Hz PWM
TIM9, 1ms IRQ
TIM10, 0-2kHz PWM
TIM11, 0-2kHz PWM

Ich habe nur 2 IRQ in Verwendung, alles andere wird über Flags oder 
anders gelöst.
1. USART2 RXNE -> wann Daten vom PC ankommen (hat bereits die höchste 
Priorität)
2. TIM9 -> wird alle 1ms aufgerufen für Timing (hat eine niedrigere 
Priorität)

Mit dem TIM9 wird quasi die Zeitbasis erstellt und bestimmt welcher IC 
(AD, DA etc) wann ausgelesen werden soll (SPI1, SPI3, I2C1) und gibt 
auch das Intervall für die Messwertausgabe über RS232 Tx (USART2) vor.

Mein Problem ist nun, dass sich über die Zeit ein Fehler beim USART2 
RXNE IRQ eingeschlichen hat. Ich bekommen nicht mehr alle Rx Daten im 
Buffer angezeigt, der Controller schluckt quasi ab und zu 1,2,3 Zeichen, 
meist am Schluss.

Ich habe keine Ahnung wie so etwas bei einem IRQ überhaupt passieren 
kann?!?!

Ich habe nun als Versuch alles außer USART2 und TIM9 vom Programm 
eliminiert und nun funktioniert wieder alles, keinen einzigen Fehler!!??

Kann es sein das der µC mit etwas zu "beschäftigt" ist?

Kann ein IRQ eine Warteschleife (bis TXE Flag etc kommt bei SPI, I2C) 
nicht einfach unterbrechen??

Vielleicht hat noch jemand eine Antwort auf meine Fragen oder einen 
Tipp/Hinweis für mich was man kontrollieren oder ändern könnte?!

Bin für jede Hilfe dankbar!!
Vielen Dank im Voraus!

von Juergen G. (jup)


Lesenswert?

An welcher Stelle in der ISR setzt Du das RXNE Flag zurueck?

Vor oder nach dem Auslesen des DatenRegisters?

von John W. (halfpastseven)


Lesenswert?

Vor dem Auslesen, sofort nach dem Aufrufen.
Könnte das das Problem sein??

Aber wieso funktioniert es dann ohne die restliche Peripherie dann ohne 
Probleme?

Nachtrag:
Habe es gerade mal vertauscht, aber die fehlenden Zeichen kommen immer 
noch vor.

von Juergen G. (jup)


Lesenswert?

Dann kommst Du wohl nicht drumherum Dir mal ins Eingemachte gucken zu 
lassen.

>Kann es sein das der µC mit etwas zu "beschäftigt" ist?

glaube nicht das dein Programm einen 168MHz cm4 auslastet.

Wenn die Prozessorlast allerdings sehr hoch ist, ist es ratsam alle 
Peripherie wo es geht ueber ISR's auszuwerten.
Evtl die UART und SPI Geschichte ueber DMA.

hast Du den RX Buffer und den Buffer-Index-Counter als volatile 
declariert?

Ju

von Jim M. (turboj)


Lesenswert?

John W. schrieb:
> [...] hat bereits die höchste
> Priorität [...]

Zeige bitte den Code. Dir ist bekannt, das bei Cortex M die höhere Zahl 
eine niedrigere Priorität hat?

von John W. (halfpastseven)


Lesenswert?

Vielen Dank erstmal an alle für eure Hilfe.

@ Juergen G
Nein, ich hatte Buffer und Counter nicht mit volatile deklariert.
Habe es aber gerade ausprobiert, am Anfang schien es als ob es besser 
wäre aber leider tritt der Fehler immer noch auf. Habe sie aber nun 
trotzdem mit volatile deklariert lassen.

@ Jim Meba
Ja das ist mir bekannt.
Der USART2 RXNE hat Priority 0 und SubPriority 0, der TIM9 BRK hat 
Priority 1 und SubPriority 1.

Hier der Code von TIM9 und USART2 Init sowie USART2 RXNE IRS:
1
  NVIC_InitTypeDef NVIC_InitStructure;
2
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
3
  TIM_OCInitTypeDef  TIM_OCInitStructure;
4
  
5
  /* TIM9 clock enable */
6
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM9, ENABLE);
7
8
  /* Enable the TIM9 global Interrupt */
9
  NVIC_InitStructure.NVIC_IRQChannel = TIM1_BRK_TIM9_IRQn;
10
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
11
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
12
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
13
  NVIC_Init(&NVIC_InitStructure);
14
15
  /* Compute the prescaler value */
16
  TIM9_PrescalerValue = (uint16_t) (SystemCoreClock / 16000000) - 1;  
17
18
  /* Time base configuration */
19
  TIM_TimeBaseStructure.TIM_Period = TIM9_ARR;
20
  TIM_TimeBaseStructure.TIM_Prescaler = TIM9_PrescalerValue;
21
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
22
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
23
  TIM_TimeBaseInit(TIM9, &TIM_TimeBaseStructure);
24
25
  /* Output Compare Toggle Mode configuration: Channel1 */
26
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
27
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
28
  TIM_OCInitStructure.TIM_Pulse = TIM9_CCR1;
29
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
30
  TIM_OC1Init(TIM9, &TIM_OCInitStructure);
31
32
  TIM_OC1PreloadConfig(TIM9, TIM_OCPreload_Disable);  
33
34
  /* TIM enable counter */
35
  TIM_Cmd(TIM9, ENABLE);
36
37
  /* TIM IT enable */
38
  TIM_ITConfig(TIM9, TIM_IT_CC1, ENABLE);

1
   GPIO_InitTypeDef  GPIO_InitStructure;
2
   USART_InitTypeDef  USART_InitStructure;
3
   NVIC_InitTypeDef  NVIC_InitStructure;    
4
  
5
    /* Enable USART2 Clock */
6
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
7
  
8
    /* Enable GPIO clock */
9
   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);  
10
  
11
  
12
   // Tx Pin 
13
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
14
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
15
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
16
   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
17
   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
18
   GPIO_Init(GPIOA, &GPIO_InitStructure); 
19
20
   // Rx Pin
21
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
22
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
23
   GPIO_Init(GPIOA, &GPIO_InitStructure); 
24
  
25
   /* Connect USART2 pins to AF7 */  
26
   GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);
27
   GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);   
28
  
29
30
   USART_InitStructure.USART_BaudRate = 115200;      
31
   USART_InitStructure.USART_WordLength = USART_WordLength_8b;
32
   USART_InitStructure.USART_StopBits = USART_StopBits_1;
33
   USART_InitStructure.USART_Parity = USART_Parity_No;
34
   USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
35
   USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
36
   USART_Init(USART2, &USART_InitStructure);    
37
  
38
  /* Enable the USARTx Interrupt */
39
  NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
40
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
41
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
42
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
43
  NVIC_Init(&NVIC_InitStructure);
44
45
  /* Enable USART */
46
  USART_Cmd(USART2, ENABLE);
47
 
48
  /* Enable Receive interrupt */
49
  USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);


1
void USART2_IRQHandler( void ) 
2
{
3
     // RXE Interrupt, wenn neues Zeichen empfangen wurde auf 1 
4
  if(USART2->SR & USART_SR_RXNE)     
5
  {
6
    USART2->SR &= ~USART_SR_RXNE;  // Receive IR-Flag zuruecksetzen    
7
    
8
    // wenn der letzte Befehl verarbeitet wurde
9
    if (RS232_In == 0)    
10
    {      
11
                    
12
      //  Zeichen nur übernehmen wenn es ein zulässiges ASCII Zeichen ist (a-z, A-Z, 0-9, '/', ';', '<', '>', '=', ',' , CR, '.', '-')
13
      if( (RxCounter < RS232_RX_ZEICHEN_MAX) && ( ((USART2->DR >= 0x61) && (USART2->DR <= 0x7A)) ||
14
        ((USART2->DR >= 0x2F)&&(USART2->DR <= 0x39)) || ((USART2->DR >= 0x41)&&(USART2->DR <= 0x5A)) ||
15
        ((USART2->DR >= 0x3B)&&(USART2->DR <= 0x3E)) || (USART2->DR <= 0x2C) || (USART2->DR == 0x0D) || 
16
        (USART2->DR == 0x2E) || (USART2->DR == 0x2D)) )
17
      {
18
        RxBuffer[RxCounter] = USART2->DR ;  // vom Eingangsbuffer ins RxBuffer kopieren          
19
        RxCounter++;        // und Zähler erhöhen
20
        
21
        // beim Endzeichen '>' oder 'CR' wenn vollst Befehl übernehmen und ganz vorne anfangen 
22
        if( (USART2->DR == 0x3E) || (USART2->DR == 0x0D) )   
23
        {  
24
          RS232_In = 1;
25
          RxCounter = 0;  
26
        }
27
        // bei zuvielen Zeichen, Buffer voll, jedenfalls ungültig, daher verwerfen  
28
        if( RxCounter >= RS232_RX_ZEICHEN_MAX )
29
        {  
30
          RxCounter  = 0;
31
        }
32
      
33
      }      
34
      
35
    }
36
// -> Fehlersuche RS232
37
//    else
38
//    {
39
//      RxBuffer_Fail[RxCounter_Fail++] = USART2->DR;
40
//    }      
41
42
    
43
// -> Fehlersuche RS232  
44
//    RxBuffer_Test[Rx_Cnt++] = USART2->DR;  
45
    
46
    
47
  }    
48
}


Ich möchte nocheinmal betonen das der Code bzw die RS232 ohne sonstige 
SPI1/3 etc Vorgängen sich völlig normal verhält und KEIN Fehler 
auftritt!!!

Was kann also die USART2 Hardware oder den USART2 IRS dazu veranlassen 
ab und zu nicht auszulösen??

Auffällig ist noch, dass meistens das 2.+ 3. Zeichen oder die letzen 2-3 
Zeichen fehlen und somit auch das Schlusszeichen!

von Juergen G. (jup)


Lesenswert?

John W. schrieb:
> if( (RxCounter < RS232_RX_ZEICHEN_MAX) && ( ((USART2->DR >= 0x61)
> && (USART2->DR <= 0x7A)) ||
>         ((USART2->DR >= 0x2F)&&(USART2->DR <= 0x39)) || ((USART2->DR >=
> 0x41)&&(USART2->DR <= 0x5A)) ||
>         ((USART2->DR >= 0x3B)&&(USART2->DR <= 0x3E)) || (USART2->DR <=
> 0x2C) || (USART2->DR == 0x0D) ||
>         (USART2->DR == 0x2E) || (USART2->DR == 0x2D)) )
>       {


Das wuerde ich nicht so machen.

lies erst USART2->DR in eine Variable und mache die Auswertung an der 
Variablen.

Ich bin mir nicht ganz sicher, aber nur mal so als Idee.

Waerend Deiner Evaluation kann schon das naechste Byte gesendet worden 
sein.
RXNE ist geloescht aber der UART wird das Byte nicht annehmen weil DR 
noch nicht gelesen wurde.

Probiers einfach mal aus.

von John W. (halfpastseven)


Angehängte Dateien:

Lesenswert?

Bin nun ein gutes Stück vorangekommen.

Ich habe nun die USART2 auf Hardware Flow Control umgestellt und direkt 
an die Serielle von meinem PC gehängt, dann war der Fehler weg (soweit 
ich das mit dem Terminalprogramm sagen kann)!!

Leider funktioniert dies (noch) nicht mit meiner eigentlichen 
Gegenstelle dem USB HID IC CP2110. Mit der eigentlich PC Software wird 
nähmlich über USB HID kommuniziert.

Ich habe jedenfalls dann mit dem Oszi auf die Leitung RTS vom Controller 
getriggert und festgestellt, das er ab und zu für 100 - 400µs auf high 
ist und somit beschäftigt ist!

Da es nur die TIM9 IRS sein konnte welche den USART2 IRS abhalten konnte 
diesen auzuführen, habe ich einen Pin solange auf 1 geschalten, solange 
diese ausgeführt wird!

Damit wurde meine Vermutung bestätigt, dass es wirklich die TIM9 IRS ist 
welche den Controller davon abhält die Daten auszuwerten
(für max. 380µs (~3-4 ASCII Zeichen)!!)

Zu den Bildern:
Kanal A (rot) ist der RTS Pin vom STM, Kanal B (blau) ist die TIM9 IRS.
Die TIM9 IRS wird alle 1ms aufgerufen für die Zeitbasis, alle 10ms 
werden aber alle IC ausgelesen (SPI1, I2C), dann braucht er natürlich 
etwas länger!

Meine Fragen nun:

- Warum kann USART2 IRS die TIM9 IRS nicht unterbrechen obwohl die 
Priorität richtig gesetzt ist??

- Muss man diese IRQ Funktion zum Unterbrechen vielleicht noch 
zusätzlich irgendwie aktivieren???

- Wenn zB. eine SPI Funktion zum Auslesen von einem IC innerhalb von der 
TIM9 IRS aufgerufen wird, zählt diese dann trotzdem zur TIM9 IRS oder 
könnte das Probleme verursachen?

Vielleicht habe ich auch grundlegend etwas am NVIC/IRQ/IRS nicht 
verstanden, bitte daher um eure Hilfe!!

Vielen Dank im Voraus!

von John W. (halfpastseven)


Lesenswert?

@ Juergen G.
Danke für diesen Hinweis und die gute Erklärung.
Es wird an meinem aktuellen Problem sicher nichts ändern aber das werde 
ich demnächst zur Sicherheit auf deine Variante umstellen.

Wie sollte man nun eine IRS ganz korrekt abarbeiten?
Zuerst Daten kopieren, auswerten und dann erst das Flag löschen oder?

von Juergen G. (jup)


Lesenswert?

John W. schrieb:
> Wie sollte man nun eine IRS ganz korrekt abarbeiten?

Das solltest Du individual festlegen, es kommt auf den IRQ, Prioritaet, 
Haeufugkeit, Systemlast etc. an und es gibt da kein Standardvorgehen.

zBsp. kann ein SPI, CAN oder Ethernet viel mehr Daten/s empfangen als 
ein UART.
Wenn dann eine (nicht so aufwendige) Evaluation stattfinden soll die aus 
irgendwelchen Gruenden in die ISR muss, ist es ratsam die Daten zu lesen 
dann das Flag zuruecksetzen dann die Evaluation.
Manchmal auch Evaluation und dann Flag zuruecksetzen.

Wenn die Evaluation aufwendiger ist, dann in der ISR nur Daten lesen und 
Flag zuruecksetzen, Evaluation dann an anderer Stelle.

Bei vielen uC's kann man auch auf Memory Value Change Triggern.

Aber wie gesagt, es kommt auf den speziellen Fall an und sollte immer 
fuer genau diesen Fall entschieden werden.

von John W. (halfpastseven)


Lesenswert?

Danke für die ausführliche Erklärung!

Hast du oder sonst jemand zufällig eine Erklärung (oder Theorie) 
bezüglich meiner letzten Fragen von oben??

von Juergen G. (jup)


Lesenswert?

Daten Kopieren, Flag loeschen, Daten auswerten.

Wenn das Flag geloescht ist, kann waerend Du Daten auswertest das 
naechste Byte schon reinkommen und wenn Du die ISR verlaesst springt der 
uC gleich wieder in die ISR wenn ein Byte da ist.

Das ist aber beim UART, Deinem speziellen Fall und einem 168MHz uC 
ziehmlich unwarscheinlich. Da die Rechenzeit in der ISR viel kuerzer ist 
als der UART Daten empfangen kann.

In Deinem Fall, Du schreibst dass die TIM9 ISR fuer Verzoegerung sorgt, 
wuerde ich diese ISR auf das Minimum and notwendiger Arbeit 
zusammenkuerzen.

Wenn TIM9 sowieso nur eine Timebase ist um zBsp. den ADC um eine 
Konversion zu bitten oder die Daten am DA zu aendern, kannst Du das 
ueber Flags machen.
Also eine globale (volatile) Variable als Flagregister nehmen und in der 
ISR nur die Flags setzten. In der Hauptschleife dann die Flags abfragen 
und die Arbeit machen lassen.
Dann ist es auch recht unwarscheinlich das die TIM9 ISR Deinen UART 
stoert.

von Juergen G. (jup)


Lesenswert?

Kleine Ergaenzung noch, falls Du Dein Programm an den Beispielen von ST 
orientierst.
Die Programmiertechnik der Chiphersteller ist nicht "Real Project" 
tauglich.

Die vielen
1
while(warte_auf_flag)
2
{}

koennen Dir ganz schoen viel Prozessorzeit kosten.

Die Beispiele sind nur um zu zeigen das die Peripherie auch das macht 
was der Chiphersteller geplant hat. Der Beispiel code macht nichts 
anderes als das zu zeigen. Wenn Du viele dieser Codebeispiele zusammen 
in ein Programm packst, erlebst Du Dein Blaues Wunder und jeder Atmega 
ist schneller als Dein ARM.

Die Programmlogik und das Timing ist das Hauptproblem der uC 
Programmierung.
Du solltest ein Timing-Diagramm fuer Deine Programme erstellen. Dann 
kannst Du zBsp. sehen das Du in einer Timebase nur jeden dritten IRQ 
irgendetwas machen musst, aber jedes mal was anderes.

So zBsp.
1. IRQ -> ADC anstossen
2. IRQ -> DAC updaten
3. IRQ -> Daten senden

dann wieder von vorn.

somit hat Deine ISR auch immer die selbe Verarbeitungszeit

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Wenn da wirklich noch ein ADC läuft, empfiehlt es sich m.E. sowieso, für 
diesen die DMA zu nutzen, dann bleibt das Interruptsystem völlig frei. 
Das gleiche geht übrigens auch für UARTs...

von John W. (halfpastseven)


Angehängte Dateien:

Lesenswert?

@ Matthias Sch.
Die ADC sind extern über SPI, nicht die internen.
Aber du hast Recht, man sollte sicher auf DMA bei den Bussystemen 
umsteigen.

@ Juergen G
Danke vielmals für deine bisherige Hilfe.

Ich muss leider zugeben das ich doch einige Beispiele der ST Lib so 
verwendet habe (while Abfrage). Mir ist das zwar von Anfang an etwas 
komisch vorgekommen aber als Anfänger habe ich da nicht mehr großartig 
darüber nachgedacht und war froh das überhaupt der SPI funktioniert hat.

Falls ich nun doch sämtliche Busperipherie auf DMA und IRQ umstelle, 
würde das natürlich vieles erleichtern, aber auch viel arbeit bedeuten. 
Die Geschwindigkeitsprobleme sollten dann aber sicher der Vergangenheit 
angehören, auch wenn die 168MHz natürlich von Haus aus etwas oversized 
sind.

Ich habe es aktuell so das TIM9 eben die Zeitbasis erstellt wird und 
alle 10ms die AD Wandler ICs ausgelesen werden. Diese müssen teilweise 
ein paar ms zuvor gestartet werden was ich ebenfalls in der IRS mache.
Da die AD Wandler ICs aber quasi gleichzeitig ausgelesen werden 
müssen(!), damit der zeitlicher Bezug zwischen den Messkanälen stimmt, 
habe ich das Auslesen über eine Funktion auch in TIM9 IRS geschrieben 
und diese dann nacheinander ausführen lassen.
Mit der Zeit hat sich das dann immer so um IC und IC erweitert und dann 
sind eben irgendwann die Zeichenfehler aufgetreten.

Muss ich mir mal genau überlegen wie ich das verbessern bzw umstellen 
könnte!?!?

Mich interessiert aber trotzdem noch immer warum die USART IRS die TIM9 
IRS nicht einfach unterbrechen kann!!!????
Das ist doch auch einer der angepriesenen Vorteil beim STM NVIC oder??
(siehe Bild)

Danke im Voraus.

von (prx) A. K. (prx)


Lesenswert?

John W. schrieb:
>     USART2->SR &= ~USART_SR_RXNE;  // Receive IR-Flag zuruecksetzen

Das ist zwar nicht die Ursache, aber trotzdem falsch, weil so in 
seltenen Fällen versehentlich auch andere IR Flags des Registers 
zurückgesetzt werden können. Besser ist
   USART2->SR = ~USART_SR_RXNE;
Siehe Definition von rc_w0.

von (prx) A. K. (prx)


Lesenswert?

Noch ein Nebentipp: Ich würde empfehlen, für den NVIC nicht die 
veralteten Funktionen der ST-Lib zu verwenden, sondern die vom CMSIS.

von (prx) A. K. (prx)


Lesenswert?

Juergen G. schrieb:
> Das wuerde ich nicht so machen.
>
> lies erst USART2->DR in eine Variable und mache die Auswertung an der
> Variablen.

Das ist nicht nur sinnvoll, sondern zwingend, da bei UARTs ein 
Lesevorgang auf DR üblicherweise zerstörend wirkt. Wenn mehr als ein 
Zeichen im Puffer liegt, dann geht mit jedem überschüssigen Zugriff auf 
DR ein Zeichen verloren.

von (prx) A. K. (prx)


Lesenswert?

A. K. schrieb:
>    USART2->SR = ~USART_SR_RXNE;
> Siehe Definition von rc_w0.

Korrektur: Für RXNE gilt: "It is cleared by a read to the USART_DR 
register."

Also: DR in jedem Fall erst einmalig auslesen, dann weiterverarbeiten. 
RXNE in Ruhe lassen.

von M. N. (Gast)


Lesenswert?

John W. schrieb:
> - Warum kann USART2 IRS die TIM9 IRS nicht unterbrechen obwohl die
> Priorität richtig gesetzt ist??

Wie sieht denn die TIM9 ISR aus?
Damit sie unterbrechbar wird, muß gleich zu Beginn etwas wie
__enable_interrupt();
stehen. Oder verwechsel ich da etwas?

von (prx) A. K. (prx)


Lesenswert?

M. N. schrieb:
> Oder verwechsel ich da etwas?

Ja. Die Cortex-Mx unterstützen ein verschachteltes Interrupt-System mit 
Prioritäten. In einer ISR sind automatisch nur Interrupts gleicher und 
niedrigerer Prio blockiert, nicht aber alle.

von Juergen G. (jup)


Lesenswert?

John W. schrieb:
> - Warum kann USART2 IRS die TIM9 IRS nicht unterbrechen obwohl die
> Priorität richtig gesetzt ist??

Das ist schon seltsam, aber nested IRQ's beim F4 habe ich schon gemacht 
und es funktioniert. Also ist der Wurm bestimmt im Programm.

vielleicht benutzt Du mal

NVIC_SetPriority(XXX_IRQn, y);
NVIC_GetPriority(XXX_IRQn);
NVIC_EncodePriority
NVIC_DecodePriority

um zu sehen wie die Prioritaten aussehen wenn alles initialisiert ist.

Manche IRQs lassen sich nicht durch NVIC_Init() beindrucken, da muss man 
die Prioritaet nach dem activieren setzen.
Das sind aber mE nur die core IRQ's und da gehoeren der Tim9 und der 
UART glaube ich nicht dazu.

Aber probier mal was GetPriority sagt, nachdem alles initialisiert ist.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

John W. schrieb:
> sämtliche Busperipherie auf DMA und IRQ umstelle,
> würde das natürlich vieles erleichtern, aber auch viel arbeit bedeuten.

Naja, es muss ja nicht gleich sämtliche Peripherie sein, in deinem Fall 
reicht ja der USART. Ist auf jeden Fall mal lehrreich, sich mit der DMA 
zu beschäftigen, denn z.B. der interne ADC ist mit mehreren Kanälen ohne 
DMA nicht sinnvoll zu benutzen.
Das die STM32 durch die etwas ungeschickte Doku kein Sonntagsspaziergang 
sind, ist dir ja bereits klar, hehehe.
M.E. ist es sinnvoll, die Prioritäten der beiden IRQs auch mal 
auszutauschen, denn eine USART ist ein eher langsames Dings.
So mach ich es z.B. in meinem BLDC Projekt auch, und da geht nichts 
verloren, trotz recht heftigen Timer- und Hallsensor IRQs.

von M. N. (Gast)


Lesenswert?

A. K. schrieb:
> M. N. schrieb:
>> Oder verwechsel ich da etwas?
>
> Ja. Die Cortex-Mx unterstützen ein verschachteltes Interrupt-System mit
> Prioritäten. In einer ISR sind automatisch nur Interrupts gleicher und
> niedrigerer Prio blockiert, nicht aber alle.

Das ist schon klar. Ich habe es mit den Renesas RX-ISRs verwechselt. 
Diese brauchen eine Spezialbehandlung, da mit jedem Interrupt das 
globale 'I'-Flag gelöscht wird, wodurch auch höher priorisierte ISRs 
gesperrt werden. Das ist zwar eigenartig, aber vielleicht braucht man so 
etwas ja irgendwo.

Juergen G. schrieb:
> vielleicht benutzt Du mal
>
> NVIC_SetPriority(XXX_IRQn, y);
> NVIC_GetPriority(XXX_IRQn);
> NVIC_EncodePriority
> NVIC_DecodePriority
>
> um zu sehen wie die Prioritaten aussehen wenn alles initialisiert ist.

Das würde ich auch sagen und kontrollieren, ob in TIM9 ISR nicht eine 
INT-Sperre eingebaut ist. Zudem sind 100-400µs ja fast eine Ewigkeit.

von John W. (halfpastseven)


Angehängte Dateien:

Lesenswert?

Wow, vielen Dank an alle für eure Hilfe und die ganzen Antworten!!

@ A. K.
Danke für den Hinweis mit dem Löschen der IRQ Flags usw.
Habe nun die USART2 IRS auf die empfohlene Variante von dir und Juergen 
G. umgestellt.

Was meinst du genau mit NVIC Funktionen von CMSIS verwenden?
Meinst du die Funktionen aus der core_cm4.h oder gibt es da noch etwas??
Wenn es was anderes gibt wäre ein Link sehr hilfreich.

@ Juergen G.
Du hast wiedermal voll ins Schwarze getroffen.
Nach dem bisherigen initaliseren waren beide IRQ auf Priority 0!!
Ich habe dann mit NVIC_SetPriority() die Priority vom TIM9 auf 1 
gestellt und dann hat es endlich so funktioniert wie ich das haben 
wollte!! (siehe Bild)

Muss ich alle NVIC Funktionen die du aufgeschrieben hast auch ausführen?
Verstehe noch nicht ganz wozu NVIC_EncodePriority() und 
NVIC_DecodePriority() genau dient!?
Warum gibt es dort Priority, PriorityGroup, PreemptPriority und 
SubPriority?
Ich kenne nur Priority und SubPriority vom NVIC der ST-Lib!?!?

Wie M. N. schrieb (Zudem sind 100-400µs ja fast eine Ewigkeit.) sollte 
ich über das Handling/Timing der AD Wandler vermutlich noch mal 
nachdenken.
Obwohl es nun mit der funktionierenden IRQ Priority keine Fehler mehr 
gibt, wäre eine Lösung über Flags vermutlich sinnvoller - oder wie 
würdet ihr das lösen??

Wenn die ST-Lib Funktionen mit den while Schleifen nicht gut sind, was 
kann man da dann genau umstellen bzw verbessern?? Alles auf IRQ oder DMA 
mit IRQ???

Mir hat es an und für sich schon gut gefallen das zB am Ende meiner SPI1 
Sende/Empfangsfunktion die Auswertung gleich folgen konnte und ich genau 
wusste, welche Daten von welchem IC gerade im Buffer stehen, eben wie im 
C-Code nacheinander programmiert. Ich schätze das würde bei einer IRQ 
Lösung schon viel komplizierter werden...

Schöne Grüße

von (prx) A. K. (prx)


Lesenswert?

John W. schrieb:

> Meinst du die Funktionen aus der core_cm4.h

Ja. Der NVIC-Support der ST-Lib ist dort nur noch drin, weil er aus der 
Zeit vor CMSIS geerbt wurde.

von funky (Gast)


Lesenswert?

Wovon ist das abhängig ob ein Interrupt die Priority Einstellung über 
die NVIC_SetPriority() Funktion benötigt oder über das NVIC Init struct?

In den Beispielen von ST wird ja eigentlich immer das Struct benutzt.

von (prx) A. K. (prx)


Lesenswert?

funky schrieb:
> Wovon ist das abhängig ob ein Interrupt die Priority Einstellung über
> die NVIC_SetPriority() Funktion benötigt oder über das NVIC Init struct?

Es führen viele Wege nach Rom. Man kann auch direkt an die Hardware.

Oder man verwendet http://www.libopencm3.org für Core- und 
Non-Core-Kram. Das erspart einem die umständlichen Structs.

> In den Beispielen von ST wird ja eigentlich immer das Struct benutzt.

Weil die Beispiel entsprechend alt sind.

Anfangs gab es CMSIS nicht, also baute ST den NVIC-Support in die Lib. 
Später kam CMSIS. Rausgeworfen hat ST deren Kram natürlich nicht, aber 
meiner vagen Erinnerung nach haben es zwischenzeitlich deshalb in ein 
anderes Include-File verschoben.

von Juergen G. (jup)


Lesenswert?

John W. schrieb:
> Muss ich alle NVIC Funktionen die du aufgeschrieben hast auch ausführen?

Nein, das war nur ein Denkanstoss um herauszufinden wo der Hase im 
Pfeffer liegt.
Wenn Get/SetPriority nicht zum Ziel fuehren, hat man mit den 
Encode/Decode Routinen ein Werkzeug um etwas tiefer ins Eingemachte zu 
gehen.

John W. schrieb:
> Verstehe noch nicht ganz wozu NVIC_EncodePriority() und
> NVIC_DecodePriority() genau dient!?

Um STM32 Einsteiger zu verwirren ;-)

Du musst nicht alles auf einmal Lernen.
Kleiner allgemeiner Hinweis.
Wenn Dir irgendetwas Raetsel aufgibt, Du es im Moment aber nicht 
unbedingt brauchst, ist es besser, es erst mal zu ignorieren.
Spaeter wenn Du dann mal ein Problem hast wo Du es unbedingt brauchst 
wirst Du es auch verstehen.

Salopp gesagt sind
NVIC_EncodePriority() und NVIC_DecodePriority()
andere Funktionen um die Prioritaeten zu Gruppieren und innerhalb der 
Gruppen zu Ordnen. Bei Groesseren Projekten kann man schnell mal die 
Uebersicht verlieren, wenn man in jeder Classen Datei Prioritaeten 
vergibt ohne auf die anderen schon vergebenen zu achten.

von Juergen G. (jup)


Lesenswert?

Nochmal zu Deinen SPI ADC's.

Du schreibst weiter oben,

John W. schrieb:
> und
> alle 10ms die AD Wandler ICs ausgelesen werden. Diese müssen teilweise
> ein paar ms zuvor gestartet werden was ich ebenfalls in der IRS mache.

Wenn die Conversion vorher angestossen werden muss ist es egal wann Du 
sie in welcher Reihenfolge ausliest, nur angestossen werden muessen sie 
zur selben Zeit.

also innerhalb der TIM9 ISR einen Counter der immer bei 10 
zurueckgesetzt wird.
Wenn zurueckgesetz, die ADC's anstossen und je nachdem wie lange sie 
fuer eine Conversion brauchen, dann in
Counter = X  das Flag setzen um den entsprechenden ADC in der Mainloop 
auzulesen.
Wenn ale ausgelesen sind die Auswertung oder den Transfer machen.

Das alles vom jetzigen Code auf ISR umzustellen ist kein Problem.
Kopiere den Code den Du in der ISR zum Auslesen und Auswerten hast in 
eine Funktion.
In der ISR setzt Du nur ein Flag und in der MainLoop rufst Du die 
Funktion auf.

von John W. (halfpastseven)


Lesenswert?

Ok, dann habe ich soweit alles verstanden!

Vielen Dank nochmal an alle und speziell an dich Juergen G., du hast mir 
wirklich sehr weitergeholfen in den letzten Tagen!!

Vielleicht liest man sich mal wieder bei einem weiteren STM32 Problem... 
;-)

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.