Forum: Mikrocontroller und Digitale Elektronik STM32F446 - USART


von Wühlhase (Gast)


Lesenswert?

Guten Abend allerseits

Ich hab mal wieder eine neue Frage zum STM32. Ich initialisiere gerade 
die USART des Controllers mit folgender Funktion:
1
void initCOM(){
2
  //Initialize USART for comunication
3
  //Enable USART3 clock
4
  RCC -> APB1ENR |= RCC_APB1ENR_USART3EN;
5
  //Set baudrate 45MHz/390,625 => 115,2kBd
6
  COM -> BRR |= (0x186 << USART_BRR_DIV_Mantissa_Pos);
7
  COM -> BRR |= 0b1010;
8
9
  //Set oversampling to 8x-oversampling
10
  COM -> CR1 |= USART_CR1_OVER8;
11
  
12
  //Set wordlenght databits to 8
13
  COM -> CR1 &= ~USART_CR1_M;
14
15
  //Disable parity control
16
  COM -> CR1 &= ~USART_CR1_PCE;
17
18
  //Select parity mode
19
  //COM -> CR1 &= ~USART_CR1_PS;
20
21
  //Enable interrupt if TX is empty or RX is'nt empty
22
  COM -> CR1 |= USART_CR1_PEIE;
23
  COM -> CR1 |= USART_CR1_TXEIE;
> COM -> CR1 |= USART_CR1_RXNEIE;
1
  //Enable transmitter and reciver
2
  COM -> CR1 |= USART_CR1_TE;
3
  COM -> CR1 |= USART_CR1_RE;
4
5
  //Select stopbits => 1 stopbit
6
  COM -> CR2 &= ~USART_CR2_STOP_0;
7
  COM -> CR2 &= ~USART_CR2_STOP_1;
8
9
  //Enable CTS/RTS
10
  COM -> CR3 |= USART_CR3_CTSE;
11
  COM -> CR3 |= USART_CR3_RTSE;  
12
}

Es geht mir um die Zeile
COM -> CR1 |= USART_CR1_RXNEIE;

Ich möchte eigentlich einen Interrupt generieren wenn ein Byte 
fertiggesendet wurde (nichtblockierendes Senden). Aber sobald das 
Programm an dieser Zeile steht, springt es sofort in die 
IRQ-Handler-Funktion. Dabei ist die USART nichtmal aktiviert.

Wie macht ihr das...Interrupts nur bei Bedarg aktivieren? Das wäre zwar 
eine Möglichkeit, jedoch erscheint mir das häufige Herumfuhrwerken in 
den Config-Registern potentiell fehlerträchtig.

von Jim M. (turboj)


Lesenswert?

Wozu die ganzen Read-Modify-Write Zyklen? Ist Dir nicht klar was der |= 
tut?

Ich hätte den ganzen CR1 Krams in eine Schreibaktion gepackt, also
1
COM -> CR1 = USART_CR1_PEIE | USART_CR1_TXEIE | USART_CR1_TE
2
  | USART_CR1_RE;

Interrupt Bits würde ich immer erste setzen wenn die restliche Hardware 
komplett initialisiert ist.

Sehe grade: Du enablest den Receiver Interrupt vor dem Receiver selbst, 
das könnte u.U. dort knallen.

von Wühlhase (Gast)


Lesenswert?

Hallo Jim

Ja...ich hab mich erst in den letzten Wochen wieder mal mit C befasst, 
du hast recht, das ist einfacher. Danke.

Ich habe die Interrupt-Aktivierung mal komplett rausgenommen, ich werde 
vorerst einmal nur senden.

von Wühlhase (Gast)


Lesenswert?

Hm...jetzt läuft die Init zwar durch und meine Sende-Funktion wird auch 
augerufen, allerdings sendet das Ding nix. :(
1
void main(){
2
  //initClock();
3
  initClock();
4
  initNVIC();
5
  initPins();
6
  initTimer();
7
  initCOM();
8
9
  //Softwareinit
10
  uiInit();
11
12
  TIM6 -> CR1 |= TIM_CR1_CEN;
13
  COM -> CR1 |= USART_CR1_UE;
14
  //COM -> CR1 |= USART_CR1_PEIE;
15
16
  //Test Pin Init
17
  while(true){
18
19
    //Test for led state
20
    if(uiButtonWasTapped(BUTTON1)){
21
      uiSetLED(LED_THROTTON, LED_DARK);
22
    }
23
    if(uiButtonWasTapped(BUTTON2)){
24
      uiSetLED(LED_THROTTON, LED_BLINK1x);
25
    }
26
    if(uiButtonWasTapped(BUTTON3)){
27
      uiSetLED(LED_THROTTON, LED_BLINK2x);
28
    }
29
    if(uiButtonWasTapped(BUTTON4)){
30
      uiSetLED(LED_THROTTON, LED_BLINK4x);
31
    }
32
    if(uiButtonWasTapped(BUTTON5)){
33
      uiSetLED(LED_THROTTON, LED_SHINE);
34
    }
35
    comSendByte('U');
36
  }
37
}
1
void initCOM(){
2
  //Initialize USART for comunication
3
  //Enable USART3 clock
4
  RCC -> APB1ENR |= RCC_APB1ENR_USART3EN;
5
  //Set baudrate 45MHz/390,625 => 115,2kBd
6
  COM -> BRR |= (0x186 << USART_BRR_DIV_Mantissa_Pos);
7
  COM -> BRR |= 0b1010;
8
9
  //Set oversampling to 8x-oversampling
10
  COM -> CR1 |= USART_CR1_OVER8;
11
  
12
  //Set wordlenght databits to 8
13
  COM -> CR1 &= ~USART_CR1_M;
14
15
  //Disable parity control
16
  COM -> CR1 &= ~USART_CR1_PCE;
17
18
  //Select parity mode
19
  //COM -> CR1 &= ~USART_CR1_PS;
20
21
  //Enable interrupt if TX is empty or RX is'nt empty
22
  //COM -> CR1 |= USART_CR1_TXEIE | USART_CR1_RXNEIE;
23
24
  //Enable transmitter and reciver
25
  COM -> CR1 |= USART_CR1_TE | USART_CR1_RE;
26
27
  //Select stopbits => 1 stopbit
28
  COM -> CR2 |= ~USART_CR2_STOP_0 & ~USART_CR2_STOP_1;
29
30
  //Enable CTS/RTS
31
  COM -> CR3 |= USART_CR3_CTSE | USART_CR3_RTSE;  
32
}
1
bool comSendByte(char c){
2
  if(!(COM -> SR & USART_SR_TXE)){
3
    return false;
4
  }
5
  else{
6
    COM -> DR = (c & 0xFF);
7
    return true;
8
  }
9
}

Meine Funktion comSendByte() scheint zu funktionieren, die 
Fallunterscheidung ob TX leer ist oder nicht scheint korrekt zu sein.

Allerdings kann ich auf der anderen Seite nichts empfangen... :(

von Wühlhase (Gast)


Lesenswert?

Ich glaub ich hab das Problem...ich hab die Alternate Function vergessen 
zu konfigurieren.

Allerdings haut mir der Compiler ständig meinen Code um die Ohren, er 
meckert 'assignment to expression with array type'. Was soll die Meldung 
bedeuten? Was für ein Array?
1
//Init pins for usart connection
2
  //D8 => TX
3
  RCC -> AHB1ENR |= RCC_AHB1ENR_GPIODEN;
4
  COM_TX_PORT -> MODER |= GPIO_MODER_MODER8_1;
5
  COM_TX_PORT -> AFR |= GPIO_AFRH_AFRH7_2 | GPIO_AFRH_AFRH7_1 | GPIO_AFRH_AFRH7_0;
6
  //D9 => RX
7
  RCC -> AHB1ENR |= RCC_AHB1ENR_GPIODEN;
8
  COM_RX_PORT -> |= GPIO_MODER_MODER9_1;
9
  COM_RX_PORT -> AFR |= GPIO_AFRH_AFRH7_2 | GPIO_AFRH_AFRH7_1 | GPIO_AFRH_AFRH7_0;
10
  //D11 => CTS
11
  RCC -> AHB1ENR |= RCC_AHB1ENR_GPIODEN;
12
  COM_RTS_PORT -> MODER |= GPIO_MODER_MODER12_1;
13
  COM_RTS_PORT -> AFR |= GPIO_AFRH_AFRH7_2 | GPIO_AFRH_AFRH7_1 | GPIO_AFRH_AFRH7_0;
14
  //D12 => RTS
15
  RCC -> AHB1ENR |= RCC_AHB1ENR_GPIOEEN;
16
  COM_RTS_PORT -> MODER |= GPIO_MODER_MODER10_1;
17
  COM_RTS_PORT -> AFR |= GPIO_AFRH_AFRH7_2 | GPIO_AFRH_AFRH7_1 | GPIO_AFRH_AFRH7_0;

von Wühlhase (Gast)


Lesenswert?

Quatsch...muß natürlich so sein:

[c]
//Init pins for usart connection
  //D8 => TX
  RCC -> AHB1ENR |= RCC_AHB1ENR_GPIODEN;
  COM_TX_PORT -> MODER |= GPIO_MODER_MODER8_1;
  COM_TX_PORT -> AFR[1] |= GPIO_AFRH_AFSEL8_2 | GPIO_AFRH_AFSEL8_1 | 
GPIO_AFRH_AFSEL8_0;
  //D9 => RX
  RCC -> AHB1ENR |= RCC_AHB1ENR_GPIODEN;
  COM_RX_PORT -> MODER |= GPIO_MODER_MODER9_1;
  COM_RX_PORT -> AFR[1] |= GPIO_AFRH_AFSEL9_2 | GPIO_AFRH_AFSEL9_1 | 
GPIO_AFRH_AFSEL9_0;
  //D11 => CTS
  RCC -> AHB1ENR |= RCC_AHB1ENR_GPIODEN;
  COM_RTS_PORT -> MODER |= GPIO_MODER_MODER12_1;
  COM_RTS_PORT -> AFR[1] |= GPIO_AFRH_AFSEL11_2 | GPIO_AFRH_AFSEL11_1 | 
GPIO_AFRH_AFSEL11_0;
  //D12 => RTS
  RCC -> AHB1ENR |= RCC_AHB1ENR_GPIOEEN;
  COM_RTS_PORT -> MODER |= GPIO_MODER_MODER10_1;
  COM_RTS_PORT -> AFR[1] |= GPIO_AFRH_AFSEL12_2 | GPIO_AFRH_AFSEL10_1 | 
GPIO_AFRH_AFSEL12_0;
[/]

von Wühlhase (Gast)


Lesenswert?

Mit dem Code oben gehts, die USART sendet jetzt fröhlich vor sich hin. 
War der montagmorgendlichen Unzeit geschuldet...

Allerdings sendet sie stets 0x00, ich schreib allerdings sowas wie 0x5A 
in das DR. Hat jemand eine Idee was da noch nicht stimmt?

von Wühlhase (Gast)


Lesenswert?

Ich habs jetzt...die USART sendet, und das sogar fehlerfrei.

Ich hätte allerdings noch eine Frage zu dem was im Datenblatt steht-es 
wäre klasse wenn das jemand wüßte.

Die Baudrate wird ja mit fck/(Oversampling*USART_DIV) berechnet. fck 
soll die Taktfrequenz sein, mit der die Peripherie auf APB1 angesteuert 
wird. Diese hab ich auf 45MHz eingestellt.

Nun ist es aber so, daß ich die Baudrate durch probieren herausfinden 
mußte. Ich hab einen Baudrate-Teiler gefunden (ging erstaunlich 
schnell), mit dem ich sehr zuverlässig senden konnte. Ich hab in einer 
Endlosschleife ein 'U' (0b01010101) gesendet, damit mehrere MB große 
Dateien generiert. Dann diese mit einem Text-Editor geöffnet und alle Us 
entfernt-Fehler wären übrig geblieben, aber es gab keine.

Wenn ich nun aber mit dem von mir gefundenen Teiler auf fck 
zurückrechne, kommt etwas von knapp 19MHz raus-nix mit 45MHz, wie das 
eigentlich gedacht war.

Wenn ich nun aber die Taktkonfiguration sein lasse (alles mit 16MHz), 
klappen die Baudrateteiler für 16MHz nicht, die USART sendet doppelt so 
schnell wie sie soll.

Ist da noch irgendwo ein Taktteiler den ich übersehen habe? Wenn ja, wo? 
Weder im Kapitel zur RCC noch zur USART hab ich da was gefunden...

von Thomas E. (picalic)


Lesenswert?

Wühlhase schrieb:
> Ist da noch irgendwo ein Taktteiler den ich übersehen habe? Wenn ja, wo?
> Weder im Kapitel zur RCC noch zur USART hab ich da was gefunden...

Servus,

hab jetzt nicht speziell im DB zu Deinem Controller nachgeschaut, von 
anderen ST32 kenne ich es aber so, daß die USARTs von verschiedenen 
PCLKs versorgt werden. Diese haben nicht notwendigerweise die 
CPU-Taktfrequenz, je nach eingestellten Teilern im Clock-Tree. Dort 
musst Du mal schauen, an welchem PCLK Dein USART hängt und was da an 
Teilerfaktoren im Spiel ist.
Aktuell bin ich gerade an einem Projekt mit STM32F303, da kann man in 
einem RCC-Register aber auch noch die Clock-Source für die USARTs 
umschalten und so z.B. HSI statt PCLK einstellen.

Mit Oversampling/16 ist's übrigens einfacher, da ist BRR einfach 
CLOCK/BAUDRATE, ohne umständliches 'rumgeschiebe...

von W.S. (Gast)


Angehängte Dateien:

Lesenswert?

Wühlhase schrieb:
> Mit dem Code oben gehts, die USART sendet jetzt fröhlich vor sich hin.
...
> Allerdings sendet sie stets 0x00, ich schreib allerdings sowas wie 0x5A
> in das DR. Hat jemand eine Idee was da noch nicht stimmt?

Bei dir ist der Name Programm, gelle?

Also, mir scheint es bei deinem UART-Wagnis am Konzept zu fehlen. Was 
willst du eigentlich draus machen? Normalerweise richtet man sich einen 
Ringpuffer ein, den man völlig asynchron zur Schnittstelle befüllen kann 
und der zwischenzeitlich per Interrupt geleert wird, bis er dann 
irgendwann (wenn vom Programm kein Nachschub kommt) leer ist. Dann 
sollte der TX mit Senden aufhören und erst dann wieder beginnen, wenn 
irgendwas in den Ringpuffer hineinkommt.

Guck dir mal als Lehrbeispiel die angehängte Datei näher an. Ist zwar 
nicht für exaktemang deinen Chip, aber ich habe eben diesen Code nur mit 
dezenten Änderungen schon auf NEC 78K3+4, Fujitsu FR, ARM7TDMI, LPC11xx, 
LPC13xx, LPC17xx, LPC4088, STM32F1xx..3xx benutzt.

Und damit du mein generelles I/O-Konzept verstehst, hänge ich dir auch 
noch gio.zip mit dran, ist ein ganz primitives Interface. Damit kriegt 
man aber auf den unterschiedlichsten Plattformen ohne viel Aufwand den 
niederen Treiberkram recht wirkungsvoll von den projektabhängigen 
höheren Schichten getrennt.

W.S.

von Wühlhase (Gast)


Lesenswert?

Hallo

@Thomas Elger:
Hm...beim F446 weiß ich nur von den zwei verschiedenen 
Peripherie-Bussen. Wobei Timer noch über einen Multiplikator versorgt 
werden.

USART3 (die ich verwenden will) hängt z.B. am Peripherie-Bus1. Von 
mehreren PLLs weiß ich aber nichts, nur von einer (bzw. es gibt noch 
eine zweite, die versorgt aber nur einige wenige Peripherie, die bei mir 
keine Rolle spielt).

@W.S.:

W.S. schrieb:
> Bei dir ist der Name Programm, gelle?
Verstehe ich nicht...
Ich gebe aber freimütig zu, daß ich kein Programmierer bin, obgleich ich 
versuche auch da stetig besser zu werden.

Ansonsten hast du aber gut beobachtet-ein Konzept ist daraus definitiv 
noch nicht ersichtlich. Es ging erstmal darum, das Ganze überhaupt zum 
Laufen zu bringen. Der Controller soll nachher mit einem PC 
kommunizieren (da gibt es dann auch ein etwas größeres Protokoll). Über 
einen Ringpuffer hab ich mal kurz nachgedacht, aber soweit ist das Ganze 
definitiv noch nicht. Danke für den Code, den schaue ich mir doch gleich 
mal an.

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.