Forum: Mikrocontroller und Digitale Elektronik STM32F401 Initialisirung USART2 bare metal


von Tycho B. (asellus)


Lesenswert?

Hallo,
ich versuche gerade USART2 auf dem Evalboard Nucleo-F401RE zum Laufen zu 
bringen. Die LED leuchtet, und wenn ein USART-Interrupt ausgelöst wird, 
soll sie ausgehen. Einfach um zu sehen, dass der Sprung in die ISR 
funktioniert. Aber sie geht nicht aus, irgendetwas fehlt. Ich habe schon 
dutzende howtos durchgelesen, der Code unten ist das Ergebnis. Aber es 
will nicht. Wenn ich ein Zeichen schicke, kommt auf der RX-Leitung auch 
etwas an, das sehe ich am Oszi.
1
#include <stm32f4xx.h>
2
3
#define LED_PIN 5
4
#define LED_ON() GPIOA->BSRRL |= (1 << 5)
5
#define LED_OFF() GPIOA->BSRRH |= (1 << 5)
6
7
void USART2_IRQHandler()
8
{
9
   volatile uint16_t IIR;
10
11
      IIR = USART2->SR;
12
      if (IIR & USART_SR_RXNE) {                  // read interrupt
13
        USART2->SR &= ~USART_SR_RXNE;            // clear interrupt
14
      }
15
      LED_OFF();
16
}
17
18
int main() {
19
20
  uint32_t tmpreg = 0x00U;
21
22
  SET_BIT(RCC->APB1ENR, (RCC_APB1ENR_USART2EN));  //USART2 clock enable
23
  SET_BIT(RCC->AHB1ENR, (RCC_AHB1ENR_GPIOAEN));  //IO port A clock enable
24
25
  GPIOA->MODER   |=  (0b10  << 4);                // USART2 Tx (PA2)  Alternate function mode
26
  GPIOA->OTYPER  &=  ~( 1 << 2);          // USART2 Tx (PA2) Output push-pull (reset state)
27
  GPIOA->OSPEEDR  |=  ( 0b11 << 4);        // USART2 Tx (PA2)  High speed 0b0111
28
  GPIOA->AFR[0]  |=  ( 0b0111 << 8);        // USART2 Tx (PA2) Alternate function 7 USART
29
30
  GPIOA->MODER   |=  (0b11  << 6);                // USART2 Rx (PA3)  Analog mode
31
  GPIOA->OSPEEDR  |=  ( 0b11 << 4);        // USART2 Rx (PA3)  High speed
32
  GPIOA->AFR[0]  |=  ( 0b0111 << 12);        // USART2 Rx (PA3) Alternate function 7 USART
33
34
35
  USART2->CR1 &= ~(USART_CR1_UE);          // deaktiviere USART2
36
  /*-------------------------- USART CR2 Configuration -----------------------*/
37
  tmpreg = USART2->CR2;
38
39
    /* Clear STOP[13:12] bits */
40
  tmpreg &= (uint32_t)~((uint32_t)USART_CR2_STOP);  //00: 1 Stop bit
41
42
      /* Write to USART CR2 */
43
  WRITE_REG(USART2->CR2, (uint32_t)tmpreg);
44
  /*-------------------------- USART CR1 Configuration -----------------------*/
45
  tmpreg = USART2->CR1;
46
47
  /* Clear M, PCE, PS, TE and RE bits */
48
  tmpreg &= (uint32_t)~((uint32_t)(USART_CR1_M | USART_CR1_PCE | USART_CR1_PS | USART_CR1_TE | USART_CR1_RE | USART_CR1_OVER8));
49
50
  /* Configure the UART Word Length, Parity and mode:
51
     M=0: 1 Start bit, 8 Data bits, n Stop bit
52
     PCE=0: Parity control disabled
53
     TXE interrupt enable, RXNE interrupt enable
54
     TE RE enable */
55
  tmpreg |= (uint32_t)((uint32_t)(USART_CR1_TXEIE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE ));
56
57
  /* Write to USART CR1 */
58
  WRITE_REG(USART2->CR1, (uint32_t)tmpreg);
59
  /*-------------------------- USART CR3 Configuration -----------------------*/
60
  tmpreg = USART2->CR3;
61
62
  /* Clear CTSE and RTSE bits, hardware flow control disabled */
63
  tmpreg &= (uint32_t)~((uint32_t)(USART_CR3_RTSE | USART_CR3_CTSE));
64
65
  /* Write to USART CR3 */
66
  WRITE_REG(USART2->CR3, (uint32_t)tmpreg);
67
68
  USART2->BRR=0x54;        // 1MBaud
69
70
  /* In asynchronous mode, the following bits must be kept cleared:
71
     - LINEN and CLKEN bits in the USART_CR2 register,
72
     - SCEN, HDSEL and IREN  bits in the USART_CR3 register.*/
73
  CLEAR_BIT(USART2->CR2, (USART_CR2_LINEN | USART_CR2_CLKEN));
74
  CLEAR_BIT(USART2->CR3, (USART_CR3_SCEN | USART_CR3_HDSEL | USART_CR3_IREN));
75
76
  USART2->CR1 |=  (USART_CR1_UE);    //enable USART2
77
78
  /* Enbale GPIOA clock */
79
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
80
  /* Configure GPIOA pin 5 as output */
81
  GPIOA->MODER |= (1 << (LED_PIN << 1));
82
  /* Configure GPIOA pin 5 in max speed */
83
  GPIOA->OSPEEDR |= (3 << (LED_PIN << 1));
84
  __enable_irq();
85
  /* Turn on the LED */
86
  LED_ON();
87
88
  for(;;){
89
  ;
90
  }
91
}

von Mike R. (thesealion)


Lesenswert?

Auf die schnelle würde ich behaupten, dir fehlt die Einrichtung des 
NVIC.

von Tycho B. (asellus)


Lesenswert?

Gerade darüber nochmal gestolpert. Ich dachte bis jetzt, dass NVIC etwas 
von der HAL ist. Manmanman, es ist echt mühsam nach Atmel...

von W.S. (Gast)


Lesenswert?

Tycho B. schrieb:
> Manmanman, es ist echt mühsam nach Atmel

Du machst es aber auch ausgesprochen verkompliziert und unleserlich. 
Abgesehen davon gehört der ganze Kram in eine searate Handler-Quelle, wo 
nur die eigentlichen Benutz-Funktionen herausgucken und wo die beiden 
Datenströme gepuffert werden.

Ich hab hier schon des öfteren mal funktionable Handler für serielle 
Schnittstellen der diversen STM32Fxxx gepostet, also rate ich dir: such 
mal danach.

W.S.

von Mampf F. (mampf) Benutzerseite


Lesenswert?

Tycho B. schrieb:
> Ich dachte bis jetzt, dass NVIC etwas
> von der HAL ist. Manmanman, es ist echt mühsam nach Atmel...

Du kannst ja auch CMSIS verwenden^^ Bare-Metal würde ich bei solchen 
Kalibern eh vermeiden :)

von Christopher J. (christopher_j23)


Lesenswert?

Tycho B. schrieb:
> NVIC etwas
> von der HAL ist.

Nene, nix HAL, das ist sogar Standard für alle Cortex-M.

Ein simples
1
NVIC_EnableIRQ(USART2_IRQn);

sollte dafür sorgen das es läuft. Man könnte auch noch eine 
Interrupt-Priorität angeben aber wenn man das nicht macht, läuft der 
einfach mit der höchsten Priorität.

Mampf F. schrieb:
> Du kannst ja auch CMSIS verwenden^^

Genau diese NVIC_EnableIRQ-Funktion ist ein Bestandteil von CMSIS und 
wird über den Header "core_cm4.h" bereitgestellt, der wiederum von 
"stm32f4xx.h" eingebunden wird.

von Bernd K. (prof7bit)


Lesenswert?

Mampf F. schrieb:
> Du kannst ja auch CMSIS verwenden^^ Bare-Metal würde ich bei solchen
> Kalibern eh vermeiden :)

Er verwendet CMSIS. Und CMSIS ist bare metal.

> solchen Kalibern

Das ist nur ein F401RE, das Manual hat weniger als 1000 Seiten, das UART 
Kapitel nur 50.

Und die muss er eh lesen, ob er nun HAL verwendet oder nicht, nur bei 
letzterem muss er noch mehr lesen und noch beides unter einen Hut 
bringen, bzw das im einen gelesene im anderen wiederfinden. Verdreifacht 
den Aufwand.

: Bearbeitet durch User
von Mampf F. (mampf) Benutzerseite


Lesenswert?

Bernd K. schrieb:
> Er verwendet CMSIS. Und CMSIS ist bare metal.

Ah okay, sorry für den Mist, den ich geschrieben habe :)

Dachte, CMSIS wär schon kein Bare-Metal mehr ... Aber stimmt, die paar 
Funktionen die Registerzugriffe wrappen kann man auch nicht als 
Middle-Ware ansehen :)

Aber so ganz klar war das dem TE wohl auch nicht, da er ja sogar sowas 
wie NVIC_EnableIRQ vermeiden wollte und dachte, das würde bereits zu HAL 
gehören :)

Bernd K. schrieb:
> Das ist nur ein F401RE

Für mich zählen alle (mir bekannten ARMs) zu den "Kalibern" xD

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

Christopher J. schrieb:
> Tycho B. schrieb:
>> NVIC etwas
>> von der HAL ist.
>
> Nene, nix HAL, das ist sogar Standard für alle Cortex-M.
>

Klares Jein! S.U.

> Ein simples
>
>
1
> NVIC_EnableIRQ(USART2_IRQn);
2
>
>
> sollte dafür sorgen das es läuft. Man könnte auch noch eine
> Interrupt-Priorität angeben aber wenn man das nicht macht, läuft der
> einfach mit der höchsten Priorität.
>

Nur zur korinthenverdauungsauscheidungsdienenden Klärung (bin mir 
sicher, dass Du das weisst): Der NVIC ist zwar Bestandteil des Cortex 
Kerns, aber die Anzahl der unterstützten Interruptprioritäten ist 
chipabhängig (von 3 bis 8 bit, also 8 bis 255 Prios möglich). Deswegen 
ist die NVIC Initialisierung so ein Zwischending zwischen Kernel und 
Chip und damit CMSIS und HAL (merkt man z.B. sehr schmerzhaft am 
FreeRTOS Portierungslayer bei der Initialisierung der 
Prioritätsbarrieren).

von Nico W. (nico_w)


Lesenswert?

Tycho B. schrieb:
> volatile uint16_t IIR;

Das muss übrigends nicht volatile sein. Das setzt du ja nur einmal im 
Interrupt.

P.S.: Ich würde noch ggf. anmerken immer die Pullups definiert zu 
setzen. Die sind nicht immer alle aus. PA_13, PA_14, PA_15 und PB_4 sind 
zumindest beim F411 anders beschaltet im reset. Ist jetzt hier 
allerdings nicht das Problem.

von Tycho B. (asellus)


Lesenswert?

W.S. schrieb:
> Abgesehen davon gehört der ganze Kram in eine searate Handler-Quelle, wo
> nur die eigentlichen Benutz-Funktionen herausgucken und wo die beiden
> Datenströme gepuffert werden.

ist klar, es ist nur ein Minimalbeispiel, mit dem ich starte

Christopher J. schrieb:
> Ein simples
> NVIC_EnableIRQ(USART2_IRQn);
>
> sollte dafür sorgen das es läuft.

Das hat gestern schon nicht funktioniert. Wenn ich die Zeile einsetze, 
dann ist die LED aus. Ich dachte, es liegt daran, dass er, warum auch 
immer, gleich in die ISR springt. LED_OFF(); in der ISR auskommentiert, 
LED bleibt trotzdem aus. Scheinbar hängt er sich auf an dieser Stelle.

Kann einer bestätigen, dass BRR=0x54 mit 84 MHz 1MBaud entspricht? 
Vielleicht

von Tycho B. (asellus)


Lesenswert?

Nico W. schrieb:
> Das muss übrigends nicht volatile sein. Das setzt du ja nur einmal im
> Interrupt.

ja, war nur von irgendwoher rauskopiert.

von Nico W. (nico_w)


Lesenswert?

0x54 ist korrekt wenn da kein Teiler für APB2 eingestellt wurde.


Aber deswegen sollte er ja nicht nicht in den Interrupt springen. (Kein 
Tippfehler, doppelte Verneinung)

Vielleicht testeweise mal das DR pollen? 1MBaud ist auch recht viel. Ich 
kam bei meinem Nucleo nicht stabil über 250k.

: Bearbeitet durch User
von Christopher J. (christopher_j23)


Lesenswert?

Ruediger A. schrieb:
> die Anzahl der unterstützten Interruptprioritäten ist
> chipabhängig (von 3 bis 8 bit, also 8 bis 255 Prios möglich)

Völlig richtig. Deswegen gibt es z.B. auch in der corecm4.h eine dicke 
Warnung, falls es nirgendwo ein #define __NVIC_PRIO_BITS gibt. Das muss 
der Hersteller setzen, da ARM ja nicht wissen kann wie viele Bits der 
Hersteller jetzt implementiert hat. Der ganze sonstige Kram steht in der 
corecmX.h, z.B.:
1
__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
2
{
3
  if(IRQn < 0) {
4
    SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M  System Interrupts */
5
  else {
6
    NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff);    }        /* set Priority for device specific Interrupts  */
7
}
8
9
__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority)
10
{
11
...
12
}
13
14
etc.


Ruediger A. schrieb:
> Deswegen
> ist die NVIC Initialisierung so ein Zwischending zwischen Kernel und
> Chip und damit CMSIS und HAL (merkt man z.B. sehr schmerzhaft am
> FreeRTOS Portierungslayer bei der Initialisierung der
> Prioritätsbarrieren).

Von den Innereien von FreeRTOS habe ich absolut null Ahnung und habe das 
auf einem STM32 auch noch nie genutzt. Ich kann mir aber nicht erklären 
wieso FreeRTOS da zwingend irgendwas aus dem HAL von ST brauchen sollte. 
Alles was für FreeRTOS von Bedeutung sein sollte (nämlich die Anzahl der 
Prioritätsbits) sollte per #define im CMSIS-Header stehen (stm32f4xx.h 
bzw. stm32f401xe.h). Das wiederum heißt natürlich nicht, das ST nicht 
seine eigenen NVIC-Funktionen im HAL implementieren kann und es dem 
Nutzer nahe legt diese auch zu nutzen, obwohl das eigentlich völlig 
unnötig ist, weil der NVIC bis auf die Prioritätsbits innerhalb der 
jeweiligen Cortex-M Familie exakt gleich ist und alle nötigen Funktionen 
bereits in der corecmX.h drin stehen.

von Mampf F. (mampf) Benutzerseite


Lesenswert?

Tycho B. schrieb:
> Wenn ich die Zeile einsetze,
> dann ist die LED aus. Ich dachte, es liegt daran, dass er, warum auch
> immer, gleich in die ISR springt. LED_OFF(); in der ISR auskommentiert,
> LED bleibt trotzdem aus. Scheinbar hängt er sich auf an dieser Stelle.

Dazu fallen mir zwei Gründe ein ...

- der Interrupt-Handler existiert nicht. Der ARM springt in einen 
Fault-Handler und bleibt dort.
- du verwendest C++ und hast den Handler nicht 'extern "C"' deklariert.

Bei meinen ersten Starts mit dem STM32 hatte ich zum Glück schon einen 
Debugger, da war das Problem dann schnell gefunden :)

von Tycho B. (asellus)


Lesenswert?

Mampf F. schrieb:
> der Interrupt-Handler existiert nicht.
1
tmpreg=USART2->SR;
2
if((tmpreg&(1<<5)) !=0)LED_OFF(); //Read data register not empty
in der for-schleife funktioniert auch nicht

(This bit is set by hardware when the content of the RDR shift register 
has been transferred to the USART_DR register. An interrupt is generated 
if RXNEIE=1 in the USART_CR1 register.)

Mampf F. schrieb:
> du verwendest C++ und hast den Handler nicht 'extern "C"' deklariert

Im eclipse-wizzard habe ich c-Projekt ausgewählt.

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

Christopher J. schrieb:
>
> Ruediger A. schrieb:
>> Deswegen
>> ist die NVIC Initialisierung so ein Zwischending zwischen Kernel und
>> Chip und damit CMSIS und HAL (merkt man z.B. sehr schmerzhaft am
>> FreeRTOS Portierungslayer bei der Initialisierung der
>> Prioritätsbarrieren).
>
> Von den Innereien von FreeRTOS habe ich absolut null Ahnung und habe das
> auf einem STM32 auch noch nie genutzt. Ich kann mir aber nicht erklären
> wieso FreeRTOS da zwingend irgendwas aus dem HAL von ST brauchen sollte.
> Alles was für FreeRTOS von Bedeutung sein sollte (nämlich die Anzahl der
> Prioritätsbits) sollte per #define im CMSIS-Header stehen (stm32f4xx.h
> bzw. stm32f401xe.h). Das wiederum heißt natürlich nicht, das ST nicht
> seine eigenen NVIC-Funktionen im HAL implementieren kann und es dem
> Nutzer nahe legt diese auch zu nutzen, obwohl das eigentlich völlig
> unnötig ist, weil der NVIC bis auf die Prioritätsbits innerhalb der
> jeweiligen Cortex-M Familie exakt gleich ist und alle nötigen Funktionen
> bereits in der corecmX.h drin stehen.

Sorry, war missverständlich ausgedrückt. FreeRTOS braucht weder HAL noch 
CMSIS. Der Fall FreeRTOS sollte nur demonstrieren, wie die 
Zwischenstellung der NVIC zwischen Kern und Chip Falltüren bereiten 
kann. FreeRTOS setzt eine Barriere zwischen den Interruptprioritäten, 
unterhalb derer Interrupthandler Systemfunktionen benutzen dürfen; 
ausserdem müssen für den SysTick ISR und den SysCall ISR Prioritäten 
vergeben werden (i.d. Regel lowest). Daraus und der variablen Shiftgröße 
zwischen FreeRTOS IRQ Prioritäten und Cortex Prioritäten ergeben sich 
eine Menge subtiler Möglichkeiten, das System falsch aufzusetzen. 
Details sind aber etwas zu OT here.

Sorry und danke fürs Nachhaken, dieser Subthread darf nun gerne zu 
gunsten des Originalanliegens vom TO abgeschlossen werden!

von Nico W. (nico_w)


Lesenswert?

Tycho B. schrieb:
> GPIOA->MODER   |=  (0b11  << 6);                // USART2 Rx (PA3)
> Analog mode

Du NASE!

von Tycho B. (asellus)


Lesenswert?

Nico W. schrieb:
> Du NASE!

Ja durchaus, aber ich habe es irgendwo so gefunden, Ehrenwort!... ))
Also mit Alternate Function funktioniert zumindest
1
tmpreg=USART2->SR;
2
if((tmpreg&(1<<5)) !=0)LED_OFF(); //Read data register not empty

in die ISR wird jedoch nicht gesprungen. Ich muss jetzt mal rausfinden 
ob die Sprungadresse in den Interrupt-Handler wirklich 
USART2_IRQHandler() ist.

von Christopher J. (christopher_j23)


Lesenswert?

Tycho B. schrieb:
> USART2->BRR=0x54;        // 1MBaud

Die letzten vier Bits im BRR sind Nachkommastellen. Wenn du durch 84 
teilen willst solltest du

USART2->BRR = (0x54 << 4);

nehmen. So bist du um den Faktor 16 zu schnell, also in etwa bei 16 
MBaud :D

von Tycho B. (asellus)


Lesenswert?

Christopher J. schrieb:
> Wenn du durch 84
> teilen willst

USART_DIV=f_ck/(Baud*8*2) wenn OVER8 Null ist
Für eine Baudrate von 1 MBaud ist USART_DIV=5,25
Daraus ergibt sich Mantissa=5
Fraction=16*0,25=4
also USART_BRR=0x54
so habe ich mir das hergelitten.

Dass 0x54 gerade 0d84 ist, ist Zufall.

von Nico W. (nico_w)


Lesenswert?

War schon ganz verwirrt. Hab nochmal meinen Code rausgesucht. Wenn OVER8 
aus ist, sollte 0x54 korrekt sein.

von Christopher J. (christopher_j23)


Lesenswert?

Tycho B. schrieb:
> Christopher J. schrieb:
>> Wenn du durch 84
>> teilen willst
>
> USART_DIV=f_ck/(Baud*8*2) wenn OVER8 Null ist
> Für eine Baudrate von 1 MBaud ist USART_DIV=5,25
> Daraus ergibt sich Mantissa=5
> Fraction=16*0,25=4
> also USART_BRR=0x54
> so habe ich mir das hergelitten.
>
> Dass 0x54 gerade 0d84 ist, ist Zufall.

Du hast natürlich recht. Was ich geschrieben habe ist Schwachsinn. 
Geistige Umnachtung meinerseits...


Habe es gerade mal ausprobiert und bei mir lande ich im 
USART2_IRQHandler, wenn ich das __enable_irq(); durch ein 
NVIC_EnableIRQ(USART2_IRQn); ersetze.

__enable_irq() und __disable_irq() sind lediglich funktionen, die global 
interrupts sperren, bzw. diese Sperre aufheben. Wohl so ein bisschen wie 
bei AVR mit sei() und cli().

von Tycho B. (asellus)


Lesenswert?

Christopher J. schrieb:
> Habe es gerade mal ausprobiert und bei mir lande ich im
> USART2_IRQHandler, wenn ich das __enable_irq(); durch ein
> NVIC_EnableIRQ(USART2_IRQn); ersetze.

Hast du etwas in der startup_stm32.s etwas eigetragen?
So was wie
.word  USART2_IRQHandler

an der Stelle für den Interrupt 38, also mitten zwischen den ganzen
.word 0

und für NVIC_EnableIRQ(USART2_IRQn); brauch ich noch
#include <core_cm4.h>
richtig?

von Tycho B. (asellus)


Lesenswert?

Nico W. schrieb:
> War schon ganz verwirrt. Hab nochmal meinen Code rausgesucht. Wenn OVER8
> aus ist, sollte 0x54 korrekt sein.

Habe mir das Oszi-Signal dekodieren lassen bei einer '0', wenn ich also 
USART2->DR='0' belade. Er erkennt eine Bitrate von 190,623 kbit/s. 1Mbit 
mit 0x54 stimmt also auch irgendwie nicht.

von Tycho B. (asellus)


Lesenswert?

84/1000*191=16,044
scheinbar laufe ich mit 16MHz))))

von Nico W. (nico_w)


Lesenswert?

HSE nicht korrekt gestartet?!?

von Louis (Gast)


Lesenswert?

eher: PLL nicht korrekt eingestellt?

von Tycho B. (asellus)


Lesenswert?

das ist putzig, habe system_stm32f4xx.h eingebunden, aber die 
system_stm32f4xx.c nicht dazugelegt. nirgends gabs Gemeckere. Jetzt 
finde ich heraus, dass die SystemInit(), die aus startup_stm32.s 
gestartet wird, hier definiert ist. Super. Eingebunden, PLL_M von 25 auf 
16 geändert, kompiliert, juhu, jetzt ist die Baudrate 250.000, also 
immer noch Faktor 4 zuwenig. Aber immerhin. Ich sehe zwar nun 12 Errors 
in der system_stm32f4xx.c, aber es lässt sich alles kompilieren und 
ausführen. Ach ja, die Fehler tauchen nur auf wenn man auf die Datei 
system_stm32f4xx.c in Eclipse klickt. Sonst ist alles wunderbar und 
keine Fehler. So wünscht man sich eine Entwicklungsumgebung.
Ja, ich weiss, dass ist ein ziemlich idiotischer Fehler meinerseits, 
aber dass der Linker nichts sagt, naja, so was wie "deklaration 
gefunden, definition nicht" oder so

Nun, anscheinend fehlt etwas:
Field 'VTOR' could not be resolved

Im AVR Studio war nur #include "avr/interrupt.h" notwendig. Wie soll ich 
jetzt rauskriegen was da noch fehlt?

von W.S. (Gast)


Lesenswert?

Tycho B. schrieb:
> ist klar, es ist nur ein Minimalbeispiel, mit dem ich starte

Tycho B. schrieb:
> das ist putzig, habe system_stm32f4xx.h eingebunden, aber die
> system_stm32f4xx.c nicht dazugelegt. nirgends gabs Gemeckere.

Jaja. Du hast herumprobiert, aber du weißt noch immer nicht, was du 
eigentlich getan hast.

Nochmal mein Rat:

Halte den Umfang der möglichen Verwirrungen so klein wie möglich.

Also wenn du nicht wirklich ganz genau weißt, was du da so alles "mal 
eben" einbindest und was du in dessen Folge dann in dein Projekt 
hineinkriegst, dann unterlasse all solche Einbindungen.

Das Gleiche gilt für das Verwenden irgend welcher IDE's. Meine Erfahrung 
ist, daß eigentlich alle IDE's sehr dazu neigen, in völlig falsch 
verstandener Vorsorglichkeit einem alle möglichen Dinge wie 
Kommandozeilenparameter, Includefiles, eigene Startupcodes und mehr 
unterzujubeln, so daß man bei nicht funktionierender Firmware sich erst 
mal nach der tatsächlichen Ursache totsuchen muß. Auch aus diesem Grunde 
bestehen meine Projekte aus einer Batchdatei, wo alle Aufrufe von 
Compiler, Assembler, Linker usw. dediziert drinstehen - eben damit mit 
keine IDE unerwünscht dazwischen kommt.

Und wenn du denn schon irgenwelches Zeugs einbinden willst, dann schau 
dir an, was dieses Zeugs klammheimlich hinter sich her reinzieht.

Und nochwas: Solche bescheuerten "Minimalbeispiele" solltest du dir 
verkneifen. Setz dich lieber mit nem Stück Papier hin und skizziere dir 
dort einen richtigen Treiber und dessen Schnittstelle(n) zum aufrufenden 
Programm hin. Das ist ein Stückchen Strategie, was sich allemal lohnt. 
genial drauflos zu tippen, ist hingegen Murks.

W.S.

von Louis (Gast)


Lesenswert?

W.S. schrieb:
> Und nochwas: Solche bescheuerten "Minimalbeispiele" solltest du dir
> verkneifen. Setz dich lieber mit nem Stück Papier hin und skizziere dir
> dort einen richtigen Treiber und dessen Schnittstelle(n) zum aufrufenden
> Programm hin. Das ist ein Stückchen Strategie, was sich allemal lohnt.
> genial drauflos zu tippen, ist hingegen Murks.
>
> W.S.

+1

von Christopher J. (christopher_j23)


Lesenswert?

W.S. schrieb:
> Das Gleiche gilt für das Verwenden irgend welcher IDE's. Meine Erfahrung
> ist, daß eigentlich alle IDE's sehr dazu neigen, in völlig falsch
> verstandener Vorsorglichkeit einem alle möglichen Dinge wie
> Kommandozeilenparameter, Includefiles, eigene Startupcodes und mehr
> unterzujubeln

Das gilt leider auch ganz besonders für das "GNU ARM Eclipse Plugin", 
falls du dieses verwenden solltest. Völlige Obfuskation des 
Build-Systems, was leider geradezu nach Problemen schreit.

Ein minimales Makefile-Projekt mit funktionierender HSI-Konfiguration 
für ein Nucleo-F401 findest du z.B. hier:
https://github.com/prof7bit/bare_metal_stm32f401xe

Wenn du den MCO-Ausgang des ST-Link als HSE nutzen willst, dann kannst 
du dir mal die SystemInit hier anschauen (HSE Konfiguration im 
Bypass-Mode):
https://github.com/ChristianRinn/bare_metal_stm32f411xe/blob/master/src/STM32F411XE/gcc_startup_system.c#L60

Letzteres ist zwar für ein Nucleo F411 aber es sollte eins zu eins 
übertragbar sein.

von Tycho B. (asellus)


Lesenswert?

W.S. schrieb:
> Und nochwas: Solche bescheuerten "Minimalbeispiele" solltest du dir
> verkneifen. Setz dich lieber mit nem Stück Papier hin und skizziere dir
> dort einen richtigen Treiber und dessen Schnittstelle(n) zum aufrufenden
> Programm hin. Das ist ein Stückchen Strategie, was sich allemal lohnt.
> genial drauflos zu tippen, ist hingegen Murks.

Diesen Ratschlag kann ich so nicht annehmen. Ich weiß ganz genau wie der 
Treiber aussehen wird, wie ich ihn programmiere, welche sind die 
Schnittstellen usw. Danke der Fürsorge. Wenn ich aber von einem 1284p 
komme, und auf ein völlig neues System umsteige, dann fange ich an mit 
einem blinky.c. Und dann schaue ich zu, dass ich Interrupts aktivieren 
kann, und diese auch angesprungen werden. Und erst dann fange ich an 
irgendetwas zu programmieren. Das geht quasi nicht anders, und du machst 
es genau so, bin ich mir sicher. Der große Plan in allen Ehren, wenn ich 
aber nicht schaffe, dass die ISR feuert, dann ist Schluss. So einfach 
ist das.

Wie gesagt, bei 1284p war es interrupt.h, nichts weiter. Ich habe mir 
für den STM32 die freie Umgebung, die auch wohl vom Hersteller 
unterstützt wird, runtergeladen und installiert, System Workbench nennt 
sich die. Dann den wizzard gestartet, c-Projekt, mein Eval-Board 
ausgewählt. Ich möchte hardwarenahe programmieren, also rumgestöbert, 
#include <stm32f4xx.h> brauche ich, ok. Ich dachte es ist so ähnlich wie 
interrupt.h. In den Ordner inc reinkopiert. Jetzt will die
#include "core_cm4.h"
#include "system_stm32f4xx.h"
#include <stdint.h>
hm, bläht sich ja auf dachte ich, aber ok, die fehlenden in inc 
reinkopiert. Überall nach beispielen für LED blinken gesucht - gefunden, 
auch verstanden, ok, blinkt. Irgendwo gelesen, dass der virtuelle 
com-Port als usart2 vorhanden ist, super. Komplettes Protokoll mit ISRs 
auf dem 1284 läuft ja schon, muss ich portieren. Nach Beispielen für 
ISRs gesucht, deutlich schwieriger, viele Umgebungen, CMSIS, HAL, 
verschiedene Evalboards F1, F3, F4. Irgend ein Beispiel gefunden, 
verstanden, probiert. Funktioniert nicht. Wie ich es probiert habe steht 
weiter oben. Und dann dieser CUBE-Scheiss, wollte ich nicht, aber was 
solls, vielleicht kann ich da was rausziehen. Installiert, projekt 
kreiert, files erstellt. Maaan, das ist ja Wahnsinn was da an overhead 
produziert wird! Versucht durch die Funktionen mich durchzuklicken, um 
die richtige Reihenfolge und die nötigen Registerzugriffe 
rauszubekommen, eingesetzt, funktioniert nicht.

Was ist jetzt falsch an diesem Vorgehen? Was soll ich mir auf Papier 
nochmal aufmalen, wenn die ISR nicht angesprungen wird?

PS. Dass Minimalbeispiele bescheuert sind höre ich zum ersten Mal.

: Bearbeitet durch User
von Tycho B. (asellus)


Angehängte Dateien:

Lesenswert?

das hier ist super fürs Verständnis von RCC:
https://github.com/jkerdels/stm32edu/blob/master/src/rcc.c
Für meine Bedürfnisse umgeschrieben:
1
#include <stdint.h>
2
#include "rcc.h"
3
#include "stm32f4xx.h"
4
5
void rcc_init(void) {
6
7
// HSI einschalten
8
RCC->CR |= 0x00000001;
9
10
// warten bis HSI stabil läuft
11
while ((RCC->CR & 0x00000002) == 0);
12
13
// SYSCLK auf HSI stellen
14
RCC->CFGR &= 0xFFFFFFFC;
15
16
// warten auf Bestätigung, dass SYSCLK auf HSI läuft
17
while ((RCC->CFGR & 0x0000000C) != 0);
18
19
// Bits 0 bis 2 "freiräumen"
20
FLASH->ACR &= 0xFFFFFFF8;
21
22
// 2 Wait States konfigurieren
23
FLASH->ACR |= 0x00000002;
24
25
// AHB-Prescaler auf 1 (prescaler off)
26
RCC->CFGR &= 0xFFFFFF0F;
27
28
// Bits 10 bis 15 "freiräumen"
29
RCC->CFGR &= 0xFFFF03FF;
30
31
// Bits 10 bis 12 auf 100 und Bits 13 bis 15 auf 000
32
//APB1/2 und APB2/1
33
RCC->CFGR |= 0x00001000;
34
35
// PLL und PLLI2S ausschalten
36
RCC->CR &= 0xFAFFFFFF;
37
38
// Bits 0 bis 5 "freiräumen"
39
RCC->PLLCFGR &= 0xFFFFFFC0;
40
41
// Bits 0 bis 5 auf den passenden Prescaler einstellen (binär)
42
//F_HSI ist 16000000L
43
RCC->PLLCFGR |= (F_HSI / 2000000L) & 0x0000003F;
44
45
// Bits 6 bis 14 sowie Bits 16 und 17 "freiräumen" -> implizit P = 2
46
RCC->PLLCFGR &= 0xFFFC803F;
47
48
// N = 168 setzen in Bits 6 bis 14
49
RCC->PLLCFGR |= (168L << 6) & 0x00007FC0;
50
51
// Bits 24 bis 27 "freiräumen"
52
RCC->PLLCFGR &= 0xF0FFFFFF;
53
54
// Wert Q = 7 in Bits 24 bis 27
55
RCC->PLLCFGR |= 0x07000000;
56
57
// HSI über Bit 16 in RCC_CR einschalten
58
RCC->CR |= 0x00000001;
59
60
// Warten bis HSI an ist
61
while ((RCC->CR & 0x00000002) == 0);
62
63
// PLL-Modul über Bit 24 einschalten
64
RCC->CR |= 0x01000000;
65
66
// Warten bis PLL stabil (Bit 25)
67
while ((RCC->CR & 0x02000000) == 0);
68
69
// PLL als Taktquelle für SYSCLK auswählen (10 in Bits 0 und 1 des RCC_CFGR)
70
RCC->CFGR &= 0xFFFFFFFC; // "freiräumen"
71
RCC->CFGR |= 0x00000002; // "10" schreiben
72
73
// warten bis die SYSCLK umgestellt ist (Bits 2 und 3 müssen 10 werden)
74
while ((RCC->CFGR & 0x0000000C) != 0x00000008);
75
}
Damit messe ich eine Baudrate von 500k, Faktor 2 zuwenig. Es liegt wohl 
daran, dass USART2 am APB1-Bus hängt, und dieser läuft mit der halben 
Frequenz. Für die Berechnung von USART_DIV=f_ck/(Baud*8*2) muss für f_ck 
42MHz genommen werden. (BRR=0x2A)

: Bearbeitet durch User
von Nico W. (nico_w)


Lesenswert?

P müsste afaik auf 4 stehen für 84MHz.

Also:
16MHz / 8 = 2MHz (HSI / M)
2MHz * 168 = 336MHz (HSI / M * N)
336MHz / 4 = 84MHz (HSI / M * N / P)

Das gepostete Beispiel läuft auf 168MHz.

von Tycho B. (asellus)


Angehängte Dateien:

Lesenswert?

Nico W. schrieb:
> P müsste afaik auf 4 stehen für 84MHz.

Das stimmt. Aber jetzt habe ich mit BRR=0x2A 500kBaud, obwohl es 1M sein 
müsste...

von Tycho B. (asellus)


Angehängte Dateien:

Lesenswert?

Ok, Interrupt läuft und sendet, allerdings wie oben geschrieben mit 
500k.
Um den Takt zu testen ist mir nichts besseres eingefallen, als
LED_ON();
LED_OFF();
mit dem Oszi aufzunehmen.
Das Resultat ist ziemlich langsam, 4MHz oder so. Das verstehe ich nicht.
Jemand eine Idee?

von Nico W. (nico_w)


Lesenswert?

Also BSRR verodert man niemals. Dazu isses ja da.

von W.S. (Gast)


Lesenswert?

Tycho B. schrieb:
> Ich habe mir
> für den STM32 die freie Umgebung, die auch wohl vom Hersteller
> unterstützt wird, runtergeladen und installiert, System Workbench nennt
> sich die. Dann den wizzard gestartet, c-Projekt, mein Eval-Board
> ausgewählt. Ich möchte hardwarenahe programmieren, also rumgestöbert,
> #include <stm32f4xx.h> brauche ich, ok. Ich dachte es ist so ähnlich wie
> interrupt.h. In den Ordner inc reinkopiert. Jetzt will die
> #include "core_cm4.h"
> #include "system_stm32f4xx.h"
> #include <stdint.h>
> hm, bläht sich ja auf dachte ich, aber ok, die fehlenden in inc
> reinkopiert. Überall nach beispielen für LED blinken gesucht - gefunden,
> auch verstanden, ok, blinkt.

Deine Herangehensweise ist der meinigen diametral entgegengesetzt.
Wie man auf deine Weise zu einer sauberen Firmware kommen kann, ist mir 
schleierhaft.

Also: Wie ich an sowas rangehe:
1. Falls vorhanden, die "zielchip".h anschauen, ob die mir ausreichend 
gefällt. Wenn nicht, dann editiere ich sie, um mißfälliges Zeugs 
rauszuschmeißen oder ich mache mir selber eine anhand des RefMan's. Hier 
gibt es zwar jemanden, der alle, die nicht klaglos das Zeugs fressen, 
was ihnen vorgeworfen wird, zu Idioten erklärt, die "nicht alle Tassen 
im Schrank haben", aber mit solchen Kindereien kann ich leben.

2. Ich mache mir nen zum Chippassenden Startupcode, zumeist besteht das 
Ganze darin, die Interrupt-Vektoren zu überprüfen und die RAM-Bereiche 
passend zu setzen.

3. Dann sehe ich zu, mir einen Konfigurations-Unit zu machen, wo ich 
sowohl den Takt, als auch die Pins und die Taktversorgung der 
Peripherie-Cores aufsetze. Ist eben chip- und applikations-abhängig.

4. Dann sollte der Chip erstmal loslaufen und wo es nen benutzbaren 
U(S)ART gibt, mache ich mir nen passenden Treiber dazu, das läuft 
ebenfalls auf das dezente Modifizieren eines bereits im Portfolio 
befindlichen Treibers hinaus.

UND:

Ab da kann man mit dem Chip kommunizieren und der Grundstock ist gelegt.

Aber hier und da ein #include mal probehalber setzen oder "überall nach 
Beispielen" suchen - das ist schlichtweg Obermurks.

Wenn du Erfolg haben willst, dann gewöhne dich um und gehe deine Sachen 
systematisch an.

W.S.

von M. Н. (Gast)


Lesenswert?

Tycho B. schrieb:
> Um den Takt zu testen ist mir nichts besseres eingefallen, als
> LED_ON();
> LED_OFF();
> mit dem Oszi aufzunehmen.
> Das Resultat ist ziemlich langsam, 4MHz oder so. Das verstehe ich nicht.
> Jemand eine Idee?

Dass funktioniert beim STM auch schlecht. Der Zugriff auf den Port muss 
durch verschiedene Clock-Crossings und über verschiedene Busse. Das 
dauert alles seine Zeit. Zudem weiß man nie, wie der Compiler/der 
Prozessor selbst bestimmte Dinge abläuft. Da kommt selten was 
brauchbares raus.

Dieses "Ich setze ein bit" und 1 Takt später isses am Ausgang 
funktioniert so nur bei kleinen Controllern wie den AVRs

von Tycho B. (asellus)


Angehängte Dateien:

Lesenswert?

Nico W. schrieb:
> Also BSRR verodert man niemals. Dazu isses ja da.

Ok, Faktor 2 dazugewonnen, also 8MHz.

von Bernd K. (prof7bit)


Lesenswert?

Als ersten Schnelltest konfiguriere den Systick, mach einen Blinker im 
Sekundentakt. Wenn der langsamer als 1 mal pro Sekunde blinkt ist 
schonmal was faul. Dann konfiguriere den UART mit den aus dem Manual 
errechneten Baudratenteilerwerten und sende 0x55. Wenn die gemessene 
Baudrate nicht stimmt ist entweder Deine Rechnung falsch oder in Deiner 
Taktkonfiguration ist noch irgendwo der Wurm drin.

: Bearbeitet durch User
von Tycho B. (asellus)


Lesenswert?

Bernd K. schrieb:
> Als ersten Schnelltest konfiguriere den Systick, mach einen Blinker im
> Sekundentakt.

Ok, kriege einen Puls von 2 ms anstelle von 1 ms, dh. 42 MHz. Schon drei 
mal die Settings durchgegangen, auch mit Cube Projekt erstellt, 
Konfiguration siehe oben, Punkt für Punkt diesen Wahnsinn durchgegangen. 
Ohne Erfolg, ich schreibe dieselben Werte in die Register wie Cube (aber 
nicht in der Reihenfolge).
1
SysTick_Config(84000000UL/1000000);
2
SysTick->CTRL |= 4;
3
...
4
TimingDelay=1000;
5
LED_ON();
6
while(TimingDelay !=0);
7
LED_OFF();
8
...
9
void SysTick_Handler(void)
10
{
11
  if(TimingDelay !=0)
12
  {
13
    TimingDelay --;
14
  }
15
}

: Bearbeitet durch User
von Tycho B. (asellus)


Lesenswert?

Also für SysTick wird SYSCLK verwendet, damit kann der Fehler nur bei 
PLLM, PLLN oder PLLP liegen.

Ist ja nicht viel Code
1
RCC->PLLCFGR &= 0xFFFFFFC0;
2
// Bits 0 bis 5 auf den passenden Prescaler einstellen (binär) PLLM=8
3
RCC->PLLCFGR |= (0x8) & 0x0000003F;
4
5
// Bits 6 bis 14 sowie Bits 16 und 17 "freiräumen" ->
6
RCC->PLLCFGR &= 0xFFFC803F;
7
RCC->PLLCFGR |= (1<<16) & 0x00030000; //PLLP = 4
8
9
// PLLN = 168 setzen in Bits 6 bis 14
10
RCC->PLLCFGR |= (168UL << 6) & 0x00007FC0;

von Hartmut D. (Gast)


Lesenswert?

@Tyco B.

Wie du an deinen Fragen selbst sehen kannst, mag deine Vorgehensweise 
für AVRs halbwegs passen aber für "modernere" Controller driftet das 
eher ab richtung "übles Bastlerniveau". Profis arbeiten wesentlich 
systematischer. Im Datenblatt / RefMan das betreffende Kapitel lesen, 
verstehen, implementieren, debuggen, testen, fertig. Was genau ist so 
schwer daran???

Statt dessen spielst du mit dem Cube rum... Das ist von ST für die - 
nennen wir sie mal semi-Programmierer  Informatiker  Bastler - 
gedacht.

Kleines Beispiel: schon mal das "clock tree" angeschaut??? Falls ja, so 
sollte sich deine "Faktor 2 -Frage" schnell erledigt haben. Alternativ: 
ein Profi nimmt auch mal ein MCO-Ausgang und misst einfach nach was auf 
dem Chip passiert...

AVRs sind tolle Mikrocontroller, keine Frage. Aber gerade die 
"AVR-Profis" fallen hier oft auf durch Ignoranz und mangelnde 
Bereitschaft die Doku durchzulesen bzw. es zu verstehen. Statt dessen 
wird gerne gefragt (und nach Kräften gebastelt) aber die Antworten 
werden dann nach eigenem Gusto verworfen, falls diese nicht dem eigenen 
AVR-fixiertem Weltbild entsprechen (siehe z.B. die Reaktion aus die 
Antwort von W.S. weiter oben).

Tycho B. schrieb:
> Diesen Ratschlag kann ich so nicht annehmen. Ich weiß ganz genau wie der
> Treiber aussehen wird, wie ich ihn programmiere, welche sind die
> Schnittstellen usw. Danke der Fürsorge. Wenn ich aber von einem 1284p
> komme, und auf ein völlig neues System umsteige, dann fange ich an mit
> einem blinky.c. Und dann schaue ich zu, dass ich Interrupts aktivieren
> kann, und diese auch angesprungen werden. Und erst dann fange ich an
> irgendetwas zu programmieren. Das geht quasi nicht anders, und du machst
> es genau so, bin ich mir sicher. Der große Plan in allen Ehren, wenn ich
> aber nicht schaffe, dass die ISR feuert, dann ist Schluss. So einfach
> ist das.

Sorry, aber das ist bullshit. Kommt vermutlich aus der Zeit, als noch 
die Bastler keinen Debugger kannten, sondern alles mit ISP auf den 
Controller geschmissen haben. Wenn es nicht funktioniert hat, so hat man 
am Code rumgeändert bis es funktionierte. (Mir ist bis heute ein Rätsel, 
weshalb JTAG nie von den Bastern angenommen wurde, das hatte sogar schon 
der olle Mega32.)

Beim STM32 hast du SWD/JTAG und einige Spezialregister (z.B. DBGMCU) zum 
Debuggen. Lerne sie kennen und nutze sie. Damit erledigt sich dein 
"Blinky" von ganz alleine (Englischkenntnisse, Fachwissen und 
Beherrschung von C vorausgesetzt).

von Tycho B. (asellus)


Lesenswert?

Ich habe vergessen das 22te Bit PLLSRC in RCC->PLLCFGR zu löschen, damit 
war HSE Oszillator als Eingang ausgewählt, welcher mit dem ST-Link-Takt 
von 8 MHz verbunden ist.

: Bearbeitet durch User
von W.S. (Gast)


Lesenswert?

Hartmut D. schrieb:
> (Mir ist bis heute ein Rätsel,
> weshalb JTAG nie von den Bastern angenommen wurde, das hatte sogar schon
> der olle Mega32.)

Nee, mir ist das durchaus kein Rätsel.

Kennst du noch den ollen 'Wiggler'? Das war zwar eine billige Lösung, 
aber dennoch eine einzige Katastrophe. Hat nie zuverlässig geklappt.

Die Alternative wäre gewesen, sich einen stink-überteuerten richtigen 
JTAG-Adapter zu kaufen. Lauterbach oder Hitex (Tantino oder so ähnlich) 
und Konsorten.

Kurzum, für JTAG gab es nie etwas, das man sich als Bastler mal eben so 
leisten konnte. Und auch heutzutage sieht es damit mau aus, wenn man mal 
die Vollversionen der einschlägigen Hersteller anguckt. Da sind Preise 
in der 400..700€ Region durchaus üblich. Jaja, der JLink-Edu oder die 
diversen XYZLink-OB sind heutzutage endlich da, auch die Dinger vom 
Chinesen. Aber das war vor 15 oder 20 Jahren überhaupt nicht so.

W.S.

von Hartmut D. (Gast)


Lesenswert?

W.S. schrieb:
> Aber das war vor 15 oder 20 Jahren überhaupt nicht so.

Mit dem Dragon gab es doch JTAG bzw. für die kleinen Controller das 
DebugWire.

von Hartmut D. (Gast)


Lesenswert?

Angeblich waren die Dragons zwar empfindlich (wobei meine erste und 
einzige nach ca. 10 AVR-Projekten immernoch einwandfrei funktioniert 
ohne jegliche externe Schutzbeschaltung o.ä.) aber relativ günstig 
verfügbar. Meinen hatte ich damals bei Schukat geholt für 36,50 EUR (mit 
MwSt. und Lieferung waren es dann 43,44 EUR.) War OK und man konnte 
damit arbeiten.

von Tycho B. (asellus)


Lesenswert?

Wie ist es eigentlich mit input capture Eingang im TimerX, z.B. Timer2 
channel 1. Wenn ich im DM00096844 Abb. 17 mir anschaue, dann kann man 
jeden Eingang auf die alternate function AF1 mappen. Im Cube wizzard ist 
es für Timer2_CH1 nur PA0, PA5 und PA15.

von Christopher J. (christopher_j23)


Lesenswert?

Weil AF1 nicht gleichbedeutend mit TimerX Ch1 ist. Welche AF an welchem 
Pin welche Funktion bewirkt steht im Datenblatt.

von fft (Gast)


Lesenswert?

Tycho B. schrieb:
> dann kann man
> jeden Eingang auf die alternate function AF1 mappen.

Nein, das steht dort nicht. Dort steht, dass für man für jeden Pin die 
AF1-x im Register aktivieren kann. Welche AF auf den jeweiligen Pin 
liegen steht im Datenblatt, nicht im RefMan.

von Tycho B. (asellus)


Lesenswert?

Vielen Dank.

von Tycho B. (asellus)


Lesenswert?

Noch eine Verständnisfrage: Wenn ich UART2 ohne Flusskontrolle benutze, 
sind dann die Pins CTS (Pin A0) und RTS trotzdem belegt und können nicht 
mehr von z.B. Timer2 input capture (Pin A0) belegt werden?

UART2 läuft, wenn ich aber die Clock von Timer2 aktiviere, dann sendet 
UART2 nichts mehr. Im Cube sehe ich den Konflikt, da ich aber die 
Flusskontrolle nicht benutze, dachte ich, dass Cube evtl. nur eine 
Warnung ausgibt.

von STM Apprentice (Gast)


Lesenswert?

Tycho B. schrieb:
> Noch eine Verständnisfrage: Wenn ich UART2 ohne Flusskontrolle benutze,
> sind dann die Pins CTS (Pin A0) und RTS trotzdem belegt und können nicht
> mehr von z.B. Timer2 input capture (Pin A0) belegt werden?

Das wird ja wohl von deiner Initialisierung abhängen.

In der SPL gibt es die Möglichkeiten
1
#define USART_HardwareFlowControl_None       ((uint16_t)0x0000)
2
#define USART_HardwareFlowControl_RTS        ((uint16_t)0x0100)
3
#define USART_HardwareFlowControl_CTS        ((uint16_t)0x0200)
4
#define USART_HardwareFlowControl_RTS_CTS    ((uint16_t)0x0300)
Würde man also keine HardwareFlowControl wählen müssten nach
allen was ich bis jetzt erfahren habe, diese Pins auch frei
sein. Denn nur das was man initialsiert ist auch belegt.

Aber du hast uns ja nicht gezeigt wie du deinen UART2 initialsierst.
Sicher ist die Alernate Function des betreffenden Pins auch noch
ein Thema.

von STM Apprentice (Gast)


Lesenswert?

Tycho B. schrieb:
> Im Cube sehe ich den Konflikt
-----^^^^----------

Tycho B. schrieb:
> Initialisirung USART2 bare metal
------------------------^^^^^^^^^^-----

von Tycho B. (asellus)


Lesenswert?

STM Apprentice schrieb:
> -----^^^^----------
Cube benutze ich nur zur Visualisierung, oder gehe den generierten Code 
durch um zu verstehen wie es bare metal läuft.
Die Initialisierung steht im ersten Beitrag, es steht auch drin:
1
/* Clear CTSE and RTSE bits, hardware flow control disabled */
2
tmpreg &= (uint32_t)~((uint32_t)(USART_CR3_RTSE | USART_CR3_CTSE));
3
4
/* Write to USART CR3 */
5
WRITE_REG(USART2->CR3, (uint32_t)tmpreg);

: Bearbeitet durch User
von STM Apprentice (Gast)


Lesenswert?

Tycho B. schrieb:
> Die Initialisierung steht im ersten Beitrag,

... die ja falsch sein kann da du Probleme dazu gemeldet hast.

Seitdem ist einiges Wasser die Donau hinuntergelaufen und wer
weiss was du inzwischen mit deinem Code gemacht hast ...

von Tycho B. (asellus)


Angehängte Dateien:

Lesenswert?

STM Apprentice schrieb:
> Seitdem ist einiges Wasser die Donau hinuntergelaufen und wer
> weiss was du inzwischen mit deinem Code gemacht hast ...

das stimmt
1
uint32_t tmpreg = 0x00U;
2
  extern __IO uint32_t TimingDelay;
3
4
  rcc_init();
5
  SysTick_Config(84000000UL/1000000);
6
  SysTick->CTRL |= 4;  
7
8
  SET_BIT(RCC->APB1ENR, (RCC_APB1ENR_USART2EN));  //USART2 clock enable
9
  SET_BIT(RCC->APB1ENR, (RCC_APB1ENR_TIM2EN));  //TIMER2 clock enable
10
  SET_BIT(RCC->AHB1ENR, (RCC_AHB1ENR_GPIOAEN));  //IO port A clock enable
11
12
  GPIOA->MODER   |=  (0b10  << 4);                // USART2 Tx (PA2)  Alternate function mode
13
  GPIOA->OTYPER  &=  ~( 1 << 2);          // USART2 Tx (PA2) Output push-pull (reset state)
14
  GPIOA->OSPEEDR  |=  ( 0b11 << 4);        // USART2 Tx (PA2)  High speed 0b0111
15
  GPIOA->AFR[0]  |=  ( 0b0111 << 8);        // USART2 Tx (PA2) Alternate function 7 USART
16
17
  GPIOA->MODER   |=  (0b10  << 6);                // USART2 Rx (PA3)  Analog mode
18
  GPIOA->OSPEEDR  |=  ( 0b11 << 6);        // USART2 Rx (PA3)  High speed
19
  GPIOA->AFR[0]  |=  ( 0b0111 << 12);        // USART2 Rx (PA3) Alternate function 7 USART
20
21
22
  USART2->CR1 &= ~(USART_CR1_UE);          // deaktiviere USART2
23
  /*-------------------------- USART CR2 Configuration -----------------------*/
24
  tmpreg = USART2->CR2;
25
26
    /* Clear STOP[13:12] bits */
27
  tmpreg &= (uint32_t)~((uint32_t)USART_CR2_STOP);  //00: 1 Stop bit
28
29
      /* Write to USART CR2 */
30
  WRITE_REG(USART2->CR2, (uint32_t)tmpreg);
31
  /*-------------------------- USART CR1 Configuration -----------------------*/
32
  tmpreg = USART2->CR1;
33
34
  /* Clear M, PCE, PS, TE and RE bits */
35
  tmpreg &= (uint32_t)~((uint32_t)(USART_CR1_M | USART_CR1_PCE | USART_CR1_PS | USART_CR1_TE | USART_CR1_RE | USART_CR1_OVER8));
36
37
  /* Configure the UART Word Length, Parity and mode:
38
     M=0: 1 Start bit, 8 Data bits, n Stop bit
39
     PCE=0: Parity control disabled
40
     TXE interrupt enable, RXNE interrupt enable
41
     TE RE enable */
42
  tmpreg |= (uint32_t)((uint32_t)(USART_CR1_TXEIE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE ));
43
44
  /* Write to USART CR1 */
45
  WRITE_REG(USART2->CR1, (uint32_t)tmpreg);
46
  /*-------------------------- USART CR3 Configuration -----------------------*/
47
  tmpreg = USART2->CR3;
48
49
  /* Clear CTSE and RTSE bits, hardware flow control disabled */
50
  tmpreg &= (uint32_t)~((uint32_t)(USART_CR3_RTSE | USART_CR3_CTSE));
51
52
  /* Write to USART CR3 */
53
  WRITE_REG(USART2->CR3, (uint32_t)tmpreg);
54
55
  USART2->BRR=0x2A;        // 0x54 1MBaud 0x222E 9600
56
57
  /* In asynchronous mode, the following bits must be kept cleared:
58
     - LINEN and CLKEN bits in the USART_CR2 register,
59
     - SCEN, HDSEL and IREN  bits in the USART_CR3 register.*/
60
  CLEAR_BIT(USART2->CR2, (USART_CR2_LINEN | USART_CR2_CLKEN));
61
  CLEAR_BIT(USART2->CR3, (USART_CR3_SCEN | USART_CR3_HDSEL | USART_CR3_IREN));
62
63
  USART2->CR1 |=  (USART_CR1_UE);    //enable USART2
64
65
  //NVIC->ISER[(((uint32_t)(int32_t)USART2_IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)USART2_IRQn) & 0x1FUL));
66
67
  /* Enbale GPIOA clock */
68
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
69
  /* Configure GPIOA pin 5 as output */
70
  GPIOA->MODER |= (1 << (LED_PIN << 1));
71
  /* Configure GPIOA pin 5 in max speed */
72
  GPIOA->OSPEEDR |= (3 << (LED_PIN << 1));
73
  /* Turn on the LED */
74
75
  UART2_disable_transmit_interrupt();
76
  NVIC_EnableIRQ(USART2_IRQn);
77
  NVIC_EnableIRQ(SysTick_IRQn);
78
79
  TIM2->CCMR1 |= (0b01<<0); //CC1s
80
  TIM2->CCMR1 |= (0b100<<4); //filter
81
  TIM2->CCER |= (0<<3)|(1<<1)|(1<<0);//CC1NP, CC1P, CC1E
82
  TIM2->CCMR1 |= (0b100<<4); //filter
83
  TIM2->DIER |= (1<<1); //CC1IE
84
  GPIOA->MODER   |=  (0b10  << 0);                // Timer2 input capture (PA0)  Alternate function mode
85
  GPIOA->OSPEEDR  |=  ( 0b11 << 0);        // Timer2 input capture (PA0)  High speed 0b0111
86
  GPIOA->AFR[0]  |=  ( 0b0001 << 0);        // Timer2 input capture (PA0) Alternate function 1 Timer2
87
  NVIC_EnableIRQ(TIM2_IRQn);

: Bearbeitet durch User
von STM Apprentice (Gast)


Lesenswert?

Tycho B. schrieb:
> /* Write to USART CR2 */
>   WRITE_REG(USART2->CR2, (uint32_t)tmpreg);

Ganz allgemein sehe ich diese aufgerufene Funktion nicht.
Warum hier extra eine Setz-Funktion wenn alles so "bare
metal" sein soll?

Wenn deine Funktion (oder ist es ein Macro?) WRITE_REG
32 Bit schreibt, könnte da etwas schiefgehen? In der SPL
wird (immer?) 16 Bit geschrieben, auch wenn die temp-
Variablen 32 Bit sind.

von Tycho B. (asellus)


Lesenswert?

STM Apprentice schrieb:
> Ganz allgemein sehe ich diese aufgerufene Funktion nicht.
> Warum hier extra eine Setz-Funktion wenn alles so "bare
> metal" sein soll?

weil ich es aus einem Beispiel rauskopiert habe und nicht extra zu
USART2->CR2 &=~ (USART_CR3_RTSE | USART_CR3_CTSE);
geändert habe.

Makro:
#define WRITE_REG(REG, VAL)   ((REG) = (VAL))

von Tycho B. (asellus)


Lesenswert?

STM Apprentice schrieb:
> Würde man also keine HardwareFlowControl wählen müssten nach
> allen was ich bis jetzt erfahren habe, diese Pins auch frei
> sein. Denn nur das was man initialsiert ist auch belegt.

Ich habe in der ISR vom Timer2 den TIM_SR_CC1IF Flag nicht gelöscht und 
blieb dort hängen, deswegen hat UART2 nicht funktioniert. Die Warnung in 
Cube hat mich irritiert, daher dachte ich es liegt daran.

von Bernd K. (prof7bit)


Lesenswert?

Tycho B. schrieb:
> Makro:
> #define WRITE_REG(REG, VAL)   ((REG) = (VAL))
1
/** @addtogroup Exported_macro
2
  * @{
3
  */
4
#define SET_BIT(REG, BIT)     ((REG) |= (BIT))
5
6
#define CLEAR_BIT(REG, BIT)   ((REG) &= ~(BIT))
7
8
#define READ_BIT(REG, BIT)    ((REG) & (BIT))
9
10
#define CLEAR_REG(REG)        ((REG) = (0x0))
11
12
#define WRITE_REG(REG, VAL)   ((REG) = (VAL))
13
14
#define READ_REG(REG)         ((REG))
15
16
#define MODIFY_REG(REG, CLEARMASK, SETMASK)  WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))
17
18
#define POSITION_VAL(VAL)     (__CLZ(__RBIT(VAL))) 
19
20
21
/**
22
  * @}
23
  */

Die sind wirklich lustig. Da hatte der Praktikant anscheinend Langeweile 
und hat angefangen in bester Anfängermanier seine "nützlichen" 
Lieblingsmakros zu definieren, so überflüssig wie selten was, nach einer 
halben Stunde haben sie ihn dann aber anscheinend dort wieder abgezogen 
und stattdessen zum Kaffeekochen verurteilt. Leider wurde danach 
vergessen diesen albernen Unfug wieder zu entfernen.

von Tycho B. (asellus)


Lesenswert?

Bernd K. schrieb:
> (__CLZ(__RBIT(VAL)))

Dreifachverkettete Makros um Nullen zu zählen, mit denen man am Anfang 
initialisiert hat. Ich glaube es ist code obfuscation, damit man Cube 
als black box benutzt.

von Tycho B. (asellus)


Lesenswert?

Ich habe gelesen, dass der interne Oszillator mit bis zu ±3% Abweichung 
behaftet ist, und messe mit SysTick an Stelle von eingestellten 2ms 
1,9757ms, also eine Abweichung von 1,2%. Das ist also in Ordnung.
Was mich aber wundert ist der Jitter, also die Standardabweichung, die 
beträgt 1µs über 10.000 Samples gemessen. Ist das normal, dass der 
RC-Oszillator diese Instabilität hat? Ich habe keine Angaben dazu 
gefunden.

Beitrag #4984161 wurde vom Autor gelöscht.
von Darth Moan (Gast)


Lesenswert?

Moin,

Bernd K. schrieb:
> Die sind wirklich lustig. Da hatte der Praktikant anscheinend Langeweile

Ja wenn dem mal so waere. Es gibt da verschiedene Seuchen, zB AUTOSARS:
1
#define FUNC(rettype, memclass) rettype
2
#define P2VAR(ptrtype, memclass, ptrclass) ptrtype*
3
#define P2CONST(ptrtype, memclass, ptrclass) const ptrtype*
4
#define CONSTP2VAR(ptrtype, memclass, ptrclass) ptrtype* const
5
#define CONSTP2CONST(ptrtype, memclass, ptrclass) const ptrtype* const
6
#define P2FUNC(rettype, ptrclass, fctname) rettype (* fctname)
7
#define CONST(consttype, memclass) const consttype
8
#define VAR(vartype, memclass) vartype
9
#define P2P2VAR(ptrtype, memclass, ptrclass) ptrtype **
10
#define P2P2CONST(ptrtype, memclass, ptrclass) const ptrtype **

Da kann man eine Function mit Rueckgabewerte und Parameter doch gleich
viel klarer erkennen, oder? ZB:
1
FUNC( Nm_ReturnType, CANNM_CODE ) CanNm_GetState( CANNM_CHANNEL_SYSTEMTYPE_FIRST  CONSTP2VAR( Nm_StateType, AUTOMATIC, CANNM_APPL_VAR ) nmStatePtr, CONSTP2VAR( Nm_ModeType, AUTOMATIC, CANNM_APPL_VAR ) nmModePtr )

Und diesen Haufen braune Masse muss man dann tatsaechlich verwenden.
Naja, zum Glueck nicht immer.

Tycho B. schrieb:
>> (__CLZ(__RBIT(VAL)))
>
> Dreifachverkettete Makros um Nullen zu zählen, mit denen man am Anfang
> initialisiert hat. Ich glaube es ist code obfuscation, damit man Cube
> als black box benutzt.

Findest Du? Also das Macro "kehrt" doch nur ein (1 << n) "um", sodass
man n wieder herausbekommt. Aus einem Register zum Bleistift. Also soo
schlecht finde ich das POSITION_VAL() Macro nun auch nicht. Oder
verstehe ich deinen Einwand falsch?

von Tycho B. (asellus)


Lesenswert?

Darth Moan schrieb:
> Findest Du? Also das Macro "kehrt" doch nur ein (1 << n) "um", sodass
> man n wieder herausbekommt. Aus einem Register zum Bleistift. Also soo
> schlecht finde ich das POSITION_VAL() Macro nun auch nicht. Oder
> verstehe ich deinen Einwand falsch?

Vielleicht hast du ja recht und ich sehe nicht den effektiven 
Einsatzzweck. Mein Einwend war, dass n ja schon bekannt ist, also als 
#define. Deswegen, um n herauszubekommen, kann man gleich n nehmen, ist 
ja schon definiert...

von go for gold (Gast)


Lesenswert?

Tycho B. schrieb:
> Gerade darüber nochmal gestolpert. Ich dachte bis jetzt, dass NVIC
> etwas von der HAL ist. Manmanman, es ist echt mühsam nach Atmel...

Wieso? Auch Atmels Cortexe bedürfen der Initialisierung des NVIC.

von go for gold (Gast)


Lesenswert?

> void USART2_IRQHandler()
> {
>   volatile uint16_t IIR;

Warum volatile für diese lokale Variable?

von Darth Moan (Gast)


Lesenswert?

Moin,

Tycho B. schrieb:
> Deswegen, um n herauszubekommen, kann man gleich n nehmen, ist
> ja schon definiert...

Ja stimmt, in dem Fall macht es natuerlich keinen Sinn, das Macro
zu nehmen. Vielleicht bin ich da schon zu sehr vorgeschaedigt, dass
mir mancherlei sinnloses Zeug schon gar nicht mehr auffaellt.

von Rainer S. (rsonline)


Lesenswert?

Tycho B. schrieb:

>   GPIOA->OSPEEDR  |=  ( 0b11 << 4);        // USART2 Tx (PA2)  High speed 0b0111

Richtig so? Einmal 0b11 und dann 0b0111?


>   GPIOA->MODER   |=  (0b10  << 6);                // USART2 Rx (PA3) Analog mode

Analog Mode ???

von Tycho B. (asellus)


Lesenswert?

Rainer S. schrieb:
>>   GPIOA->OSPEEDR  |=  ( 0b11 << 4);        // USART2 Tx (PA2)  High speed 
0b0111
>
> Richtig so? Einmal 0b11 und dann 0b0111?

Sorry, der Kommentar ist falsch, (copy&paste aus einem Beispiel für 
einen anderen µC)

Rainer S. schrieb:
>>   GPIOA->MODER   |=  (0b10  << 6);                // USART2 Rx (PA3) Analog 
mode
>
> Analog Mode ???

Dasselbe, der Kommentar ist falsch
00: Input (reset state)
01: General purpose output mode
10: Alternate function mode
11: Analog mode

von Tycho B. (asellus)


Lesenswert?

go for gold schrieb:
> Warum volatile für diese lokale Variable?

Nico W. schrieb:
> Das muss übrigends nicht volatile sein. Das setzt du ja nur einmal im
> Interrupt.

Tycho B. schrieb:
> ja, war nur von irgendwoher rauskopiert.

von Tycho B. (asellus)


Lesenswert?

STM32F401RET, mit aktivieretem USART2

In CubeMX finde ich für TIM2 Channel4 einen Konflikt mit USART2, wegen 
Doppelbelegung des Pins PA3. Es gibt aber die Auswahl "Input Capture 
indirect mode". Was ist das? Suche in Google und RM0368 haben nichts 
brauchbares geliefert.

von Christopher J. (christopher_j23)


Lesenswert?

Du kannst die Timer Inputs zum Teil remappen, d.h. du kannst TIM2 Ch4 so 
konfigurieren, dass er als Input-Pin, den Pin nutzt der normalerweise 
für Ch3 zuständig ist. Entsprechende Doku findest du im RefMan wenn du 
dir die Beschreibung für CC4S im TIMx_CCMR2 anschaust. Wenn du nach CC1S 
suchst findest du auch noch allgemein gehaltene Beispiele und Diagramme 
(am Beispiel von Ch1).

von Tycho B. (asellus)


Lesenswert?

Christopher J. schrieb:
> Du kannst die Timer Inputs zum Teil remappen,

Ich möchte alle vier Input Capture units des Timer2 benutzen, damit ist 
TI3 mit der dritten Input capture unit belegt.
TIM2_CH4 liegt auf PA3 und lässt sich nicht weiter remappen, nur 
"indirect mode" geht. Deswegen meine Frage, was das überhaupt ist.

von Christopher J. (christopher_j23)


Lesenswert?

Tycho B. schrieb:
> Christopher J. schrieb:
>> Du kannst die Timer Inputs zum Teil remappen,
>
> Ich möchte alle vier Input Capture units des Timer2 benutzen, damit ist
> TI3 mit der dritten Input capture unit belegt.

Sorry wenn ich mich missverständlich ausgedrückt habe: Dieser "indirect 
mode" ist nichts anderes als das du durch entsprechende Konfiguration 
der CCxS Bits im CCMRy dafür sorgst, dass z.B. Timer 2 Ch3 an TI 4 
anliegt. Mit dem "remappen" von (alternativen) Pin-Funktionen hat das 
umkonfigurieren der CCxS Bits nichts zu tun. Wenn du alle Kanäle nutzen 
willst bringt dir das aber nichts, da musst du wohl oder übel einen 
anderen Timer nehmen.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Tycho B. schrieb:
> das ist putzig, habe system_stm32f4xx.h eingebunden, aber die
> system_stm32f4xx.c nicht dazugelegt. nirgends gabs Gemeckere. Jetzt
> finde ich heraus, dass die SystemInit(), die aus startup_stm32.s
> gestartet wird, hier definiert ist. Super. Eingebunden, PLL_M von 25 auf
> 16 geändert, kompiliert, juhu, jetzt ist die Baudrate 250.000, also
> immer noch Faktor 4 zuwenig.

Dieser Beitrag ist zwar schon einen Monat her, jedoch möchte ich der 
Vollständigkeit einen Patch der system_stm32f4xx.h erwähnen, weil 
bereits viele dieses PLL_M-Problem hatten.

Nämlich, dort wo standardmäßig steht:
1
#define PLL_M      25

folgendes stattdessen einfügen:
1
#if defined (STM32F401RE)                           // STM32F401 Nucleo Board with 8 MHz crystal (84 MHz)
2
#define PLL_M      8
3
#define PLL_N      336
4
#define PLL_P      4
5
#define PLL_Q      7
6
#elif defined (STM32F411RE)                         // STM32F411 Nucleo Board with 8 MHz crystal (100 MHz)
7
#define PLL_M      8
8
#define PLL_N      400
9
#define PLL_P      4
10
#define PLL_Q      7
11
#elif defined (STM32F407VG)                         // STM32F4 Discovery Board with 8 MHz crystal (168 MHz)
12
#define PLL_M      8
13
#define PLL_N      336
14
#define PLL_P      2
15
#define PLL_Q      7
16
#else
17
#error unknown STM32
18
#endif

Sind STM32F401RE, STM32F411RE oder STM32F407VG gesetzt, werden die 
jeweiligen µCs mit dem jeweils maximal möglichen Takt konfiguriert.

Die jeweiligen Werte kann man komfortabel mit dem Tool STM32CubeMX 
ermitteln. Den Rest muss man ja nicht nutzen.

: Bearbeitet durch Moderator
von Nico W. (nico_w)


Lesenswert?

Ich würde den PLL_M auf 4 setzen für weniger jitter. Siehe auch im RM 
oder Datenblatt. Bin mir grade nicht mehr so sicher wo das stand. Dann 
natürlich auch den PLL_N entsprechend anpassen (halbieren).

von Tycho B. (asellus)


Lesenswert?

In " Tips and warnings while programming the DMA controller" steht:
1
/* Program M0AR with QUADSPI data register address */
2
DMA2_Stream7->M0AR = (uint32_t)&QUADSPI->DR;
3
/* Program PAR with Buffer address */
4
DMA2_Stream7->PAR = (uint32_t)&u32Buffer[0];

Muss es nicht genau anders rum sein, also Peripherie an PAR und 
Pufferadresse an M0AR?

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.