Forum: Mikrocontroller und Digitale Elektronik STM32 ST-Library Pro oder Kontra


von Juergen (Gast)


Lesenswert?

Hallo miteinander,

den Einstieg in die STM32 Welt habe ich erst mal geschafft. Als 
Testboard dient das Discovery zusammen mit Yagarto und traditionellen 
makefile's. Zum Debuggen nehme ich den st-link gdb server und Eclipse. 
Die lite Version von Atollic habe ich auch probiert, ist mir aber doch 
zu sehr beschränkt.
Allerdings stehe ich mit den STM32F10x_StdPeriph_Driver'n ein bischen 
auf Kriegsfuß. Am Anfang sieht das ja ganz nett aus. Wenn man dann aber 
die Doku zum Stm32 durcharbeitet lernt man die Register kennen. Und 
sieht sie auch schön im Debugger. Wenn ich einfache Ports z.B. mit der 
Lib programiere, dann fehlt mir irgendwo am Ende die Sicht auf die 
Register. Und der Code wird auch aufgebläht. Das ist mir jedenfalls zu 
fett in einigen Beispielen, wenn für die 2 Leds neben der Lib auch noch 
die STM32vldiscovery.c benutzt wird. Da sieht man dann die Leds schön 
blinken, zum Verständnis des Controllers trägt das aber nicht bei. 
Deshalb hier die Frage: Programmieren alle nur mit dieser Lib oder gibt 
es auch Verweigerer? Was ist ohne diese Lib nur umständlich zu lösen?

von ich (Gast)


Lesenswert?

Was fragst du hier nach? Du hast doch für dich schon entschieden, das 
dir die LIBs nicht gefallen. Also können jetz 1000 Leute schreiben 
"Mensch sind die toll, weil [...]"...du würdest bei deiner Meinung 
bleiben.

Ansonsten gibts hier auch ne Suche...

von (prx) A. K. (prx)


Lesenswert?

Juergen schrieb:

> Allerdings stehe ich mit den STM32F10x_StdPeriph_Driver'n ein bischen
> auf Kriegsfuß. [...]

Damit bist du nicht allein. Die Lib führt zudem zu mehr erforderlicher 
Doku, denn mit der Doku der Lib ist es meist nicht getan, die 
Register/Funktions-Doku braucht man trotzdem.

> es auch Verweigerer? Was ist ohne diese Lib nur umständlich zu lösen?

In manchen Fällen kann man sie als Hinweis nehmen. Als Beispielcode. Und 
bei sehr komplexer Peripherie wie CAN oder USB kann sie nützlich sein.

von Juergen (Gast)


Lesenswert?

Also, immer schön ruhig bleiben. Ich habe gerade den Einstieg durch und 
nicht die ganze Peripherie. Meine Entscheidung ist auch noch nicht 
endgültig getroffen. Und meine Frage war: "Was ist ohne diese Lib nur 
umständlich zu lösen". Ich dachte ich kann von den Erfahrungen anderer 
lernen ohne angemacht zu werden!

von (prx) A. K. (prx)


Angehängte Dateien:

Lesenswert?

Die Lib hat mit ihrer Favorisierung von Structs eine Aufrufkonvention, 
die wohl geistig von anderen Controllern wie 8-Bittern abstammt, die 
aber bei ARMs umständlich, zeitraubend und zeilenfressend ist.

So erschliesst sich mir nicht, weshalb man für GPIO-Inits derart viele 
Zeilen benötigt, wie sie die Lib frisst. Sieht bei mir so aus:
1
    gpio_config(GPIOB, 1<<9,        GPIO_Mode_Input       | GPIO_Input_Pullup);
2
    gpio_config(GPIOB, 1<<12,       GPIO_Mode_Output50MHz | GPIO_Output_PushPull);
3
    gpio_config(GPIOB, 1<<13|1<<15, GPIO_Mode_Output50MHz | GPIO_AltOut_PushPull);
4
    gpio_config(GPIOB, 1<<14,       GPIO_Mode_Input       | GPIO_Input_Pullup);

von Juergen (Gast)


Lesenswert?

Sehr schönes Beispiel wie es einfacher und verständlicher geht!

von Random .. (thorstendb) Benutzerseite


Lesenswert?

so mach ichs:

Headerfile gpio.h:
1
// AF enable
2
#define AFIO_EN    0  // Alternate Function Enable
3
4
// GPIO enable
5
#define IOPA_EN    2  // GPIO A enable
6
#define IOPB_EN    3  // GPIO B enable
7
#define IOPC_EN    4  // GPIO C enable
8
#define IOPD_EN    5  // GPIO D enable
9
#define IOPE_EN    6  // GPIO E enable
10
#define IOPF_EN    7  // GPIO F enable
11
#define IOPG_EN    8  // GPIO G enable
12
13
// DAC Enable
14
#define DAC_EN    29
15
16
17
18
#define GPIO_CONF_BIT(BIT)  ((BIT>7? BIT-8 : BIT) << 2)    // 4Bits per port pin
19
20
// Mode and Conf Bits
21
#define MODE0          (unsigned int)0
22
#define MODE1          (unsigned int)1
23
#define CONF0          (unsigned int)2
24
#define CONF1          (unsigned int)3
25
26
27
// Port Mode
28
#define GPIO_MODE_INPUT        (((unsigned int)0<<MODE0) | ((unsigned int)0<<MODE1))    // GPIO is input
29
#define GPIO_SPEED_2MHZ        (((unsigned int)0<<MODE0) | ((unsigned int)1<<MODE1))    // Max output Speed  2MHz
30
#define GPIO_SPEED_10MHZ      (((unsigned int)1<<MODE0) | ((unsigned int)0<<MODE1))    // Max output Speed 10MHz
31
#define GPIO_SPEED_50MHZ      (((unsigned int)1<<MODE0) | ((unsigned int)1<<MODE1))    // Max output Speed 50MHz
32
33
// Port Conf
34
#define GPIO_OUT_PUSH_PULL    (((unsigned int)0<<CONF0) | ((unsigned int)0<<CONF1))    // general purpose output push-pull
35
36
#define GPIO_AF_PUSHPULL      (((unsigned int)0<<CONF0) | ((unsigned int)1<<CONF1))   // alternate function push-pull
37
38
#define GPIO_IN_FLOATING      (((unsigned int)1<<CONF0) | ((unsigned int)0<<CONF1))   // input floating
39
#define GPIO_IN_ANALOG        (((unsigned int)0<<CONF0) | ((unsigned int)0<<CONF1))   // input analog
40
41
#define GPIO_IN_PULL_DOWN     (((unsigned int)0<<CONF0) | ((unsigned int)1<<CONF1))   // alternate function push-pull
42
#define GPIO_IN_PULL_UP       (((unsigned int)0<<CONF0) | ((unsigned int)1<<CONF1))   // alternate function push-pull

LED.c:
1
int led_init(void)
2
{  
3
  bit_on(RCC->APB2ENR, IOPB_EN);      // enable PORT B
4
  
5
  GPIOB->CRH = 
6
  ((GPIO_OUT_PUSH_PULL | GPIO_SPEED_2MHZ) << GPIO_CONF_BIT(8))  | \
7
  ((GPIO_OUT_PUSH_PULL | GPIO_SPEED_2MHZ) << GPIO_CONF_BIT(9))  | \
8
  ((GPIO_OUT_PUSH_PULL | GPIO_SPEED_2MHZ) << GPIO_CONF_BIT(10)) | \
9
  ((GPIO_OUT_PUSH_PULL | GPIO_SPEED_2MHZ) << GPIO_CONF_BIT(11)) | \
10
  ((GPIO_OUT_PUSH_PULL | GPIO_SPEED_2MHZ) << GPIO_CONF_BIT(12)) | \
11
  ((GPIO_OUT_PUSH_PULL | GPIO_SPEED_2MHZ) << GPIO_CONF_BIT(13)) | \
12
  ((GPIO_OUT_PUSH_PULL | GPIO_SPEED_2MHZ) << GPIO_CONF_BIT(14)) | \
13
  ((GPIO_OUT_PUSH_PULL | GPIO_SPEED_2MHZ) << GPIO_CONF_BIT(15));
14
15
  return(0);
16
}

Übersichtlich, kurz, kompakt, wartbar, ohne Driver Library, und man kann 
es leicht in andere Projekte übernehmen ;-)

Fertige Libs verwende ich nur bei komplexen Sachen wie TCP/IP, USB, 
SD-Karte.


VG,
/th.

von (prx) A. K. (prx)


Lesenswert?

Mit dem Ansatz hätte ich ein Problem, denn der funktioniert nur, wenn 
man sämtliche GPIO-Configs eines Ports an einer Stelle zusammen zieht, 
auch wenn die Pins zu 5 verschiedenen Funktionsmodulen gehören.

Will man das hingegen modularisieren, d.h. die Port-Inits in das Modul 
packen, das die jeweiligen Pins tatsächlich verwendet, dann funktioniert 
dieser Ansatz nicht mehr so einfach.

Ausserdem erschwert es symbolische Namen für die Portpins.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

funzt auch ausserhalb noch genau so gut:
1
GPIOB->CRH |= 
2
  (GPIO_OUT_PUSH_PULL | GPIO_SPEED_2MHZ) << GPIO_CONF_BIT(2));

und ebenfalls mit symbolischen Namen:
1
#define LED_PORT      GPIOB->ODR
2
#define PIN14   14
3
#define LED3    PIN14
4
5
GPIOB->CRH |= 
6
  (GPIO_OUT_PUSH_PULL | GPIO_SPEED_2MHZ) << GPIO_CONF_BIT(LED3));
7
8
LED_PORT |= (1<<LED3);

;-)

Besonderheit bei STM ist, dass du immer 4 Bits (2Mode, 2Conf) in einem 
CRL und einem CRH hast für einen 16Bit Port.

VG,
/th.

von (prx) A. K. (prx)


Lesenswert?

Random ... schrieb:

> funzt auch ausserhalb noch genau so gut:

Das setzt voraus, dass in CRx vorher 0 drin steht. Tut es ab Reset aber 
nicht.

> und ebenfalls mit symbolischen Namen:

Nicht ganz, denn die Unterscheidung CRH/CRL muss da noch rein, abhängig 
von der Bitnummer. Sonst ist der Ärger vorprogrammiert.

Deine ODR-Manipulation ist nicht Interrupt-fest. BSRR/BRR statt ODR 
empfohlen.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

> Das setzt voraus, dass in CRx vorher 0 drin steht.
> Tut es ab Reset aber nicht.

gnarff das hab ich mir jetzt verkniffen, weil ich es als bekannt 
vorrausgesetzt hab und es nur ums Prinzip ging:
1
GPIOB->CRH &= ~(0xf << GPIO_CONF_BIT(LED3));
2
GPIOB->CRH |= 
3
  (GPIO_OUT_PUSH_PULL | GPIO_SPEED_2MHZ) << GPIO_CONF_BIT(LED3));

> Nicht ganz, denn die Unterscheidung CRH/CRL muss da noch rein,
> abhängig von der Bitnummer.
Klar, etwas denken muss man dabei noch, das nimmt einem auch die beste 
DriverLib nicht ab!
Dann sollte man da aber eher nen Function call draus machen, mit 
if(pin<8) {CRL=...} else {CRH=...}. Diese Transferleistung hab ich aber 
den Lesern dieses Threads überlassen, ich wollte nur auf die 
Möglichkeiten hinweisen.
Ausserdem kann man mit dieser Lösung (mit etwas Nachdenken) alle 
benötigten PINs (nach CRL/H, ob nun Eingang, Ausgang oder 
AlternateFunction) eines Ports in einem Schwung gemeinsam setzen, 
während eine Unterscheidung nach CRL und CRH nur das Setzen eines 
Portpins zulassen würde (vielleicht einer Liste, das wäre dann eine 
Schleife).
Mit dieser Lösung kann man das sehr geschickt und übersichtlich bündeln.
Z.B. brauche ich für SPI 4 Pins, 2 als GPIO OUT und 2 als 
AlternateFunction (MOSI, SCK), alle aus CRH.


> Deine ODR-Manipulation ist nicht Interrupt-fest.
> BSRR/BRR statt ODR empfohlen.
Warum ist ein 16Bit Zugriff auf dem Cortex-M3 nicht Interrupt-fest?

von Matze T. (gruetzwurschd)


Lesenswert?

Hallo Jürgen,

wahrscheinlich hat kaum einer (öffentlich) mehr über diese Lib geflucht 
als ich. (siehe hier: Beitrag "Literatur für ARM-CORTEX-M3" )

Im endeffekt mach ich es mittlerweile so, dass ich die Lib hernehme um 
etwas zum laufen zu bringen und nach und nach dann den code selber 
nachschreibe. Um es kurz zu sagen, für erste schritte ganz okay, aber 
für mehr würde ich die nicht verwenden.

Gruß Tarkan

von Juergen (Gast)


Lesenswert?

Vielen Dank an alle. Ich denke ich werde die lib nur zum Lernen benutzen 
und beim Selberschreiben der Register den STM32 am besten kennen lernen.

Interessant wäre es ja, wenn es für die häufigsten Aufgaben (GPIO, 
USART, SPI) eine Lib geben würde die sowohl den stm32 als auch andere 
Cortex M3 Controller unterstützt.

von Mehmet K. (mkmk)


Lesenswert?

Tarkan D. schrieb:
> Im endeffekt mach ich es mittlerweile so, dass ich die Lib hernehme um
> etwas zum laufen zu bringen und nach und nach dann den code selber
> nachschreibe.

Ich mache es genauso.

von Mehmet K. (mkmk)


Lesenswert?

Ich habe jetzt mal den Code von A.K. etwas studiert.
Sehr clever. Werde ich mir merken!!

von (prx) A. K. (prx)


Lesenswert?

Random ... schrieb:

> Warum ist ein 16Bit Zugriff auf dem Cortex-M3 nicht Interrupt-fest?

Weil
   ODR |= 0x01
nicht ein Befehl ist, sondern bezogen auf ODR zwei:
   Ra = ODR
   Ra |= 0x01
   ODR = Ra.
Das sieht in der Praxis irgendwann so aus:
   R1 = ODR        ; Bit 0,1=0
   R1 |= 0x01
      R2 = ODR     ; ISR setzt Bit 1
      R2 |= 0x02
      ODR = R2
   ODR = R1        ; Bit 0=1, Bit 1=0
Bit 1 ist nun wieder im Zustand vor der ISR.

von (prx) A. K. (prx)


Lesenswert?

Mehmet Kendi schrieb:

> Ich habe jetzt mal den Code von A.K. etwas studiert.
> Sehr clever. Werde ich mir merken!!

Wenn man diese Funktionen inlined, dann dampft zumindest GCC sie zudem 
pro Aufruf netto auf diese 4 Befehle
   Ra = CRx
   Ra &= const1
   Ra |= const2
   CRx = Ra
ein. Was auch nicht länger ist als der Aufruf.

von (prx) A. K. (prx)


Lesenswert?

Juergen schrieb:

> Interessant wäre es ja, wenn es für die häufigsten Aufgaben (GPIO,
> USART, SPI) eine Lib geben würde die sowohl den stm32 als auch andere
> Cortex M3 Controller unterstützt.

Da die UARTs verschiedener Familien verschieden sind, sind die 
Implementierungen von Treibern für die UARTs notwendigerweise auch 
verschieden. Gemeinsam können sie nur die Schnittstelle nach aussen 
haben. Und da liegt der Hase im Pfeffer: Wie muss diese aussehen? 
Darüber würden sich Komitees so lange streiten, bis ein Monster 
rauskommt, das niemand verwendet.

Folglich gibts sowas öffentlich de fakto nur auf sehr hoher Ebene in 
Form von Betriebssystemen.

ST hätte das allerdings für deren verschiedene Controller-Familien wie 
STM8 und STM32 machen können (sollen), das hätte sich m.E. als 
Argument angeboten. Ist natürlich nur mit begrenzter Grundfunktion 
sinnvoll, damit alle Hardware-Funktionen voll abzudecken wäre zu gross 
und zu unübersichlich geworden, aber grad bei UART gibts eine klare 
Grundfunktionalität. Die vorhandenenen Libs sind ja wenig mehr als 
Wrapper um die Register, ein relativ übersichtlicher UART-Support mit 
Pufferung und ISR hätte sich da angeboten.

Was nicht heisst, dass man sich das nicht selber stricken kann. So mache 
ich das. Wie beispielsweise ein Treiber für UART, mit sehr ähnlichem 
Interface bei LPC2000, STR9, STM32. Ebenso CAN für AVR/MCP2515, LPC2000, 
PIC16, STM32.

Das lohnt sich aber nur, wenn sich ein einheitliches Interface nach oben 
anbietet und die Varianz der Funktionalität der Hardware sich verbergen 
lässt. Bei SPI scheint mit das eher zweifelhaft. Auch bei GPIO lohnt 
sich das eigentlich nur für die Port-Init. Bei CAN lassen sich die sehr 
unterschiedlichen Komplixitäten bei der Filterung aber nicht gut 
verbergen.

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.