Forum: Mikrocontroller und Digitale Elektronik STM32F030F4P6 und USART1


von Ralph S. (jjflash)


Angehängte Dateien:

Lesenswert?

Ich bin glaub ich zu doof oder schlicht betriebsblind.

Ich bekomme USART1 auf dem STM32F030F4P6 einfach nicht hin ... und ich 
weiß nicht warum.

Gefühlt hab ich die entsprechenden Abschnitte im Datenblatt bereits 20 
mal gelesen ...

Als serielle Schnittstellenpins sollen PA9 und PA10 verwendet werden. 
Auch die Überlegung, dass die Portpins des Chips hinüber sind (immerhin 
hab den 20 pol. SMD Chip auf Adapter gelötet und kann immer was 
passieren) glaub ich nicht, weil ich auf den Pins eine LED blinken 
lassen kann.

Ich finde hier den Fehler einfach nicht:
1
#include "stm32f0xx.h"
2
#include "stm32f0xx_rcc.h"
3
#include "stm32f0xx_gpio.h"
4
#include "stm32f0xx_usart.h"
5
6
inline void delay_ms(uint32_t dtime)
7
{
8
  volatile uint32_t n = (dtime * 3750);
9
  while(n > 0) n--;
10
}
11
    
12
void serial_init(void)
13
{
14
    GPIO_InitTypeDef GPIO_InitStructure;
15
    USART_InitTypeDef USART_InitStructure;
16
17
    USART_InitStructure.USART_BaudRate = 19200;
18
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
19
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
20
    USART_InitStructure.USART_Parity = USART_Parity_No;
21
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
22
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
23
24
    // Enable GPIO clock
25
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
26
27
    // Enable USART clock
28
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
29
30
    // PA9 fuer USART1_Tx
31
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1);
32
33
    // PA10 fuer USART1 Rx
34
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1);
35
36
    // USART Tx und Rx als alternate function push-pull
37
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
38
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
39
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
40
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
41
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
42
    GPIO_Init(GPIOA, &GPIO_InitStructure);
43
44
    // USART1 Register konfigurieren
45
    USART_Init(USART1, &USART_InitStructure);
46
47
    // USART1 starten
48
    USART_Cmd(USART1, ENABLE);
49
}
50
51
void serial_putchar(uint8_t ch)
52
{
53
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
54
    USART_SendData(USART1, ch);
55
}
56
57
int main(void)
58
{
59
60
  serial_init();
61
        
62
  while(1)
63
  {
64
    serial_putchar('x');    
65
  }
66
}

Außer dass die Pins PA9 und PA10 permanente High-Pegel (mit Scope 
kontrolliert) haben, geschieht nix.

Hardware:

BOOT0 ==> GND
OSC_IN und OSC_OUT ==> offen (kein Quarz)
NRST ==> +3,3V
VDDA ==> +3,3V (mit 47 uF gegen GND)
VDD ==> +3,3V (mit 10 uF gegen GND)

Upload-Tool: Chinaclone ST-Link-V2 oder ST-Link eines Nucleo-Boards

Wäre nett wenn mir auf die Sprünge geholfen werden könnte, im Anhang 
alle in der Toolchain verwendeten Dateien.

von Uwe Bonnes (Gast)


Lesenswert?

Wer schaltet die GPIO Clock ein? Vermutlich GPIO_Init(). Dann geht 
GPIO_PinAFConfig() zuvor ins Leere...

von Ralph S. (jjflash)


Lesenswert?

... ich dachte ich schalte den Clock hiermit ein:

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

Bin ich jetzt vollkommen daneben? (Ich werde mir das Startupfile noch 
mal ansehen)

von Jim M. (turboj)


Lesenswert?

Äh, den USART RX möchte man als Input haben. Da ist Push-Pull Output 
IMHO falsch.

Der Stack wird im Linker Skript auf 0x20000FFF gesetzt, laut ARM ABI 
muss der aber durch 8 teilbar sein, 0x20001000 wäre besser. Das könnte 
Dir mit (Hard) Fault aussteigen, lass mal eine LED blinken.

Ich habe im Hard Fault Handler immer eine spezielle LED Blink Sequenz 
drin, damit ich Programmierfehler gleich mitbekomme.

von Ralph S. (jjflash)


Lesenswert?

... im Hardfault-Handler LED blinken lassen ist gute Idee, das werde ich 
machen.

Den Stack im Linker hatte ich absichtlich auf 0x20000fff weil das die 
höchste Adresse des Ram's ist... werde ich wieder auf 0x20001000 ändern.

Hmmm, den RX hatte ich ursprünglich auf Input gehabt (und wieder 
geändert) weil ich grundsätzlich erst einmal "etwas sehen" wollte, also 
erst einmal nur schicken (also Ausgang).

Irgendwie ist der Wurm drin und ich bin knapp dran, mir ein Discovery 
oder ein Nucleo-Board mit STM32F0xx zu kaufen um dann mal mit 
"offiziellen" Beispielen zu spielen und zu vergleichen.

Grundsätzlich versteh ich aber nicht wo der Hund begraben liegt.

Dennoch vielen Dank, die Idee im Hardfault es blinken zu lassen als 
Anzeige dass was falsch läuft ist gut !

von Termi (Gast)


Lesenswert?

Fehlt wohl noch die SystemInit();

zumindest im geposteten Code.

G Termi

von Termi (Gast)


Lesenswert?

ach und eine kleine Pause nach dem senden von 'x' würde ich auch 
einbauen.

von chris (Gast)


Angehängte Dateien:

Lesenswert?

Hi,

ich hab leider grade keine Zeit, deinen Code genauer zu studieren, aber 
im Anhang findest du mal eine funktionierende UART-Init etc.
Das ganze basiert auf der UART-Lib von hier:
http://mikrocontroller.bplaced.net/wordpress/

Ich hab das notwendige einigermaßen angepasst auf den STM32F0


Inhalt der main():
1
UB_Uart_Init();
2
uart_puts(COM1, "Hallo UART!", CRLF);


lg
Chris

von Little B. (lil-b)


Lesenswert?

Uwe Bonnes schrieb:
> Wer schaltet die GPIO Clock ein? Vermutlich GPIO_Init(). Dann geht
> GPIO_PinAFConfig() zuvor ins Leere...

Falsch

Ralph S. schrieb:
> ... ich dachte ich schalte den Clock hiermit ein:
>
>     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

Richtig

Jim M. schrieb:
> Äh, den USART RX möchte man als Input haben. Da ist Push-Pull Output
> IMHO falsch.

Irrelevant, da die Ports als Alternate Function konfiguriert werden.

Termi schrieb:
> Fehlt wohl noch die SystemInit();

Wird in der Startup.s aufgerufen

Termi schrieb:
> ach und eine kleine Pause nach dem senden von 'x' würde ich auch
> einbauen.

Braucht es nicht, da auf freien Buffer gewartet wird:

Ralph S. schrieb:
> while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);

Ich habe mir dein Programm nun eine weile angeschaut, die 
Initialisierung passt und es scheint nichts zu fehlen.
Du hast doch einen Debugger. Sag doch mal, was in den USART1-Registern 
steht (Adresse 0x40013800 bis 0x4001382B) und in den GPIOA-Registern 
(Adresse 0x48000000 bis 0x4800002B). Dann sieht man evtl eine falsche 
konfiguration.

von W.S. (Gast)


Lesenswert?

Ralph S. schrieb:
> Irgendwie ist der Wurm drin und ich bin knapp dran, mir ein Discovery
> oder ein Nucleo-Board mit STM32F0xx

Ja, da ist tatsächlich der Wurm drin. Aber nicht im Prozessor, sondern 
in deinem Programm.

Ich hab schon so unsäglich oft den Leuten gepredigt, daß sie ihre 
verdammten Konfigurationen selber machen sollen und zu diesem Zwecke 
die im RefMan genannten Register in gehöriger Weise SELBST setzen 
mögen. Aber dann lese ich als Allererstes bei dir

USART_InitStructure.USART_BaudRate = ...

und bin wieder mal ... grmpff. Kannst du das nicht komplett bleiben 
lassen??

Hier mal ein Auszug au einem anderen Programm:
1
  // erstmal die benötigten Takte einschalten
2
  RCC_AHBENR  = (1<<17) |   // Port A enable
3
                (1<<18) |   // Port B enable
4
                (1<<19) |   // Port C enable
5
          (1<<20) |   // Port D enable
6
          (1<<22);    // Port F enable
7
8
  RCC_APB2ENR = (1<<16) |   // TIM15 enable
9
                (1<<14) |   // USART1 enable
10
     1;         // Sys Cfgen
11
12
  RCC_APB1ENR = (1<<23) |   // USB
13
                (1<<21) |   // I2C1
14
                (1<<17) |   // USART2 enable
15
                (1<<2)  |   // TIM4 enable
16
                (1<<1)  |   // TIM3 enable
17
                 1;         // TIM2 enable

Jetzt noch ein Auszug, wegen des Takt-Aufsetzens:
1
  // USB erstmal passiv schalten
2
  GPIOA_BSRR = (1<<15);     /* PA15 von Hand auf High setzen wg. Transistor + 1k5 an D+ */
3
4
  monitor(1);
5
6
  // Takt aufsetzen: System = 72 MHz
7
  RCC_CIR = 0;                  // keine Interrupts für PLL usw.
8
  RCC_CR = 0x83 | (1<<16);      // Resetwerte, interner 8 MHz RC-Oszi ein und Quarzoszi ein
9
10
  while ((RCC_CR & 2)==0)       // warten auf ready vom RC-Oszi
11
  { monitor(2); }
12
13
  // RCC_CFGR vorläufig setzen
14
  RCC_CFGR = (7<<24)   |        // 7 = PLL:2 an MCO = PA.8 zur Beobachtung per Oszillograf
15
             (1<<22);           // USBPRES, vermutlich 72MHz/1.5-->48MHz
16
17
  // so, eigentlich sollte jetzt der interne 8 MHz RC-Oszi den Takt angeben
18
  RCC_CR   &= ~(1<<24);         // PLL aus, um sie einstellen zu können
19
  RCC_CFGR2 = 0;                // Clock für ADC aus, Vorteiler für PLL auf 1:1
20
  RCC_CFGR3 =  (1<<22) |        // Clock für Timer + UART's auf SysClk Takt
21
               (1<<20) |
22
         (1<<18) |
23
         (1<<16) |
24
          1;
25
26
  // RCC_CFGR fertig setzen bis auf PLL-Auswahl
27
  RCC_CFGR  =  (1<<16) |        // Quarz-Oszi als Input für PLL
28
               (4<<11) |        // APB2 Vorteiler auf :2 (72-->36 MHz)
29
               (4<<8)  |        // APB1 Vorteiler auf :2 (72-->36 MHz)
30
               (0<<17) |        // Quarz nicht :2 teilen vor der PLL
31
         (4<<18) |        // PLL Teiler: 4--> x6, also 12MHz*6-->72MHz
32
               (7<<24) |        // 7 = PLL:2 an MCO = PA.8 zur Beobachtung
33
               (1<<22) |        // USBPRES, vermutlich 72MHz/1.5-->48MHz
34
          0;
35
36
  // warten auf Quarz-Oszillator
37
  while (!(RCC_CR & (1<<17)))   // warten auf ready vom ext. Quarz-Oszi
38
  { monitor(3); }
39
40
  // PLL einschalten und warten auf Einschwingen
41
  RCC_CR |= (1<<24);            // PLL einschalten
42
  while (!(RCC_CR & (1<<25)))   // warten auf ready von PLL
43
  { monitor(4); --i; }
44
45
  // wichtig: die nötigen Waitstates einschalten
46
  FLASH_ACR = 0x32;             // 2 Waitstates ab 48 MHz
47
48
  // Finale
49
  RCC_CFGR = (RCC_CFGR & 0xFFFFFF00) | 2;   // mutig die PLL als Systemtakt einschalten
50
51
  monitor(5);
52
53
  // Restbedenken: an MCO = PA.8 liegen jetzt 36 MHz an.
54
  // Man kann dies jetzt oszillografieren, sollte es aber dann abschalten,
55
  // falle es nicht wirklich gebraucht wird
56
  // Alternative: MCO erst garnicht einschalten
57
58
}
59
60
 /* ende */

Und noch ein Auszug, diesmal vom UART-setup
1
dword InitSerial1 (long baudrate)    /* liefert tatsächliche Baudrate zurück */
2
{ long Baudteiler;
3
4
  Baudteiler = F_Platform / baudrate; /* SysClk Frequenz */
5
  U1Buf.InWP =                      /* alle Puffer rücksetzen */
6
  U1Buf.InRP =
7
  U1Buf.OutWP =
8
  U1Buf.OutRP = 0;
9
  /* Int 37 */
10
  NVIC_ICPR1 = (1<<(U1Int-32));     /* pending löschen */
11
  NVIC_ISER1 = (1<<(U1Int-32));     /* Int erlauben */
12
  USART1_CR1 = 0;                   /* erstmal aus */
13
  USART1_CR2 = (3<<12);             /* 1.5 Stopbits */
14
  USART1_CR3 = 0;
15
  USART1_GTPR = 0;
16
  USART1_BRR = Baudteiler & 0xFFFF; /* Baudratenteiler */
17
  USART1_CR1 = (1<<7)  |            /* TX Int enable */
18
               (1<<5)  |            /* RX Int enable */
19
         (1<<3)  |            /* TX enable     */
20
               (1<<2)  |            /* RX enable     */
21
               (1<<0);              /* UART enable   */
22
  return F_Platform / Baudteiler;
23
}

So. den Interrupthandler wirst du ja hoffentlich zuwege bringen, ebenso 
die Basis-Funktionalität, z der ich hier nur den Header poste - sonst 
wird's für den Rest der Welt zu langweilig:
1
extern dword InitSerial1 (long baudrate);    /* liefert tatsächliche Baudrate zurück */
2
extern char  V24Char_Out1  (char c);         /* Zeichen senden */
3
extern int   V24numTxFree1 (void);           /* Anzahl freier Plätze im Sendepuffer */
4
extern bool  V24RxAvail1   (void);           /* ob Zeichen empfangen wurden */
5
extern char  V24GetChar1   (void);           /* empfangene Zeichen abholen */
6
extern void  V24TxDone1    (void);           /* warten bis Sendepuffer geleert ist */

So. Jetzt fang an, es richtig zu machen - und zwar so, daß du weißt, was 
tatsächlich abgeht - bei dieser elenden ST-Lib weißt du es nämlich 
nicht, was du ganz weit oben bereits deutlich gezeigt hast.

W.S.

von Ralph S. (jjflash)


Lesenswert?

W.S. schrieb:
> So. Jetzt fang an, es richtig zu machen - und zwar so, daß du weißt, was
> tatsächlich abgeht - bei dieser elenden ST-Lib weißt du es nämlich
> nicht, was du ganz weit oben bereits deutlich gezeigt hast.

... dann setz ich mich mal hin und wühl mich durch die Register... was 
ich eigentlich vermeiden wollte. Hrmpf... (aber letztlich hat der W.S. 
Gast wohl recht... und wenn ich überleg wieviel Zeit draufgegangen ist, 
hätte ich wohl gleich die Register nehmen sollen)...

Wird aber wohl auch n bissi dauern ...

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.