Einfache Ein- und Ausgaben mit dem ARM

Mit der zweiten Übung tasten wir uns im wahrsten Sinne des Wortes an die eigentliche Aufgabe eines jeden Controllers. Dieser soll Zeit seines Lebens fortlaufend Eingaben aus seiner Umgebung nach einer vorgegebenen Logik verarbeiten und entsprechend dieser Verarbeitung Ausgaben erzeugen. Es ist das allseits beliebte EVA-Prinzip, *gähn*. Spaß beiseite! Zum Gähnen ist das nur solange, wie man mit der EVA nichts Praktisches anfängt *grins*. Es soll der blaue Taster auf dem STM32F4 Discovery ausgewertet werden. Und da der Taster blau ist, auch zünftig die blaue LED eingeschaltet werden.

Es ist zunächst wieder wichtig, sich mit der konkreten Schaltung zu beschäftigen. Diese entnehmen wir der Produktbeschreibung zum STM32F4 Discovery. In der folgenden vereinfachten Darstellung ist das Wesentliche zusammengefasst.

Die blaue LED hängt überraschenderweise immer noch an D15 und der Taster ist diesmal an A0 angeschlossen. Die tatsächliche Tasterlogik ist hier etwas vereinfacht als Umschalter abgebildet. RCC liefert wieder über AHB1 den Takt. Wir merken schon, als Erstes müssen wir immer dafür sorgen, das jedes Gerät, welches wir benutzen wollen, seinen Takt bekommt. Es ist also Folgendes zu tun, um die Aufgabe zu erfüllen:

  1. über den AHB1 Bus den GPIO Port A mit Takt versorgen
  2. das Bit 0 des GPIO Port A als Eingang ohne PullUp konfigurieren
  3. über den AHB1 Bus den GPIO Port D mit Takt versorgen
  4. das Bit 15 des GPIO Port D als Ausgang konfigurieren
  5. das Bit 0 von GPIO Port A einlesen
  6. wenn der Taster gedrückt ist, das Bit 15 des GPIO Port D
    • auf High schalten
    • sonst auf Low schalten

Falls das Tutorial-Projekt nicht mehr offen ist, öffnen Sie dies. Legen Sie bitte ein neues kleines Programm an und laden das Grundgerüst 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 intelligenter Lichtschalter 1 mit SiSy STM32
//----------------------------------------------------------------------
// Funktion  : der blaue Taster schaltet die blaue LED an
// Schaltung : blaue LED an GPIO Port D15, Taster an A0
//----------------------------------------------------------------------
// Hardware  : STM32F4 Discovery
// Takt      : 168 MHz
// Sprache   : ARM C++
// Datum     : heute
// Version   : 1
// Autor     : ich
//---------------------------------------------------------------------- 

Rekapitulieren wir noch mal das Blockbild von oben. Man erkennt, dass der RCC-Unit (Reset & Clock Control ) mitgeteilt werden muss, dass GPIOD und GPIOA über den AHB1 mit Takt versorgt werden müssen. Wie bereits im Beispiel HalloARM, benutzen wir dafür die Funktion RCC_AHB1PeriphClockCmd aus den STM32F4-Peripherie-Treibern.

// GPIOD Takt einschalten 
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
// GPIOA Takt einschalten 
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

Damit sind die GPIO Ports A und D bereit initialisiert zu werden. Es sind nach dem bekannten Muster

  1. Takt einschalten
  2. Initialisierungsstruktur anlegen
  3. Struktur mit Standardwerten füllen
  4. Anwendungsspezifische Anpassungen vornehmen
  5. Gerät initialisieren

als Nächstes die Initialisierungsstrukturen für die beiden Ports vorzubereiten. Dazu können zwei Instanzen der Struktur angelegt werden oder nur eine, um diese dann wieder zu verwenden. Wir entscheiden uns für die Wiederverwendung einer Struktur.

GPIO_InitTypeDef  GPIO_InitStructure;
GPIO_StructInit (&GPIO_InitStructure);

Jetzt kümmern wir uns um den Ausgang für die LED, PortD Bit15. Dieser muss im Output Mode als Gegentaktstufe, also Push-Pull arbeiten und benötigt kein PullUp oder PullDown. Die Geschwindigkeit des Ports ist uns bei dieser Anwendung eigentlich recht egal. Strom müssen wir jetzt nicht sparen, also belassen wir es bei den 100 MHz.

GPIO_InitStructure.GPIO_Pin =  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);

Kümmern wir uns jetzt um den Eingang für den Taster, Port A Bit0. Der blaue Taster auf dem STM32F4 hat eine kleine Treiberstufe, die zwischen High und Low umschaltet. Ist der Taster nicht gedrückt, zieht die Treiberstufe die Pin A.0 auf Low. Wird der Taster gedrückt, wird Pin A.0 gegen High geschaltet. Der Taster ist also Hihg-Aktiv und benötigt weder einen PullUp noch ein PullDown.

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);

Das Einlesen eines Port-Pin erfolgt mit der Funktion GPIO_ReadInputDataBit aus den STM32F4 Peripherietreibern. Die Funktion erwartet den Port und das Bit als Parameter und liefert mit 0 oder 1 den aktuellen Status des Port-Pins als Rückgabewert. Das können wir recht nett auswerten.

if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)==1)

Entsprechend der Auswerung können wir mit den bereits bekannten Funktionen GPIO_SetBit und GPIO_ResetBit die LED einschalten:

GPIO_SetBits(GPIOD,GPIO_Pin_15);

oder ausschalten:

GPIO_ResetBits(GPIOD,GPIO_Pin_15);

Zuerst wieder der Entwurf in Form von Kommentaren:

//----------------------------------------------------------------------
// Titel     : ENTWURF intelligenter Lichtschalter 1 in SiSy STM32
//----------------------------------------------------------------------
// Funktion  : blaue Taste schaltet blaue LED an
// Schaltung : Taste an PA0 LED an PD15
//----------------------------------------------------------------------
// Hardware  : STM32F4 Discovery
// Takt      : 168 MHz
// Sprache   : ARM C
// Datum     : 29.08.2012
// Version   : ENTWURF
// Autor     : Alexander Huwaldt
//----------------------------------------------------------------------
#include <stddef.h>
#include <stdlib.h>
#include "hardware.h"
 
void initApplication()
{
	SysTick_Config(SystemCoreClock/100);
 
	// GPIOD Takt einschalten
	// Konfiguriere PD15 als Ausgang
	// GPIOA Takt einschalten
	// Konfiguriere PA0 als Eingang
 
}
 
int main(void)
{
	SystemInit();
	initApplication();
	do{
		// WENN Taster an A0 gedrückt DANN
		//  LED an D15 einschalten
		// SONST
		//  LED an D15 ausschalten 
 
	} while (true);
	return 0;
}
 
extern "C" void SysTick_Handler(void)
{
	// hier nichts tun
}

Nachdem wir den Entwurf in Ruhe rekapituliert haben, kann es an die Umsetzung gehen. Wenn Sie Quelltexte lieber kopieren, können Sie das gern mit dem obigen Entwurf machen, aber die eigentlichen Befehle sollten Sie aus lernpsychologischen Überlegungen tatsächlich selbst und bewusst, demzufolge selbstbewusst, eintippen.

//----------------------------------------------------------------------
// Titel     : Beispiel intelligenter Lichtschalter 1 in SiSy STM32
//----------------------------------------------------------------------
// Funktion  : blaue Taste schaltet blaue LED an
// Schaltung : Taste an PA0 LED an PD15
//----------------------------------------------------------------------
// Hardware  : STM32F4 Discovery
// Takt      : 168 MHz
// Sprache   : ARM C++
// Datum     : 29.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 Takt einschalten
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
	// GPIOA Takt einschalten
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
 
	GPIO_InitTypeDef  GPIO_InitStructure;
	GPIO_StructInit (&GPIO_InitStructure);
 
	// Konfiguriere PD15
	GPIO_InitStructure.GPIO_Pin = 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);
 
	//  Konfiguriere PA0
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
 
}
 
int main(void)
{
	SystemInit();
	initApplication();
	do{
		if ( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 1 )
		{
			GPIO_SetBits(GPIOD,GPIO_Pin_15);
		}
		else
		{
			GPIO_ResetBits(GPIOD,GPIO_Pin_15);
		}
	} while (true);
	return 0;
}
 
extern "C" void SysTick_Handler(void)
{
	// Application SysTick	
}

Ü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

Die blaue LED auf dem STM32F4 Discovery leuchtet jetzt immer solange, wie der blaue Taster gedrückt ist.

Videozusammenfassung

An dieser Stelle wieder eine kurze Zusammenfassung. Wirklich Neues ist fett hervorgehoben:

Initialisierungssequenz für Geräte:

  1. Takt einschalten, RCC_xxxClockCmd
  2. Initialisierungsstruktur anlegen, xxx_InitTypDef initStruct
  3. Struktur mit Standardwerten füllen, xxx_StructInit (&initStruct)
  4. Anwendungsspezifische Anpassungen vornehmen, initStruc.xxx_Mode = wert
  5. Gerät initialisieren, xxx_Init(xxx, &initStructure)

Initialisierungsstruktur für Digitalports: GPIO_InitTypeDef initStruct;

  • initStruct.GPIO_Pin = GPIO_Pin_0..15;
  • initStruct.GPIO_Mode = GPIO_Mode_OUT|IN|AF;
  • initStruct.GPIO_OType = GPIO_OType_PP|OD;
  • initStruct.GPIO_Speed = GPIO_Speed_2..100MHz;
  • initStruct.GPIO_PuPd = GPIO_PuPd_NOPULL|UP|DOWN;

wichtige Funktionen für Digitalports:

  • RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOx, ENABLE|DISABLE);
  • GPIO_StructInit (&initStruct);
  • GPIO_Init(GPIOx, &initStruct);
  • GPIO_ReadInputDataBit(GPIOx, GPIO_Pin_x)
  • GPIO_SetBits(GPIOx,GPIO_Pin_x);
  • GPIO_ResetBits(GPIOx,GPIO_Pin_x);

Und hier diesen Abschnitt dann auch als Videozusammenfassung.

Nächstes Thema