Forum: Mikrocontroller und Digitale Elektronik STM32 Interrupts - Welche Regeln sollte man beachten?


von Christian J. (Gast)


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
}

von Stefan F. (Gast)


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.

von Christian J. (Gast)


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];

von John Doe (Gast)


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...

von foobar (Gast)


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.

von Christian J. (Gast)


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.

von foobar (Gast)


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
   ...

von Christian J. (Gast)


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));

von MaWin (Gast)


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).

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


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.

: Bearbeitet durch User
von Christian J. (Gast)


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 :-)

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


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

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.