STM32 Interrupts - Welche Regeln sollte man beachten?

Gast #6634580
Lesenswert?

Moin,

sorry..... aktuell kommen grad echt ne Menge Fragen auf, die teilweise 
nicht eindeutig zu beantworten sind durch Googlen :-)

habe mir bis gestern um 2 Uhr und heute morgen weiter mal meinen kleinen 
NMEA Parser geschrieben für den GPS RMC Satz. Mehr brauche ich nicht, 
daher auch nix aus dem Netz besorgt. Für die 4 Werte.

Das liegt derzeit alles im ISR Handler, wenn die USART das letzte Byte 
erkannt hat und danach 1s Pause ist. Bläht den Code aber mal eben so um 
5 kb auf durch die String Funktionen und 1 x float Rechnung.

Der nicht optimierte Code (-O0) braucht nach Messung mit dem DWT Counter 
3800 Zyklen für vollen Durchlauf.

Gibt es da eine Schmerzgrenze? Oder sollte man besser ein Flag setzen, 
damit das in der arbeitslosen Hauptroutine erledigt wird? Aktuell läuft 
alles über INTs, die ganze Datensammelei, das Ein/Aus der Peripherie 
usw.

Würde mich mal interessieren, wie die Profis das so machen?
1
/* UART1 Interrupt Service Routine */
2
void USART1_IRQHandler() {
3

4
    #define MAX_NMEA_FIELDS 16
5
    static char* nmea_fields[MAX_NMEA_FIELDS];
6

7
    /* GPS Empfang abgeschaltet? Dann alles verwerfen */
8
    if (fGPSRecDisable) {
9
        /* Buffer ungültig machen */
10
        fGPSDataValid = false;
11
        NmeaBuf_Ptr = 0;
12
        USART1->SR;
13
        USART1->DR;
14
        return;
15
    }
16

17
    /* Neues Byte wurde empfangen vom GPS Modul */
18
    if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
19
    {
20
       // USART_ClearITPendingBit(USART1, USART_IT_RXNE);
21
       NmeaBuf[NmeaBuf_Ptr] = USART_ReceiveData(USART1);
22
       NmeaBuf_Ptr = (NmeaBuf_Ptr + 1) % BUFSIZE;               /* Pointer auf nächstes Byte */
23
       fGPSReceiveInProgress = true;
24
       SetLED(GELB,ENABLE);
25
    }
26

27
    /* Timeout, letztes Byte wurde empfangen */
28
    if (USART_GetITStatus(USART1, USART_IT_IDLE) == SET)
29
    {
30
       fGPSReceiveInProgress = false;
31

32
       /* FIXXXXXME Test */
33
       strcpy((char*)NmeaBuf,"$GNRMC,124520.00,A,5200.19391,N,00910.23188,E,30.585,231.29,180121,,,A*45");
34

35
        /* String terminieren */
36
        NmeaBuf[NmeaBuf_Ptr] = 0;
37

38
       /* Vorsortierung: Überprüfe die ersten Zeichen auf $GNRMC*/
39
       const char str1[] = "$GNRMC";
40
       if (strncmp((char*)NmeaBuf,str1,sizeof(str1)-1) != 0)
41
            fGPSDataValid = false;
42
       else {
43

44
           fGPSDataValid = true;
45

46
           /* String zerteilen, der durch , separiert ist */
47
           uint8_t i = 0;
48
           char* ptr = (char*)NmeaBuf;
49
           nmea_fields[i++] = ptr;  /* Erstes Element setzen, NMEA Typ RMC */
50

51
           /* Feld auf einzelne Teil Strings erzeugen */
52
           while (((ptr = strchr(ptr,',')) != NULL) && (i < MAX_NMEA_FIELDS)) {
53
                *ptr = '\0';
54
                nmea_fields[i++] = ++ptr;
55
           }
56

57
           char foo[4];
58

59
           /* Gültiger Fix = 1, kein Fix = 0 */
60
           if (strncmp((char*)nmea_fields[2],"A",1) == 0)
61
               gps_info.validfix = 1;
62
           else
63
               gps_info.validfix = 0;
64

65
           /* Uhrzeit extrahieren */
66
           char *p = (char*)nmea_fields[1];
67
           gps_info.utc_hour = atoi(strncpy(foo,p,2));
68
           p +=2;
69
           gps_info.min = atoi(strncpy(foo,p,2));
70
           p +=2;
71
           gps_info.sec = atoi(strncpy(foo,p,2));
72

73
           /* Geschwindigkeit extrahieren in khm */
74
           gps_info.speedkhm = (float)atoi((char*)nmea_fields[7]) * 1.852;
75
       }
76

77
       NmeaBuf_Ptr = 0; /* Reset RX Pointer */
78
       SetLED(GELB,DISABLE);
79
    }
80

81
    /* Overflow Error abfangen */
82
    if (USART_GetITStatus(USART1, USART_IT_ORE_RX) != RESET)
83
    {
84
        fGPSDataValid = false;
85
        NmeaBuf_Ptr = 0; /* Reset RX Pointer & Clear */
86
        //memset((void*)NmeaBuf,0,sizeof(NmeaBuf));
87
    }
88

89
    /* Alle Error Flags löschen */
90
    USART1->SR;
91
    USART1->DR;
92

93
}
Gast #6634620
Lesenswert?

Christian J. schrieb:
> Gibt es da eine Schmerzgrenze?

Das kommt ganz auf deine Anwendung an. Während diese ISR läuft, sind 
alle niedriger priorisierten Interrupts gesperrt.

Wenn die ISR zur Verarbeitung der Daten länger braucht, als die nächsten 
Daten herein kommen, hast du ganz sicher eine Schmerzgrenze 
Überschritten.
Gast #6634649
Lesenswert?

Stefan ⛄ F. schrieb:
> Das kommt ganz auf deine Anwendung an. Während diese ISR läuft, sind
> alle niedriger priorisierten Interrupts gesperrt.

Klar, logisch.... dachte nur es gibt da heilige Grals, die man nicht 
verletzten sollte.

Nimmt man zb das Wörtchen static hier weg klappt die Auswertung schon 
nicht mehr, da bleiben Felder dann leer. Ohne mir das genau angeschaut 
zu haben was da jetzt nicht klappt. Normalerweise deklariere ich alles 
als static im Int, damit es im .data (oder .bss?) Bereich liegt damit da 
keine "Stack Geschichten" passieren können.

static char* nmea_fields[MAX_NMEA_FIELDS];
Gast #6634694
Lesenswert?

Christian J. schrieb:
> Nimmt man zb das Wörtchen static hier weg klappt die Auswertung schon
> nicht mehr, da bleiben Felder dann leer. Ohne mir das genau angeschaut
> zu haben was da jetzt nicht klappt. Normalerweise deklariere ich alles
> als static im Int, damit es im .data (oder .bss?) Bereich liegt damit da
> keine "Stack Geschichten" passieren können.

Gutgemeinter Rat: Besorg Dir ein C-Buch.
Dein Code macht mir Angst...
Gast #6634714
Lesenswert?

Zum einen:
1
   char foo[4];
2
   ...
3
   gps_info.min = atoi(strncpy(foo,p,2));
Der strncpy macht nicht das, was du dir vorstellst.

Zum anderen: String-Funktionen in Interrupts zu benutzen ist unsauber. 
POSIX erlaubt sie z.B. nicht in signal-Handlern.  Die, die du benutzt, 
sind wahrscheinlich OK aber es gab Zeiten, wo sie z.B. auf x86 
sporadisch nicht funktionierten (direction-flag falsch).  Komplexere wie 
z.B. strtok oder printf sind immer ein no-go.
Gast #6634721
Lesenswert?

foobar schrieb:
> Der strncpy macht nicht das, was du dir vorstellst.

Und was macht er? Das sah vorher anders aus und wurde nur zusammen 
geschrumpft. Der Rückgabewert ist aber ein Zeiger auf das was ich haben 
will. Laufen tut es zumindest. Wie geht es besser?

PS: Es fehlt der \0 Terminator, der wird ja nicht kopiert. Das klappt 
also nur zufällig.

Declaration
Following is the declaration for strncpy() function.
char *strncpy(char *dest, const char *src, size_t n)
Parameters
dest − This is the pointer to the destination array where the content is 
to be copied.
src − This is the string to be copied.
n − The number of characters to be copied from source.

Return Value
This function returns the final copy of the copied string.
Gast #6634728
Lesenswert?

man strncpy:
1
   ...
2
       The  strncpy() function is similar, except that at most n
3
       bytes of src are copied.  WARNING: If there  is  no  null
4
       byte among the first n bytes of src, the string placed in
5
       dest will not be null-terminated.
6
   ...
Gast #6634731
Lesenswert?

Habs schon, danke !!!!
Fehlersicher ist das sowieso nicht, sobald der String nicht so ist wie 
erwartet fliegt das alles aus der Kurve :-( Wobei alle static Vars 0 
sind beim Start des Systems, allerdings nicht wenn der Speicher sich vom 
Stack geklaut wird, dann steht da Müll drin.
1
char foo[4] = {0,0,0,0};
2
char *p = nmea_fields[1];
3

4
gps_info.utc_hour = atoi(strncpy(foo,p,2));
5
p +=2;
6
gps_info.min = atoi(strncpy(foo,p,2));
7
p +=2;
8
gps_info.sec = atoi(strncpy(foo,p,2));
Gast #6634915
Lesenswert?

Welche Regeln sollte man beachten?

1. ISR wird 'priviligiert' ausgeführt.
2. Eine höhere Priorität unterbricht eine niedrigere.
3. Die ISR sollte enden, bevor sie wieder angesprungen wird (werden 
sollte).
(Firma: matzetronics) #6634923
Lesenswert?

Und immer erstmal das IT Flag löschen, wenns gesetzt ist, egal, was 
später passiert.
Das ist besonders bei externen (GPIO) Interrupts wichtig.
Beim Initialisieren der IRQ Quelle die Pendings löschen.
Delays, die auf sys_tick basieren, hängen in ISRs, weil sys_tick meist 
eine niedrige Priorität hat - ist eigentlich logisch und macht man 
sowieso nicht, kann aber im Fehlerfall Kopfzerbrechen bereiten.
Gast #6634967
Lesenswert?

Matthias S. schrieb:
> Delays, die auf sys_tick basieren, hängen in ISRs, weil sys_tick meist
> eine niedrige Priorität hat - ist eigentlich logisch und macht man
> sowieso nicht, kann aber im Fehlerfall Kopfzerbrechen bereiten.

Naja, wer einen Delay in einer ISR verwendet gehört eigentlich auch an 
den Ei... aufgehängt :-)
(Firma: matzetronics) #6634977
Lesenswert?

Christian J. schrieb:
> Naja, wer einen Delay in einer ISR verwendet gehört eigentlich auch an
> den Ei... aufgehängt :-)

Es sind Fälle vorstellbar, wo mans trotzdem braucht. Ich hatte z.B. mal 
prellende Hallsensoren in einem BLDC, musste aber unbedingt den Sensor 
auswerten. Da konnte es schon mal passieren, das man ein paar µs warten 
muss.
Aber ich schrieb ja auch:
Matthias S. schrieb:
> und macht man
> sowieso nicht

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren