Forum: Mikrocontroller und Digitale Elektronik [STM32] USART + Interrupt


von STM32 Beginner (Gast)


Lesenswert?

Hi,

nachdem ich mittels Polling schon einzelne Zeichen empfangen kann, 
möchte ich einen Schritt weiter gehen und meine Empfangsroutine 
interruptfähig machen. Aus eigenem Bestreben möchte ich gerne auf den 
Einsatz der ST-Firmware-Library verzichten - man hat irgendwie überhaupt 
keine Ahnung auf welche Register die einzelnen Funktionen zugreifen...
Ich hab versucht möglichst viele Informationen aus dem Reference Manual, 
den Beispielprogrammen von ST und den Threads im Internet zu quetschen. 
Leider hab ich bis jetzt überhaupt keine Erfahrungen mit Interrupts auf 
dem Cortex-M3.

Meine bisherigen Überlegungen sehen so aus:

Wenn ich das Reference Manual richtig verstanden habe, dann muss ich das 
USART_CR1_RXNEIE Flag setzen? Im Interrupt Handler wird dann geprüft ob 
ein Zeichen im Empfangsbuffer liegt:
1
void USART1_IRQHandler (void)
2
{
3
 if(USART1->SR & USART_SR_RXNE)
4
 {
5
   zeichen = USART1->DR;
6
   [....]
7
 }
8
[....]
9
}

In einem Beispielprogramm von ST hab ich dann noch folgendes gefunden:
1
 
2
  /* Enable the USARTy Interrupt */
3
  NVIC_InitStructure.NVIC_IRQChannel = USARTy_IRQn;
4
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
5
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
6
  NVIC_Init(&NVIC_InitStructure);

Nur was macht der Codeauszug? Ich hab im Reference Manual nach dem 
Begriff "NVIC" gesucht, aber da finde ich gerade mal 3 Seiten, die mich 
als absoluten Anfänger, jetzt nicht soviel weiter bringen..

Irgendwie steh ich grad voll an. Ich finde im Internet zwar ziemlich 
viele Infos, aber überall nur Codeauszüge, die mit der ST-Firmware_Lib 
einhergehen...

Ich hoffe, mir kann jemand die Funktionsweise von Interrupts auf einem 
Cortex M3 näher bringen - im Moment bin ich einfach nur total verwirrt!

Danke!

von (prx) A. K. (prx)


Lesenswert?

Der NVIV gehört zu den Kernkomponenten des Cortex-M3 und deren Doku 
findet man daher nicht in der STM32 Reference sondern in der Cortex-M3 
Doku von ARM und dem Cortex-M3 Programming Manual zum STM32.

Für das Verständnis nützlicher: 
http://www.amazon.de/Definitive-Guide-Cortex-M3-Embedded-Technology/dp/0750685344
und der Insiders Guide
http://www.st.com/mcdfiles/1221142709.pdf

von STM32 Beginner (Gast)


Lesenswert?

Den Insiders Guide von Hitex kenn ich. Das Buch klingt echt interessant 
- mal schauen vielleicht leg ich mir das zu. Ich werd mal die ganzen 
Manuals studieren und mich dann nochmal melden.
Danke für die Tipps!

von STM32 Beginner (Gast)


Lesenswert?

Hallo,

hab mir jetzt mal die ganzen Manuals hergenommen und ein bisschen 
quergelesen. Ich hoffe ich hab das jetzt richtig verstanden: Um einen 
Interrupt zu aktivieren gehe ich her und setze im "Interrupt-Set-Enable" 
Register an geeigneter Stelle einen 1er. Die Bitposition entspricht 
dabei der Position in der Vektortabelle im Startup File?

von Arne (Gast)


Lesenswert?

Vorsicht.
Das ist im NVIC m.E. ein wenig unpraktisch gelöst.
Die ersten 16 Vektoren (0 - 15) sind sog. Systemvektoren, die Du in 
jedem Cortex-M3 (ST, TI, NXP,...) hast. Diese werden im Register "System 
Handler Control and State Register" (0xE000ED24) ein-/ausgeknipst 
(sofern das möglich ist - beim NMI natürlich nicht). Siehe ARM Doku DDI 
0337G Seite 8-29 ff.
Alle weiteren Vektoren (16 - n) schaltest Du - wie Du bemerkt hast - 
über IRQ n to m Set Enable Register ein/aus. Selbes Dokument, Seite 8-13 
ff.

Ist ein wenig verwirrend, hatte es am Anfang auch falsch :-/

von STM32 Beginner (Gast)


Lesenswert?

Gut zu wissen, dass die Systemvektoren in einem anderen Register 
konfiguriert werden. Da hätte ich wieder ewig gesucht.
Aber um den USART1 Interrupt zu enablen, brauch ich ja erstmal die 
Systemvektoren nicht anzurühren, oder?
Oben hab ich geschrieben, dass die Bitposition im SETENA (Set-Enable) 
Register der Position in der Vektortabelle im Startup File entspricht.
Mein Startup File sieht so aus:
1
; External Interrupts
2
                DCD     WWDG_IRQHandler           ; Window Watchdog
3
                DCD     PVD_IRQHandler            ; PVD through EXTI Line detect
4
                DCD     TAMPER_IRQHandler         ; Tamper
5
                DCD     RTC_IRQHandler            ; RTC
6
                DCD     FLASH_IRQHandler          ; Flash
7
                DCD     RCC_IRQHandler            ; RCC
8
                DCD     EXTI0_IRQHandler          ; EXTI Line 0
9
                DCD     EXTI1_IRQHandler          ; EXTI Line 1
10
                DCD     EXTI2_IRQHandler          ; EXTI Line 2
11
                DCD     EXTI3_IRQHandler          ; EXTI Line 3
12
                DCD     EXTI4_IRQHandler          ; EXTI Line 4
13
                DCD     DMAChannel1_IRQHandler    ; DMA Channel 1
14
                DCD     DMAChannel2_IRQHandler    ; DMA Channel 2
15
                DCD     DMAChannel3_IRQHandler    ; DMA Channel 3
16
                DCD     DMAChannel4_IRQHandler    ; DMA Channel 4
17
                DCD     DMAChannel5_IRQHandler    ; DMA Channel 5
18
                DCD     DMAChannel6_IRQHandler    ; DMA Channel 6
19
                DCD     DMAChannel7_IRQHandler    ; DMA Channel 7
20
                DCD     ADC_IRQHandler            ; ADC
21
                DCD     USB_HP_CAN_TX_IRQHandler  ; USB High Priority or CAN TX
22
                DCD     USB_LP_CAN_RX0_IRQHandler ; USB Low  Priority or CAN RX0
23
                DCD     CAN_RX1_IRQHandler        ; CAN RX1
24
                DCD     CAN_SCE_IRQHandler        ; CAN SCE
25
                DCD     EXTI9_5_IRQHandler        ; EXTI Line 9..5
26
                DCD     TIM1_BRK_IRQHandler       ; TIM1 Break
27
                DCD     TIM1_UP_IRQHandler        ; TIM1 Update
28
                DCD     TIM1_TRG_COM_IRQHandler   ; TIM1 Trigger and Commutation
29
                DCD     TIM1_CC_IRQHandler        ; TIM1 Capture Compare
30
                DCD     TIM2_IRQHandler           ; TIM2
31
                DCD     TIM3_IRQHandler           ; TIM3
32
                DCD     TIM4_IRQHandler           ; TIM4
33
                DCD     I2C1_EV_IRQHandler        ; I2C1 Event
34
                DCD     I2C1_ER_IRQHandler        ; I2C1 Error
35
                DCD     I2C2_EV_IRQHandler        ; I2C2 Event
36
                DCD     I2C2_ER_IRQHandler        ; I2C2 Error
37
                DCD     SPI1_IRQHandler           ; SPI1
38
                DCD     SPI2_IRQHandler           ; SPI2
39
                DCD     USART1_IRQHandler         ; USART1
40
                DCD     USART2_IRQHandler         ; USART2
41
                DCD     USART3_IRQHandler         ; USART3
42
                DCD     EXTI15_10_IRQHandler      ; EXTI Line 15..10
43
                DCD     RTCAlarm_IRQHandler       ; RTC Alarm through EXTI Line
44
                DCD     USBWakeUp_IRQHandler      ; USB Wakeup from suspend
Der Eintrag, der für mich interessant ist (USART1 Interrupt) steht an 
37.Stelle. Da das SETENA Register nur 32bit breit ist, hab ich da ja 
dann ein Problem? Kann ich jetzt einfach hergehen und die Zeile
1
DCD     USART1_IRQHandler         ; USART1
 an die oberste Stelle kopieren, sodass dies im SETENA Register dem 
ersten Bit entspricht?

Oder versteh ich da gerade etwas total falsch?

von Arne (Gast)


Lesenswert?

Meine Tabelle für IAR sieht so aus:
1
const tISR_Item __vector_table[] = {
2
  { .mPointer = __sfe( "CSTACK" ) },  /* SP    */
3
  __iar_program_start,        /* Index 1  */
4
  NMIException,            /* Index 2  */
5
  HardFaultException,          /* Index 3  */
6
  MemManagerException,        /* Index 4  */
7
  BusFaultException,          /* Index 5  */
8
  UsageFaultException,        /* Index 6  */
9
  NULL,                /* Reserved */
10
  NULL,                /* Reserved */
11
  NULL,                /* Reserved */
12
  NULL,                /* Reserved */
13
  SystemDriverSVCallISR,        /* Index 11  */
14
  UndefinedInterruptISR,        /* Index 12  */
15
  NULL,                /* Reserved */
16
  UndefinedInterruptISR,        /* Index 14  */
17
  UndefinedInterruptISR,        /* Index 15  */
18
  UndefinedInterruptISR,        /* Index 16  */
19
  UndefinedInterruptISR,        /* Index 17  */
20
  UndefinedInterruptISR,        /* Index 18  */
21
  UndefinedInterruptISR,        /* Index 19  */
22
  UndefinedInterruptISR,        /* Index 20  */
23
  UndefinedInterruptISR,        /* Index 21  */
24
  UndefinedInterruptISR,        /* Index 22  */
25
  UndefinedInterruptISR,        /* Index 23  */
26
  UndefinedInterruptISR,        /* Index 24  */
27
  UndefinedInterruptISR,        /* Index 25  */
28
  UndefinedInterruptISR,        /* Index 26  */
29
  UndefinedInterruptISR,        /* Index 27  */
30
  UndefinedInterruptISR,        /* Index 28  */
31
  UndefinedInterruptISR,        /* Index 29  */
32
  UndefinedInterruptISR,        /* Index 30  */
33
  UndefinedInterruptISR,        /* Index 31  */
34
  UndefinedInterruptISR,        /* Index 32  */
35
  UndefinedInterruptISR,        /* Index 33  */
36
  UndefinedInterruptISR,        /* Index 34  */
37
  UndefinedInterruptISR,        /* Index 35  */
38
  UndefinedInterruptISR,        /* Index 36  */
39
  UndefinedInterruptISR,        /* Index 37  */
40
  UndefinedInterruptISR,        /* Index 38  */
41
  UndefinedInterruptISR,        /* Index 39  */
42
  UndefinedInterruptISR,        /* Index 40  */
43
  UndefinedInterruptISR,        /* Index 41  */
44
  UndefinedInterruptISR,        /* Index 42  */
45
  UndefinedInterruptISR,        /* Index 43  */
46
  UndefinedInterruptISR,        /* Index 44  */
47
  UndefinedInterruptISR,        /* Index 45  */
48
  UndefinedInterruptISR,        /* Index 46  */
49
  UndefinedInterruptISR,        /* Index 47  */
50
  UndefinedInterruptISR,        /* Index 48  */
51
  UndefinedInterruptISR,        /* Index 49  */
52
  UndefinedInterruptISR,        /* Index 50  */
53
  UndefinedInterruptISR,        /* Index 51  */
54
  UndefinedInterruptISR,        /* Index 52  */
55
  UndefinedInterruptISR,        /* Index 53  */  /* UART 1  */
56
  UndefinedInterruptISR,        /* Index 54  */
57
  UndefinedInterruptISR,        /* Index 55  */
58
  UndefinedInterruptISR,        /* Index 56  */
59
  UndefinedInterruptISR,        /* Index 57  */
60
  UndefinedInterruptISR         /* Index 58  */
61
};
Da sind nur die Systemvektoren hart eingetragen. Zur Laufzeit kopiere 
ich die Tabelle ins SRAM und trage dort dann die Vektoren ein, die ich 
je nach Applikation benötige.
> Oder versteh ich da gerade etwas total falsch?
Teilweise... ;)
UART 1 liegt an Index 53. Da 'pokst' Du dann die Adresse Deiner ISR 
rein, setzt im Interrupt Set-Enable Register dann eine 1 an der 
richtigen Stelle (müsste in 0xE000E104 das Bit Nr. 4 sein, d.h. 'das 
fünfte von rechts' ;) -> Index 53 - 16 (Systemvektoren) = 37).
Es gibt nicht nur EIN Interrupt Set-Enable Register! Das ERSTE liegt bei 
0xE000E100 für die ersten 32 Applikationsvektoren (oben Index 16 bis 
47), bei 0xE000E104 liegt das zweite für die zweiten 32 
Applikationsvektoren (oben Index 48 bis 79). Klaro?
Schau ins Usermanual von ST (RM0008) Kap 9.1.2 Table 52. Die Angaben in 
der Spalte 'Position' sind für die Interrupt-Set-Enable Register die 
wichtigen.

BTW: wieso enthält Deine Vektortabelle keine Systemvektoren (0..15)? 
oder sind die weiter vorne im Code?

von STM32 Beginner (Gast)


Lesenswert?

Die Systemvektoren sind bei mir weiter vorne im Code:
1
__Vectors       DCD     __initial_sp              ; Top of Stack
2
                DCD     Reset_Handler             ; Reset Handler
3
                DCD     NMI_Handler               ; NMI Handler
4
                DCD     HardFault_Handler         ; Hard Fault Handler
5
                DCD     MemManage_Handler         ; MPU Fault Handler
6
                DCD     BusFault_Handler          ; Bus Fault Handler
7
                DCD     UsageFault_Handler        ; Usage Fault Handler
8
                DCD     0                         ; Reserved
9
                DCD     0                         ; Reserved
10
                DCD     0                         ; Reserved
11
                DCD     0                         ; Reserved
12
                DCD     SVC_Handler               ; SVCall Handler
13
                DCD     DebugMon_Handler          ; Debug Monitor Handler
14
                DCD     0                         ; Reserved
15
                DCD     PendSV_Handler            ; PendSV Handler
16
                DCD     SysTick_Handler           ; SysTick Handler

Wenn ich dich jetzt richtig verstanden habe, dann schalte ich den 
gewünschten USART1 Interrupt mit der folgenden Zeile ein:

NVIC->ISER[1] = (1<<6);

Stimmt das?

von Plan (Gast)


Lesenswert?

37 - 32 = 5.
vvvvvvvvvvvvvvvvvvvvvvv
NVIC->ISER[1] = (1<<4);
^^^^^^^^^^^^^^^^^^^^^^^

NVIC->ISER[1] |= 0x00000010;

Das oder | sollte rein, denn es könnten ja noch andere ISRs an sein.

von Arne (Gast)


Lesenswert?

@STM32 Beginner:
Vergiss nicht den UART im RCC einzuschalten (USART1EN in RCC_APB2ENR) 
und den IRQ im UART selbst anzuknipsen: RXNEIE in USART_CR1.

von STM32 Beginner (Gast)


Lesenswert?

Kleiner Nachtrag:

Ich hab mich oben verzählt, der USART1 Interrupt steht an der 38.Stelle.
Das heißt, um den USART1 Interrupt zu aktivieren ist folgende Zeile 
nötig:

NVIC->ISER[1] |= 0x00000020;

Der Interrupt funktioniert jetzt tadellos. Herzlichen Dank an alle, die 
mir den richtigen Weg aufgezeigt haben!

von Karl Heinz (Gast)


Lesenswert?

Hallo,

Ich würde gerne was Ähnliches machen und zwar Hab ich eine 
Hauptfunktion laufen und sobald ich von der USART2 ein Befehl erhalte 
oder Zeichen empfange, soll eine entsprechende Funktion (je nach 
empfangenen Zeichen) ausgeführt werden.

Kann mir da vielleicht jemand helfen?

von Gregor Rebel (Gast)


Lesenswert?

Die ToolChain_STM32 ist OpenSource und bietet u.a. ein 
interruptgesteuertes USART Interface. Zum VErständnis gibt es einige 
Beispielprogramme.

Zu finden ist die ToolChain hier:
http://thetoolchain.com

von tobiflea (Gast)


Lesenswert?

Hallo,

ich habe zu diesem Thema UART mit Interrupts am STM32 auch eine Frage.
Und zwar wird bei mir ein Interrupt ausgelöst, wenn ich den UE, TE, RE 
und die IRQ-Enable-Bits TCIE und RXNEIE setze. Und wenn später in der 
Software Daten senden will und in das DR-Register schreibe, kommt kein 
Interrupt mehr.

Hier mein Code:
1
void UART_Init(void)
2
{
3
  /* enable clock for UART */
4
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
5
6
  USART1->BRR = (24000000 / 115200);
7
  USART1->CR1 = (USART_CR1_UE | USART_CR1_TE | USART_CR1_RE | USART_CR1_TCIE | USART_CR1_RXNEIE);
8
9
  /* init ringbuffer */
10
  ringbuffer_init(&uart_tx_ringbuffer_s, uart_tx_data_ac, UART_TX_BUFFER_LENGTH);
11
12
  uart_status_ec = UART_STATE_IDLE;
13
14
  USART1->DR = 0xAA;
15
}
16
17
void UART_Transmit(uint8_t* data_pc, uint8_t length_c)
18
{
19
  /* fill TX ringbuffer */
20
  while (length_c > 0)
21
  {
22
    ringbuffer_write(&uart_tx_ringbuffer_s, *data_pc);
23
    data_pc++;
24
    length_c--;
25
  }
26
27
  /* start transmission */
28
  uart_status_ec = UART_STATE_TX_BUSY;
29
  USART1->DR = ringbuffer_read(&uart_tx_ringbuffer_s);
30
}
31
32
void USART1_IRQHandler(void)
33
{
34
  if ((USART1->SR & USART_SR_TC) != 0x00)
35
  {
36
    /* Transmission clompete interrupt */
37
    if (uart_tx_ringbuffer_s.content_c > 0)
38
    {
39
      USART1->DR = ringbuffer_read(&uart_tx_ringbuffer_s);
40
      uart_status_ec = UART_STATE_TX_BUSY;
41
    }
42
    else
43
    {
44
      uart_status_ec = UART_STATE_IDLE;
45
    }
46
  }
47
}

Der UART wird im NVIC natürlich mit NVIC->ISER[1] |= NVIC_ISER_SETENA_5; 
aktiviert.


Hat jemand eine Idee, woran das liegen könnte?

Gruß tobiflea

von tobfilea (Gast)


Lesenswert?

Ich habe gemerkt, dass mein Debugger (STM32VL-Discovery mit SWD) sich 
unterschiedlich verhält. Mal springt er nur einmal beim Aktivieren des 
UARTs (UE, TE, RE) direkt in die ISR. Nach einem Reset springt er nach 
UART-Initialisierung andauernd in die ISR. Das Verhalten ist völlig 
komisch.

Kann mir jemand einen Code der UART mit IRQ-Verwendung zur Durchsicht 
zur Verfügung stellen?

von tobiflea (Gast)


Lesenswert?

Wenn das SR-Register vor der Abfrage in der if-Bedingung explizit in 
eine Variable kopiert wird, funktioniert die ganze Sache. Wieso auch 
immer...

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.