4 Zeitlicher Arbeitsablauf

4.1 Bereitstellen grundlegender Funktionalitäten

Im ersten Schritt ging es darum, den Mikrocontroller so zu programmieren, dass dieser die für dieses Projekt grundlegenden Funktionalitäten bereitstellen kann.
Der Mikrocontroller befand sich vorerst auf dem STK500(siehe Kapitel 2.4.1). Um dessen Komponenten im Mikrocontroller nutzen zu können, müssen dafür Register initialisiert werden oder Funktionalitäten wie z.B. Bibliotheken für das LC-Display bereit gestellt werden.
Die folgenden Kapitel beschreiben den Programmcode, der notwendig ist um diese Funktionalitäten bereitzustellen.

4.1.1 Taster

Um die Taster des STK500 im Mikrocontroller nutzen zu können müssen diese mit dem Mikrocontroller verbunden und entprellt55Als Prellen bezeichnet man das ungewollte mehrfache Schalten eines mechanischen Schalters bei einem einzelnen Schaltvorgang. werden.
Dazu muss die Stiftleiste von PortA mit der Stiftleiste für die Taster verbunden werden. Das Entprellen der Taster wird softwareseitig realisiert. Dies bietet sich bei einem Mikrocontroler an. Dazu gibt es vorgefertigte Bibliotheken die genutzt werden können. Im Projekt wurde die Bibliothek Debounce.h(4) von Peter Dannegger genutzt. Sie ist sehr komfortabl und funktionsreich und basiert auf Timer-Interrupts. Um sie zu nutzen wird die Datei Debounce.h in das Projektverzeichnis kopiert und mit Zeile 1 des Codelisting 1 in das Programm eingebunden. Die Zeilen 2-10 spiegeln die Funktion zum Initialisieren der Bibliothek wieder. Diese Zeilen müssen auf den jeweils verwendeten Mikrocontroller angepasst werden.
Durch die Verwendung der Bibliothek ist es möglich Funktionen wie z.B. get_key_press() zu nutzen um den Status der Taster prellfrei auszulesen und diese Information für Entscheidungen im Programmablauf zu verwenden.

Listing 1: Taster
1 keywordstyle include "Debounce.h"                          // Taster entprellen
2 void        debounce_init                (void) {    // Taster entprellen
3         KEY_DDR &= ~ALL_KEYS;                  // configure key port for input
4         KEY_PORT |= ALL_KEYS;                   // and turn on pull up resistors
5         TCCR0B = (1 << CS02) | (1 << CS00);   // divide by 1024
6         // preload for 10ms
7         TCNT0 = (uint8_t) (int16_t) -(F_CPU / 1024 * 10 * 10e-3 + 0.5);
8         TIMSK0 |= 1 << TOIE0;                      // enable timer interrupt
9         sei();                                     // global enable Interrupts
10 }
11 if (get_key_press(1 << KEY4))
12         menu_select(&menu_context);            // Aktuellen Menuepunkt auswaehlen

4.1.2 LEDs

LEDs sollen im Programmablauf genutzt werden können, um z.B. Fehler zu signalisieren.
Dazu muss zuerst die Stiftleiste von PortB mit der LED Stiftleiste des STK500 verbunden werden. Um LEDs an PortB betreiben zu können müssen die entsprechenden Pins im Register DDRB als Ausgänge definiert werden. Dies geschieht in Zeile 1 des Codelisting 2. Die Bibliothek zum Entprellen der Taster nutzt die Variablen LED_DDR und LED_PORT. Diese Variablen werden auch hier genutzt um auf die Register zuzugreifen. Dies gewährleistet eine bessere Übersicht. Die Werte im 8-Bit Register LED_PORT spiegeln die Spannungen an den Pins des PortB am Mikrocontroller wieder. Da die LEDs auf dem STK500 mit active-low-Logik betrieben werden, muss das jeweilige Bit gelöscht, also auf ’’0’’, gesetzt werden damit die LED leuchtet. Um alle Bits in einem Register zu verändern kann das Register mit einem 2-stelligen Hex-Wert (8-Bit) oder einem 8 stelligen binären Bitmuster beschrieben werden. In Zeile 2 werden alle Bits mit dem Hex-Wert 0xFF auf ’’1’’ gesetzt und somit alle LEDs ausgeschaltet. Um ein einzelnes Bit zu verändern, können die Anweisungen in den Zeilen 3 und 4 verwendet werden. Dabei steht das ’’X’’ in PBX für die x-te Stelle im Register die gesetzt oder gelöscht werden soll.
Es ist damit möglich im Programmablauf einzelne LEDs anzusteuern.

Listing 2: LEDs
1 LED_DDR   = 0xFF;                 // LED Port Richtung definieren (Ausgang)
2 LED_PORT  = 0xFF;                  // LEDs ausschalten
3 LED_PORT &= ~((1 << PBX));         // loescht Bit an PortB - LED an
4 LED_PORT |=  ((1 << PBX));         // setzt  Bit an PortB - LED aus

4.1.3 Ansteuerung des LC-Display

Um den aktuellen Status des Motor komfortabel in Textform anzeigen zu können und die Schrittmotorkarte menübasiert ansteuern zu können wird ein LC-Display verwendet. Das verwendete Display ist alpha numerisch und kann 4x20 Zeichen anzeigen.
Die meisten LC-Displays werden auf die gleiche Weise angesteuert. Hier gibt es fertige Bibliotheken die frei genutzt werden können. Im Projekt wurde die Bibliothek von Peter Fleury (5) verwendet. Die Bibliothek wird heruntergeladen und die Dateien lcd.c und lcd.h in das Projektverzeichnis entpackt. Die Bibliothek wird mit #include ’’lcd.h’’ eingebunden. In der lcd.h müssen dann noch die Daten des Displays eingegeben werden (siehe Codelisting 3 Zeilen 2–10).
Danach kann das Display im Programm mit den Befehlen aus Zeile 12–21 angesteuert werden.

Listing 3: lcd.h (Auszug)
1 /**< Use 0 for HD44780 controller,  1 for KS0073 controller */
2 define LCD_CONTROLLER_KS0073 0
3 define LCD_LINES           4          /**< number of visible lines of the display */
4 define LCD_DISP_LENGTH    19        /**< visibles characters per line of the display */
5 define LCD_LINE_LENGTH  0x40           /**< internal line length of the display   */
6 define LCD_START_LINE1  0x00           /**< DDRAM address of first char of line 1 */
7 define LCD_START_LINE2  0x40           /**< DDRAM address of first char of line 2 */
8 define LCD_START_LINE3  0x14           /**< DDRAM address of first char of line 3 */
9 define LCD_START_LINE4  0x54           /**< DDRAM address of first char of line 4 */
10 define LCD_WRAP_LINES      1       /**< 0: no wrap, 1: wrap at end of visibile line */
11 // Funktionen zum Ansteuern des Displays:
12 extern void lcd_init(uint8_t dispAttr);
13 extern void lcd_clrscr(void);
14 extern void lcd_home(void);
15 extern void lcd_gotoxy(uint8_t x, uint8_t y);
16 extern void lcd_putc(char c);
17 extern void lcd_puts(const char *s);
18 extern void lcd_puts_p(const char *progmem_s);
19 extern void lcd_command(uint8_t cmd);
20 extern void lcd_data(uint8_t data);
21 define lcd_puts_P(__s) lcd_puts_p(PSTR(__s))

4.1.4 RS-232-Schnittstelle

RS-232 ist der Name der am häufigsten verwendeten seriellen Schnittstelle um Daten zwischen zwei elektronischen Geräten hin und her zu senden. (7)
Auf dem STK500 ist bereits eine serielle Schnittstelle vorbereitet. Um diese nutzen zu können, müssen die Pins 3 und 4 des PortC (erster UART) des Mikrocontrollers mit der Stiftleiste Rx/Tx auf dem STK500 verbunden werden. Eine weitere Schnittstelle wurde auf einem Steckbrett aufgebaut. Diese wurde mit den Pins 1 und 2 des PortC (zweiter UART) des Mikrocontrollers verbunden. Um die Schnittstellen im Mikrocontroller nutzen zu können müssen diese noch durch setzen von Bits in den entsprechenden Registern des Mikrocontrollers aktiviert werden.
Das Codelisting 4 teilt sich in 4 wesentliche Bereiche:

  • Zeilen 1 – 2: Setzen der Baudrate und einbinden der benötigten Bibliotheken.

  • Zeilen 3 – 17: Initialisieren der Schnittstellen durch setzen der richtigen Bits in den entsprechenden Registern.

  • Zeilen 18 – 35: Funktionen zum Senden von Daten

  • Zeilen 36 – 65: Funktionen zum Empfangen von Daten

Listing 4: RS-232
1 define BAUD 9600         // BAUD Rate definieren
2 keywordstyle include <util/setbaud.h>  // UART Funktionen
3 // UART Initialisieren
4 void        uart_init                () {
5         // UART 0 - IN (Rapidform Software/Terminal)
6         UBRR0H = UBRRH_VALUE;
7         UBRR0L = UBRRL_VALUE;
8         UCSR0C = (3 << UCSZ00);
9         UCSR0B |= (1 << TXEN0);    //Transmitter Enabled
10         UCSR0B |= (1 << RXEN0);    // UART RX einschalten
11         // UART 1 - OUT (Stepper Karte/Drehtisch)
12         UBRR1H = UBRRH_VALUE;
13         UBRR1L = UBRRL_VALUE;
14         UCSR1C = (3 << UCSZ00);
15         UCSR1B |= (1 << TXEN1);    //Transmitter Enabled
16         UCSR1B |= (1 << RXEN1);    // UART RX einschalten
17 }
18 // UART Zeichen senden
19 void        uart_put_charater        (unsigned char c, int dir) {
20         if (dir == D_RapidForm) {       // To Rapidform
21                 while (!(UCSR0A & (1 << UDRE0))) {}//warten bis Senden moeglich
22                 UDR0 = c;            // sende Zeichen
23         }
24         else {                              // To Stepper
25                 while (!(UCSR1A & (1 << UDRE1))) {}//warten bis Senden moeglich
26                 UDR1 = c;            // sende Zeichen
27         }
28 }
29 // UART String senden
30 void        uart_put_string                (char *s, int dir) {
31         while (*s){ // so lange *s != ’\0’ Terminierungszeichen
32                 uart_put_charater(*s, dir); // Zeichenweise senden
33                 s++;
34         }
35 }
36 // UART Zeichen empfangen
37 int        uart_get_character        (int dir) {
38         if (dir == D_RapidForm) {       // Aus RapidForm Register auslesen
39                 while (!(UCSR0A & (1 << RXC0))) ; // warten bis Zeichen verfuegbar
40                 return UDR0; // Zeichen aus UDR an Aufrufer zurueckgeben
41         }
42         if (dir == D_Stepper) {               // Aus Schrittmotor Register auslesen
43                 while (!(UCSR1A & (1 << RXC1))) ; // warten bis Zeichen verfuegbar
44                 return UDR1; // Zeichen aus UDR an Aufrufer zurueckgeben
45         }
46         return -1;    // Wenn nichts ausgelesen wurde -1 zurueckgeben
47 }
48 // UART String empfangen
49 void        uart_get_string                (char * string_in, int dir) {
50         char c;              // Einzelnes Zeichen
51         int i = 0;  // Zaehlvariable
52         do {
53                 c = uart_get_character(dir);  // Einzelnes Zeichen holen
54                 if (c != \r’) {           // Wenn keinn \r
55                         *string_in = c;           // Zeichen in Empfangsstring schreiben
56                         string_in += 1;          // Adresse des Empfangsstring um 1 ink
57                         i++;                     // Zaehlvariable um 1 erhoehen
58                 }
59         } while (i < 100 && c != \r && c != \n’); // So lange bis \r \n o. >100 Zeichen
60         *string_in = \0’;                       // 0 Terminieren
61         if (dir == D_Stepper)
62                 LED_PORT |= ( 1 << LED3 );  // "Daten Vorhanden" LED ausschalten
63         else
64                 LED_PORT |= ( 1 << LED2 );  // "Daten Vorhanden" LED ausschalten
65 }

Damit stehen die essentiellen Funktionen uart_put_string(dir) und uart_get_string(dir) zur Verfügung. Mit diesen kann der Mikrocontroller über die serielle Schnittstelle Strings senden und empfangen. Der Parameter dir gibt dabei die Schnittstelle an über die gesendet oder empfangen werden soll.