1. STM32 Projekt mit STM32CubeIDE
Ich verfüge unter anderem über ein NUCLEO-F446ZE. Dieses werde ich in einer kleinen Serie zuerst mit dem STM32CubeIDE in Betrieb nehmen und ein kleines Blink-Projekt erstellen und debuggen. Im weiterem Verlauf soll dann zuerst auf das STM32CubeIDE verzichtet werden und durch das Visual Studio Code eingesetzt werden. Da ich grundsätzlich mit git, einem Version Control System arbeite, plane ich im weiteren Verlauf, die STM32 HAL und die CMSIS nicht als Kopie sondern als Submodul einzubinden.
Danach wird geprüft in wie weit man mit C++ statt mit C entwickeln kann und die STM32 HAL durch eine eigene C++-HAL ersetzt werden.
Aber fangen wir ersteinmal vorne an.
Die Schritte in diesem Tutorial sind auch auf GitHub verfügbar. STM32FirstSteps
Neues Projekt in STM32CubeIDE anlegen
Funktioniert sehr gradlinig Erklärung nur der Vollständigkeit halber. Mit New
-> STM32 Project
ein neues Projekt anlegen und Namen eingeben. Im Target Selector
kann der verwendete Controller ausgewählt werden, es kann jedoch auch direkt das verwendete Board von STM ausgewählt werden. Dies erspart die Konfiguration der Pins.
In den Targeted Languages habe ich C++
angegeben, das Projekt wird ein Executable
und als Typ habe ich STM32Cube
ausgewählt.
Auf der nächsten Seite wird das Framework Package ausgewählt und man kann einstellen wie die library files eingebunden werden. Da ich sie später via git submodules eh anders einbinden will, wähle ich hier Copy only necessary library files
. ZUm Abschluss wird abgefragt ob man alle Peripherie im default mode
initialisieren will. Das will ich.
Im Anschluss öffnet sich das Projekt und die Pinout & Configuration
. Hier kann die Pin Belegung nachvollzogen und geändert werden.
Neben Pinout & Configuration
gibt es zahlreiche weitere Einstellungen. Besonders praktisch finde ich Clock Configuration
, da hier der clock tree des µC nachvollzogen und eingestellt werden kann. Bei Änderungen erscheint im Projekt Explorer
inter der *.ioc -Datei der Hinweis [Code Generation is required]
. Diese kann im Reiter Project
oder per Linksklick auf die *.ioc-Datei gestartet werden.
Debuggen
Das Debuggen wird mit dem Klick auf den Käfer Toolbar-Knopf gestartet. Debug button
Der Build-Prozess startet und STM32CubeIDE will daraufhin eine neue Ansicht öffnen, die spezielle für das Debuggen optimiert wurde. Ich stimme dem zu. Daraufhin (wenn man das Board angeschlossen hat) wird die main.c
Datei geöffnet und eine grüne Hervorhebung zeigt an, an welcher Stelle das Programm gestoppt wurde.
Debugger hält an erster Instruction der main function
Mit Step Into
(F5
) , Step Over
(F6
), Resume
(F85
) und Suspend
kann der Debugger gesteuert werden. Mit Terminate
(STRG
+ F2
) kann das Debuggen beendet werden.
Das erste eigene Programm schreiben
Das erste eigene Programm soll eine LED blinken lassen. Daher schaue ich mir zuerst die main
-Funktion in der main.c
-Datei an.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART3_UART_Init();
MX_USB_OTG_FS_PCD_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
Die main
-Funktion besteht aus Anweisungen zum Initialisieren des Boards und einer abschließenden endlosen while
-Schleife. Diese ist bei einem frisch erstelltem Projekt jedoch leer. Funktionsaufrufe, die mit HAL_
beginnen, rufen Funktionen aus der HAL-Bibliothek auf.MX_
kennzeichnet Funktionen, die von der Codegenerierung erstellt wurden Ein Überrest des STM32CubeMX? SystemClock_Config
wird ebenfalls von der Codegenerierung erzeugt, hat jedoch keinen Prefix.
Die Funktion ist von USER CODE
Kommentare durchsetzt. Code zwischen BEGIN
und END
bleibt bei einer erneuten Codegenerierung erhalten, Code außerhalb der Blöcke wird überschrieben.
Um das Ziel “Blinken einer LED” zu erreichen, füge ich deshalb folgendes in den USER CODE
ein, so dass die while-Schleife wie folgt aussieht.
1
2
3
4
5
6
7
8
9
10
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_GPIO_TogglePin(LD1_GPIO_Port, LD1_Pin);
HAL_Delay(500);
}
/* USER CODE END 3 */
Wird der Debugger nun gestartet, wird beim Durchsteppen die LD1 aktiviert bzw. deaktiviert. Verlässt man den Step-Betrieb, blinkt die LD1 in einem 1Hz Takt.
Weiterführende Links
- From Zero to main(): Bare metal C
Der Beginn der Zero to main() Serie. In diesem Artikel wird beschreiben, wie ein µC nach dem einschalten in dermain
-Funktion landet und was dafür notwendig ist.