Forum: Mikrocontroller und Digitale Elektronik STM32 UART Interrupt funktioniert nur "teilweise"


von har (Gast)


Lesenswert?

Hallo liebes Forum,

Zu allererst: ich verwende einen STM32F3.
Damit ich kein Polling bei der UART machen muss, möchte ich dass beim 
Ankommen der Daten ein Interrupt ausgelöst wird.
Wenn ich im Debug-Modus bin und ich schicke das erste mal einen char, 
dann wird kein Interrupt ausgelöst. Erst beim wiederholten Senden wird 
ein Interrupt ausgelöst. Und nach mehrmaligem weiteren Senden ist das 
quasi eine Lotterie: manchmal wird ein Interrupt ausgelöst, und dann 
wieder nicht.
Könnt ihr mir helfen? Habe ich in der Konfiguration etwas vergessen?
Vielen Dank im Voraus!
1
#include <stm32f30x.h>
2
#include <stm32f30x_gpio.h>
3
#include <stm32f30x_rcc.h>
4
#include <stm32f30x_usart.h>
5
#include <stm32f30x_misc.h>     
6
7
8
9
//global variables
10
volatile unsigned char uart_receive;    // for receiving one char
11
12
13
// prototypes
14
unsigned char USART_ReadByteSync(USART_TypeDef *USARTx);
15
void USART2_IRQHandler(void);
16
void InitializeUSART();
17
int _write (char *pBuffer, int size);
18
19
20
void InitializeUSART()
21
{
22
    RCC_USARTCLKConfig(RCC_USART2CLK_SYSCLK);
23
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
24
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);  //Enable peripheral clock for USART2
25
26
27
    GPIO_InitTypeDef gpioConfig;
28
29
    //PB3 = USART2.TX => Alternative Function Output
30
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_7);  //Alternativ Funktionalität USART2 für Pin 3 an GPIOB aktivieren
31
    gpioConfig.GPIO_Mode = GPIO_Mode_AF;            // Alternate function mode  page: 237
32
    gpioConfig.GPIO_OType = GPIO_OType_PP;          // Output Push-Pull         page: 237
33
    gpioConfig.GPIO_Speed = GPIO_Speed_10MHz;       // Medium speed             page: 238
34
    gpioConfig.GPIO_PuPd = GPIO_PuPd_NOPULL;        // No pull-up, pull down    page: 239
35
    gpioConfig.GPIO_Pin = GPIO_Pin_3;
36
    GPIO_Init(GPIOB, &gpioConfig);                  // pin initialization
37
38
39
    //PB4 = USART2.RX => Alternative Function Output
40
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_7);
41
    gpioConfig.GPIO_Mode = GPIO_Mode_AF;
42
    gpioConfig.GPIO_OType = GPIO_OType_PP;
43
    gpioConfig.GPIO_Speed = GPIO_Speed_10MHz;
44
    gpioConfig.GPIO_PuPd = GPIO_PuPd_NOPULL;
45
    gpioConfig.GPIO_Pin = GPIO_Pin_4;
46
    GPIO_Init(GPIOB, &gpioConfig);
47
48
49
    USART_InitTypeDef usartConfig;
50
51
    usartConfig.USART_BaudRate = 9600;
52
    usartConfig.USART_WordLength = USART_WordLength_8b;         //USART2->CR1 |= 0x0000;
53
    usartConfig.USART_StopBits = USART_StopBits_1;
54
    usartConfig.USART_Parity = USART_Parity_No;
55
    usartConfig.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
56
    usartConfig.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
57
    USART_Init(USART2, &usartConfig);
58
59
60
61
    // ######## configure NVIC for Interrupt when data received: ###########
62
    NVIC_InitTypeDef NVIC_InitStructure;
63
64
    NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;               // select NVIC channel
65
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;       // value between 0 and 15; A lower priority value indicates a higher priority
66
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
67
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
68
    NVIC_Init(&NVIC_InitStructure);
69
    USART_ClearITPendingBit(USART2, USART_IT_RXNE);                 // Clear interrupt flag
70
    USART_ClearITPendingBit(USART2, USART_IT_TXE);
71
72
    USART_ITConfig(USART2, USART_IT_TXE, DISABLE);                  // disable Transmit Data Register empty interrupt
73
    USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);                  // Enable the USART RX Interrupt
74
    //NVIC_EnableIRQ(USART2_IRQn);                                    // Enable USART2 global interrupt
75
76
77
    USART_Cmd(USART2, ENABLE);      // UART2 enable             //USARTx->CR1 |= USART_CR1_UE;  //Enable the selected USART by setting the UE bit in the CR1 register */    page: 937
78
}
79
80
81
82
void USART2_IRQHandler(void)
83
{
84
    //Interrupt handler implementation
85
    
86
    if (USART_GetITStatus(USART2, USART_IT_RXNE))   //checking if interrupt
87
        uart_receive = USART_ReadByteSync(USART2);
88
89
    USART_ClearITPendingBit(USART2, USART_IT_RXNE); //Clear interrupt flag
90
}
91
92
93
94
unsigned char USART_ReadByteSync(USART_TypeDef *USARTx)
95
{
96
    while (USART_GetFlagStatus(USARTx, USART_FLAG_RXNE) == RESET)       // RXNE is set when content of the shift register is transferred to the RDR
97
    {
98
99
    }
100
101
    return (unsigned char)USART_ReceiveData(USARTx);
102
}
103
104
105
106
int main ()
107
{
108
109
    SystemInit();
110
    InitializeUSART();
111
112
    for (;;)
113
    {
114
    while(1)
115
        {
116
             //for sending 1 char
117
            while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);    // we can only write in the TDR register when TXE=1 (because when TXE ==1 -> data is transferred to the shift register)     page: 898 - 899
118
            {
119
                USART_SendData(USART2, 0x55);       //USARTx->TDR = (Data & (uint16_t)0x01FF);      //Transmit data by writing it in the TDR
120
            }
121
           
122
123
        }
124
    }
125
}

von ui (Gast)


Lesenswert?

Schonmal an der Priorität des Interrupts geschraubt?

Reindebuggen in die ISR funktioniert? Also die wird angesprungen?

Ansonsten schaut der Code auf den schnellen Blick gut aus.

von ui (Gast)


Lesenswert?

Ach ja die Baudrate schaut komisch aus.
Aus dem Code zum USART von STM:
1
  uint32_t USART_BaudRate;            /*!< This member configures the USART communication baud rate.
2
                                           The baud rate is computed using the following formula:
3
                                            - IntegerDivider = ((PCLKx) / (8 * (OVR8+1) * (USART_InitStruct->USART_BaudRate)))
4
                                            - FractionalDivider = ((IntegerDivider - ((u32) IntegerDivider)) * 8 * (OVR8+1)) + 0.5 
5
                                           Where OVR8 is the "oversampling by 8 mode" configuration bit in the CR1 register. */

der Wert 9600 entspricht NICHT 9600 Baud.

von har (Gast)


Lesenswert?

ui schrieb:
> Schonmal an der Priorität des Interrupts geschraubt?

Naja, es ist bereits die höchste Priorität vergeben!

ui schrieb:
> Reindebuggen in die ISR funktioniert? Also die wird angesprungen?

Ja hab ich, Aber wie gesagt: Sie wird nur hin und wieder angesprungen!

von W.S. (Gast)


Lesenswert?

har schrieb:
> Könnt ihr mir helfen?

Ja.

Aber vor dem Helfen muss bei deiner Quelle erst noch eine heftigste 
Schimpfkanonade stattfinden. Hast du denn überhaupt nicht mal 
nachgedacht über einen sinnvollen Treiber für deinen USART2 ? Wo bleibt 
die Pufferung? Wo bleibt der entgegengesetzte Datenstrom? Wo bleibt die 
sinnvolle Kapselung des ganzen inneren Schmonzes des Treibers?

Abgesehen davon graust mich dein Quellcode. Du kannst natürlich immerzu 
so weitermachen und dich in die ST-Lib hineinwühlen bis du nicht mehr 
weiterkommst (was anscheinend gerade jetzt erreicht ist) ODER du 
schmeißt den ganzen Haufen aufgedunsenen Zeuges weg und denkst dir dann 
ein ordentliches Konzept aus.

Damit du mal nen Eindruck eines Interrupthandlers bekommst, zitiere ich 
mal aus einer meiner Quellen:
1
/* Pufferlängen: immer eine 2er Potenz */
2
#define OBLEN  32
3
#define IBLEN  8
4
5
#define TUBUF  struct T_UARTBUF
6
struct T_UARTBUF
7
{ char OutBuf[OBLEN];
8
  char InBuf[IBLEN];
9
  volatile int  InWP;
10
  volatile int  InRP;
11
  volatile int  OutWP;
12
  volatile int  OutRP;
13
};
14
15
/* die Puffer für alle 5 USART/UART */
16
...
17
TUBUF U2Buf;
18
...
19
20
21
__irq void USART2_IRQHandler (void)
22
{ char c;
23
  int  i, j;
24
25
  if (USART2_ISR & ((1<<5)|(1<<3)))      // RX: Zeichen empfangen
26
  { c = USART2_RDR;
27
    i = U2Buf.InWP;
28
    j = (i+1) & (IBLEN-1);
29
    if (j!=U2Buf.InRP)
30
    { U2Buf.InBuf[i] = c;
31
      U2Buf.InWP = j;
32
    }
33
  }
34
35
  if  (USART2_ISR & (1<<7))              // TX: Sendepuffer leer?
36
  { i = U2Buf.OutRP;
37
    if (i!=U2Buf.OutWP)                  // ob es was zu senden gibt
38
    { USART2_TDR  = U2Buf.OutBuf[i];
39
      U2Buf.OutRP = (i+1) & (OBLEN-1);
40
    }
41
    else
42
    { USART2_CR1 &= ~(3<<6);             // nö, TXEIE und TCIE ausschalten
43
    }
44
  }
45
}

Hier werden Input und Output gepuffert und sämtlicher 
Schnittstellenverkehr erfolgt per Interrupt. Der USART ist ein eher 
langsames Interface, also spielt die Priorität keine große Rolle.
Ach ja, beim Senden, also wenn der Treiber was in den Sendepuffer 
schreibt, wird TXEIE und TCIE wieder eingeschaltet.

So. lies und versuche es zu verstehen. Den Rest des Treibers solltest du 
jetzt selber schreiben können.

W.S.

von Oswin (Gast)


Lesenswert?

W.S. schrieb:
> (USART2_ISR & ((1<<5)|(1<<3)))
So ein Murks!
Code sollte sich selbst eklären, nicht durch Kommentare die vor dem 
nächsten Strg-S schon veraltet sind.
Im Manual sind die Bits benamst. Das sollte sich im Code auch 
wiederfinden.

Warum häst Du Dich eigentlich immer für den Schlausten, der die Weisheit 
bzw. die Programmiererleuchtung mit Löffeln gefressen hat?!?

von unkti (Gast)


Lesenswert?

>Warum häst Du Dich eigentlich immer für den Schlausten, der die Weisheit
>bzw. die Programmiererleuchtung mit Löffeln gefressen hat?!?

Na Du fragst dumm: Weil er die Lernbetty erfunden hat!

von Adib (Gast)


Lesenswert?

Hallo har,

Nimm doch den aktuellen cubeMx.
Der generiert dir Code, der zumindest beim Senden funktioniert.
Empfang mache ich per Dma.

Adib.

von W.S. (Gast)


Lesenswert?

Oswin schrieb:
> Code sollte sich selbst eklären, nicht durch Kommentare die vor dem
> nächsten Strg-S schon veraltet sind.

Kannst du steckenlassen. Ich mache das aus gutem Grund und ich füge auch 
aus gutem Grund einen Kommentar dran.

Du bist nicht der TO, sondern nur jemand, der eben mal daherkommt um 
hier seinen Spruch abzulassen und was besserzuwissen, ohne auch nur die 
Spur eines echten eigenen Beitrages geliefert zu haben.

Also poste mal etwas wirklich Funktionables, dann können all diejenigen, 
die Hilfe suchen, selber entscheiden, ob sie deins nehmen oder nicht.

Klar soweit?

W.S.

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.