Der SystemTick in C

ARM Controller sind prädestiniert für den Einsatz spezieller Laufzeitumgebungen oder bestimmter Betriebssysteme. Solche basieren oft auf einer Timer-getriggerten Verteilung von Ressourcen, vor allem der Ressource Rechenzeit. Dafür steht beim ARM ein spezieller Timer zur Verfügung, der ausschließlich die Aufgabe hat, ein System-Trigger-Ereignis zu generieren. Auch ohne Echtzeitbetriebssystem ist dieser SystemTick für den Anwendungsentwickler sehr interessant. Die hier im Tutorial verwendeten Programmvorlagen, und insbesondere das später verwendete mySTM32 C++ Framework, sind bereits auf die Nutzung des SysTick vorbereitet bzw. basieren darauf.

Diese Übung wird eine einfache Verwendung des SysTick_Handler zur Generierung zyklischer Ausgaben demonstrieren. Wir lassen die LEDs auf dem Board abwechselnd blinken. Das folgende Blockbild verdeutlicht welche Bausteine bei dieser Aufgabe eine Rolle spielen. Vergleichen Sie diese vereinfachte Darstellung mit dem Blockbild aus dem Datenblatt.

Die vier LEDs auf dem STM32F4 Discovery sind immer noch fest mit den Pins D12 bis D15 verbunden. Der SystemTick soll so konfiguriert werden, dass dieses Ereignis alle 10 Millisekunden eintritt. Die Takt-Versorgung des GPIO Ports D erfolgt, wie wir wissen, über AHB1. Fassen wir die Aufgaben zusammen:

  1. das SysTick-Ereignis auf 10 ms konfigurieren
  2. über den AHB1 Bus den GPIO Port D mit Takt versorgen
  3. die Bits 12 bis 15 des GPIO Port D als Ausgang konfigurieren
  4. wenn das SysTick-Ereignis eintritt, die LEDs an GPIO Port D12 bis 15 unterschiedlich blinken lassen

Legen Sie bitte ein neues kleines Programm an und laden Sie das Grundgerüst für eine ARM C++ Anwendung. Beachten Sie die Einstellungen für die Zielplattform STM32F4-Discovery.

Erstellen Sie die Programmkopfdokumentation. Übersetzen und Übertragen Sie das noch leere Programm auf den Controller, um die Verbindung zu testen.

//----------------------------------------------------------------------
// Titel     : Beispiel einfache SysTick-Nutzung in SiSy STM32
//----------------------------------------------------------------------
// Funktion  : lässt die 4 LEDs zyklisch blinken
// Schaltung : LEDs an D12,13,14,15
//----------------------------------------------------------------------
// Hardware  : STM32F4 Discovery
// Takt      : 168 MHz
// Sprache   : ARM C++
// Datum     : 08.10.2012
// Version   : ENTWURF
// Autor     : Alexander Huwaldt
//----------------------------------------------------------------------

In den bisher verwendeten Programmgerüsten war der SystemTick schon vorbereitet. Diese Vorbereitung besteht aus dem Aufruf der Funktion SysTick_Config und dem Ereignishandler SysTick_Handler. Die Funktion SysTick_Config erwartet als Parameter den Zählerwert für den SysTick-Timer. Mit dem Parameter SystemCoreClock/100 legen wir fest, dass nach 1.680.000 Systemtakten der SysTick-Interrupt ausgelöst und der SysTick_Handler aufgerufen wird. Bei einem Systemtakt (SystemCoreClock) von 168 MHz entspricht dies 10 ms.

// SysTickCounter = TakteProSekunde/100 = 10 ms
SysTick_Config(SystemCoreClock/100);

Zehn Millisekunden sind ein sehr brauchbares und auch sehr übliches System-Timer-Ereignis. Da wir jedoch LEDs blinken lassen wollen, müssen wir uns überlegen, wie wir das dem Auge sichtbar machen. Einhundert Umschaltvorgänge pro Sekunde nehmen wir beim besten Willen nicht mehr wahr. Treten wir also auf die Bremse und richten uns einen eigenen einfachen Vorteil für unsere blinkenden LEDs ein. Dabei ist zu beachten, dass die Gültigkeit einer Variablen abhängig von deren Position im Quellcode ist. Eine Möglichkeit ist, eine globale Variable einzurichten. Das Thema globale Variablen, inclusive der sich darum entfachenden Glaubenskriege, kann an dieser Stelle nicht erschöpfend diskutiert werden. Die Globale bleibt eine Möglichkeit. Als Zweites bietet sich eine lokale Variable in dem SysTick_Handler an. Diese verliert jedoch jedesmal ihre Gültigkeit, wenn die Funktion verlassen wird. Die Lösung ist, die lokale Variable als statisch zu deklarieren.

// Zähler einrichten
static u8 sysTickCounter=0;
sysTickCounter++;
if (sysTickCounter==100)
  ...

Der Basistyp u8 entspricht dem C-Typ unsigned char, also einem vorzeichenlosen 8 Bit Wert. Das CMSIS, die Perihperie-Treiber und auch das deklarierte Typ-System orientieren sich an den MISRA-Regeln. Diese verbieten zum Beispiel die Nutzung des Basistyp char und fordern in MISRA-Regel 13 und 14 Typbezeichner zu verwenden, die Bitlänge und Vorzeichen erkennen lassen. Das kennen wir bereits vom AVR-C. Dort hieß der entsprechende Typ uint8_t. Beim ARM-C hat man es etwas knackiger deklariert.

Die Zuweisung des Startwertes Null zu einer statischen Variable erfolgt nur ein mal, nämlich beim Systemstart. Danach behält die statische Variable ihren Zählerwert. Diesen können wir dann nutzen, um nur bei bestimmten Zählerwerten die LEDs umzuschalten. Der 8 Bit Wert läuft bis 255 und nach dem Überlauf fängt er wieder bei Null an. Das sollte uns als Bremse reichen.

Ein weiterer Aspekt, den wir diskutieren wollen, ist der zu betreibende Aufwand, um ein Pin zu konfigurieren. Bisher haben wir dafür jeweils sieben Zeilen aufgewendet. Für unsere vier Ausgabe-Pins müssen wir den Schreibaufwand für fast dreißig Zeilen nicht fürchten. Die Funktion GPIO_Init kann nicht nur ein Pin, sondern auch mehrere Pins gleichartig konfigurieren. Dazu werden die gewünschten Pins ODER-verknüpft und können der Initialisierungsstruktur zugewiesen werden.

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;

Für das Blinken der LEDs nutzen wir die Funktion GPIO_ToggleBits aus der STM32F4 Treiberbibliothek.

GPIO_ToggleBits(GPIOD,GPIO_Pin_12);

Mehr Neues gibt es für das Beispiel nicht zu besprechen. Also ran an den Entwurf.

Auch wenn es uns in den Fingern kribbelt sofort die nötigen Befehle einzugeben, kasteien wir uns mit dem folgenden Entwurf.

//----------------------------------------------------------------------
// Titel     : Beispiel einfache SysTick-Nutzung in SiSy STM32
//----------------------------------------------------------------------
// Funktion  : lässt die 4 LEDs zyklisch blinken
// Schaltung : LEDs an D12,13,14,15
//----------------------------------------------------------------------
// Hardware  : STM32F4 Discovery
// Takt      : 168 MHz
// Sprache   : ARM C++
// Datum     : 08.10.2012
// Version   : ENTWURF
// Autor     : Alexander Huwaldt
//----------------------------------------------------------------------
#include <stddef.h>
#include <stdlib.h>
#include "hardware.h"
 
void initApplication()
{
	SysTick_Config(SystemCoreClock/100);
	// weitere Initialisierungen durchführen
 
	// GPIOD Peripherie Takt einschalteb
	// Konfiguriere PD12,13,14,15 als Ausgang
 
}
 
int main(void)
{
	SystemInit();
	initApplication();
	while(true)
	{
		//leer
	}
	return 0;
}
 
extern "C" void SysTick_Handler(void)
{
	// Zähler anlegen
	// Zähler eins hochzählen
	// wenn Zähler = 25  dann D12 umschalten
	// wenn Zähler = 50  dann D13 umschalten
	// wenn Zähler = 100 dann D14 umschalten
	// wenn Zähler = 200 dann D15 umschalten
}

Nachdem wir den Entwurf bei einem kräftigen Schluck Kaffee auf uns wirken lassen haben , kann der Code erstellt werden.
Nicht kopieren! Selbstbewusst tippen!

//----------------------------------------------------------------------
// Titel     : Beispiel einfache SysTick-Nutzung in SiSy STM32
//----------------------------------------------------------------------
// Funktion  : 
// Schaltung : 
//----------------------------------------------------------------------
// Hardware  : STM32F4 Discovery
// Takt      : 168 MHz
// Sprache   : ARM C++
// Datum     : 30.08.2012
// Version   : 1
// Autor     : Alexander Huwaldt
//----------------------------------------------------------------------
#include <stddef.h>
#include <stdlib.h>
#include "hardware.h"
 
void initApplication()
{
	SysTick_Config(SystemCoreClock/100);
	// weitere Initialisierungen durchführen
 
	// GPIOD Periph clock enable
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
 
	GPIO_InitTypeDef  GPIO_InitStructure;
	// Konfigurire PD12,13,14,15 als Ausgang
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_Init(GPIOD, &GPIO_InitStructure);
 
}
 
int main(void)
{
	SystemInit();
	initApplication();
	while(true)
	{
		//leer
	}
	return 0;
}
 
 
extern "C" void SysTick_Handler(void)
{
	static u8 sysTickCounter=0; 
	sysTickCounter++;
	if (sysTickCounter==25)
		GPIO_ToggleBits(GPIOD,GPIO_Pin_12);		
	if (sysTickCounter==50)
		GPIO_ToggleBits(GPIOD,GPIO_Pin_13);		
	if (sysTickCounter==100)
		GPIO_ToggleBits(GPIOD,GPIO_Pin_14);		
	if (sysTickCounter==200)
		GPIO_ToggleBits(GPIOD,GPIO_Pin_15);		
 
}

Übersetzen Sie das Programm. Korrigieren Sie ggf. Schreibfehler. Übertragen Sie das lauffähige Programm in den Programmspeicher des Controllers.

  1. Kompiliern
  2. Linken
  3. Brennen

Videozusammenfassung

Und hier diesen Abschnitt wiederum als Text- und Videozusammenfassung.

Initialisierung

  1. SysTick auf 10 ms konfigurieren, SysTick_Config(SystemCoreClock/100);
  2. GPIOD mit Takt versorgen
  3. GPIOD Pin 12 bis 15 als Ausgang konfigurieren

Mainloop

  1. bleibt leer, alles läuft im SysTick

SysTick-Ereignis

  1. Funktion SysTickFunction
  2. SysTick-Zähler als statische Variable
  3. LEDs mit GPIO_ToggleBits umschalten

Nächstes Thema