Forum: Mikrocontroller und Digitale Elektronik STM32L100 zu Fuß


von Max G. (l0wside) Benutzerseite


Lesenswert?

Ich versuche gerade, mich auf der Basis der Doku auf Registerebene durch 
den STM32L100 zu kämpfen. Vermutlich gibt es sinnvollere Methoden, seine 
Zeit zu verbringen; andererseits ist es mir ganz sympathisch, wenn ich 
weiß, was der Rechner denn nun macht, anstatt das alles wie ein 
schlechtes Schnitzel unter einem Haufen Soße (=CMSIS, StdPeriphLib) zu 
ersäufen. Auf dem MSP430 bin ich auch auf Registerebene zu Potte 
gekommen.

Nur den NVIC-Code habe ich geklaut, deswegen ist dort StdPeriphLib-Code 
drin.


Ich habe es geschafft, die LEDs auf dem Demoboard (PORT C) blinken zu 
lassen. Mäßig schöner Code:
1
#include <stm32l1xx.h>
2
#include <stdint.h>
3
4
int main() {
5
    RCC->AHBENR  |=  RCC_AHBENR_GPIOCEN;
6
    RCC->APB1ENR |=  RCC_APB1ENR_TIM2EN;
7
    GPIOC->MODER   |=   (0x55555555);   /* General purpose output mode*/
8
    GPIOC->OSPEEDR |=   (0x55555555);   /* 2 MHz Low speed            */
9
    static int i;
10
11
12
13
    TIM2->PSC = 40000; // Prescaler
14
    TIM2->ARR = 500;   // Timer overflow value
15
    TIM2->EGR = 0x01; // Update generation
16
    TIM2->DIER |= 0x01; // Enable timer interrupt
17
    TIM2->CR1 |= 0x01; // Timer enabled
18
    RCC->AHBENR  |=  RCC_AHBENR_GPIOAEN;
19
20
    NVIC_InitTypeDef nvicStructure;
21
    nvicStructure.NVIC_IRQChannel = TIM2_IRQn;
22
    nvicStructure.NVIC_IRQChannelPreemptionPriority = 0;
23
    nvicStructure.NVIC_IRQChannelSubPriority = 1;
24
    nvicStructure.NVIC_IRQChannelCmd = ENABLE;
25
    NVIC_Init(&nvicStructure);
26
27
    unsigned char n = 0;
28
  while (1) {
29
    GPIOC->BSRRL = 1 << 9; /* all on */
30
    GPIOA->ODR = 0xFFFFFFFF;
31
    for (i=0; i<100000; ++i);
32
    GPIOC->BSRRH = 1 <<9; /* all off */
33
    GPIOA->ODR = 0;
34
    for (i=0; i<100000; ++i);
35
  }
36
}
37
38
volatile uint16_t led_status = 0;
39
40
void TIM2_IRQHandler() {
41
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
42
      led_status = !led_status;
43
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
44
        if (led_status) {
45
          GPIOC->BSRRL = (1 << 8);
46
        } else {
47
          GPIOC->BSRRH = (1 << 8);
48
        }
49
    }
50
}

Die Hauptschleife beackert also PC9, der Interrupt PC8. Das funktioniert 
:)

Wenn ich nun versuche, den Code so zu erweitern, dass der USART1 auch 
noch etwas tun darf, klappt das nicht. Checkliste:
* Peripheral Clocks für USART und für Port A einschalten
* PA9 auf Alternate Function schalten, als Alternate Function #7 = 
USART1_TX nehmen
* Baudrate für USART1 setzen
* USART Enable und TX Enable setzen
* Wert ins USART Data Register schreiben

In Code gegossen (obiger Code erweitert):
1
#include <stm32l1xx.h>
2
#include <stdint.h>
3
4
int main() {
5
  RCC->AHBENR  |=  RCC_AHBENR_GPIOCEN;
6
  RCC->APB1ENR |=  RCC_APB1ENR_TIM2EN;
7
  GPIOC->MODER   |=   (0x55555555);   /* General purpose output mode*/
8
  GPIOC->OSPEEDR |=   (0x55555555);   /* 2 MHz Low speed            */
9
  static int i;
10
11
12
13
  TIM2->PSC = 40000; // Prescaler
14
  TIM2->ARR = 500;   // Timer overflow value
15
  TIM2->EGR = 0x01; // Update generation
16
  TIM2->DIER |= 0x01; // Enable timer interrupt
17
  TIM2->CR1 |= 0x01; // Timer enabled
18
  RCC->AHBENR  |=  RCC_AHBENR_GPIOAEN;
19
  RCC->APB2ENR |=  RCC_APB2ENR_USART1EN;
20
  GPIOA->MODER  |= (2UL << 18) | (2UL << 20);
21
  GPIOA->AFR[1] |= (7UL << 4) | (7UL << 8);
22
  GPIOA->OSPEEDR =   0xFFFFFFFF;
23
  USART1->BRR = 0x341;
24
  USART1->CR1 |= USART_CR1_UE + USART_CR1_TE;
25
26
    NVIC_InitTypeDef nvicStructure;
27
    nvicStructure.NVIC_IRQChannel = TIM2_IRQn;
28
    nvicStructure.NVIC_IRQChannelPreemptionPriority = 0;
29
    nvicStructure.NVIC_IRQChannelSubPriority = 1;
30
    nvicStructure.NVIC_IRQChannelCmd = ENABLE;
31
    NVIC_Init(&nvicStructure);
32
33
    unsigned char n = 0;
34
  while (1) {
35
    GPIOC->BSRRL = 1 << 9; /* all on */
36
    GPIOA->ODR = 0xFFFFFFFF;
37
    for (i=0; i<100000; ++i);
38
    GPIOC->BSRRH = 1 <<9; /* all off */
39
    GPIOA->ODR = 0;
40
    for (i=0; i<100000; ++i);
41
    while (!(USART1->SR & USART_SR_TXE));
42
    USART1->DR = n;
43
    n++;
44
  }
45
}
46
47
volatile uint16_t led_status = 0;
48
49
void TIM2_IRQHandler() {
50
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
51
      led_status = !led_status;
52
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
53
        if (led_status) {
54
          GPIOC->BSRRL = (1 << 8);
55
        } else {
56
          GPIOC->BSRRH = (1 << 8);
57
        }
58
    }
59
}

Leider tut sich exakt gar nichts, der LA sieht nur dröhnendes Schweigen 
an PA9. Die LEDs blinken beide, die Abfrage while (!(USART1->SR & 
USART_SR_TXE)) bleibt nicht hängen.

Jemand eine Idee, wo ich noch schrauben kann (außer die Soßenlösung 
CMSIS/StdPeriphLib)?

Max

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

Max G. schrieb:
> GPIOA->ODR = 0xFFFFFFFF;
...
> GPIOA->ODR = 0;

Was ist denn die Idee hierbei eigentlich?

Und sind die Defines alle richtig gesetzt? Das hatte ich mal, als ich 
STM32 per Hand gemacht hatte. Sind die Bits bei AF1 usw. wirklich die 
korrekten? Alles nochmal nachkontrollieren.

Hängt der UART wirklich an APB2?

Sonst nächster Schritt, erstmal den TX als GPIO schalten und toggeln. 
Das sollte ja funktionieren.

von Mehmet K. (mkmk)


Lesenswert?

Max G. schrieb:
> Auf dem MSP430 bin ich auch auf Registerebene zu Potte gekommen.

Vermutlich nicht dasselbe wie ein STM32.
Ich mag es auch, wenn die Kontrolle soweit als möglich bei mir liegt. 
Aber ein Projekt gleich auf der Registerebene anzufangen finde ich keine 
so gute Idee. Und vorallem dann nicht, wenn man mit dem MCU Neuland 
betritt.
Ich an Deiner Stelle würde das Ganze mit der StdPeriphLib aufsetzen. 
Geht ja schnell. Und dann Schritt für Schritt diese Lib aussen vor 
lassen. Aber Du wirst sehen, dass manches von dieser Lib gar nicht mal 
so schlecht gehandhabt wird.

von Max G. (l0wside) Benutzerseite


Lesenswert?

Gefühlt ist der Unterschied hauptsächlich, dass die Register zum einen 
32 Bit breit sind und die Doku zu jedem Config-Register entsprechend 
lang, und dass man für die Peripherie die Clocks aktivieren muss, was 
man auf dem MSP430 nicht muss.
Aber vermutlich hast du Recht, und ich sollte es erst mal mit der 
StdPeriphLib versuchen. Fahrradfahren ist auch einfacher, wenn man 
vorher ein Laufrad hatte :)

Danke für die sachlichen Beiträge!

Max

von Max G. (l0wside) Benutzerseite


Lesenswert?

Nachtrag: mit Hilfe der StdPeriphLib funktioniert es jetzt. Fast: die 
Baudrate ist um den Faktor 2 daneben, aber das ist kein Desaster.

Wenn man mit dem Kopf schon durch die Wand will, sollte man vielleicht 
die Gipskartonwand statt der Betonwand nehmen...

Max

von Nico W. (nico_w)


Lesenswert?

Ich habe das damals mit dem MBED gemacht. Erstmal alles reingeladen und 
serial zum laufen bekommen und dann nach und nach den ganzen Ballast 
rausgelöscht.

https://github.com/Traumflug/Teacup_Firmware/commits/arm-stm32f411-port?page=2

Bei "STM32F411: get minimum to compile and upload" fängt der Spaß an. Is 
nen STM32F4 aber sollte ja analog beim deinem gehen.

Ob man dann mit der StdPeriphLib anfängt sollte auch keinen großen 
Unterschied machen. Mein kompletter Port von der Firmware für einen 
3D-Drucker ist dadurch ultra kompakt. Unter 20kB wenn ich mich recht 
erinnere.

Warum macht man das? Weil man viel lernt und es Spaß gemacht hat und es 
sau schnell ist.

: Bearbeitet durch User
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.