Dieses Blog durchsuchen

Freitag, 22. Februar 2013

[Projekt] Raspberry Pi und der Atmel - Teil 2 - Serielle Verbindung

Erster Teil: Raspberry Pi und der Atmel - Teil1

Hallo.

Aufbauend auf der Schaltung aus dem ersten Teil möchte ich mich nun der seriellen Verbinung des Raspberry Pi mit dem Mega8 witmen.
Die Grundlagen hierfür sind zum einen die Schaltung aus dem ersten Teil und zum anderen das wir die serielle Schnittstelle der kleinen Himbeere frei gemacht haben. Wie dies funktioniert erfahrt ihr in meinem Post Raspberry Pi UART und der Asuro im Teil "UART Freimachen".
Solltet ihr diesen Abschnitt erfolgreich hinter euch gebracht haben, so könnt ihr die serielle Schnittstelle für unsere weiteren Schritte nutzen.

Logikpegel konvertieren

 

Unsere Himbeere verfügt über eine serielle Schnittstelle welche leider nicht 5V tolerant ist. Nimmt man sich jedoch einen Standard Mikrocontroller, wie den Mega8 in unserem Fall, so arbeiten wir mit einem 5V Logikpegel. Um nun den Pi nicht gleich den Funktionsumfang eines Backsteines zu geben müssen wir den 5V Logikpegel in einen für den Pi freundlichen 3,3V Logikpegel wandeln. Dabei sollten wir jedoch einiges beachten. Da unser Mikrocontroller über eine eigene Spannungsquelle verfügt ist es ratsam die Schaltung des Controllers vom Pi zu trennen. Natürlich wäre eine Pegelwandlung auch über einen handelsüblichen Pegelwandler wie den MAX3232 möglich oder sogar mit Standard Transistoren wie den BC547 realisierbar. Da wir nicht mit einem Baudratenquarz sondern mit einem ganzzahligen Quarz arbeiten ist mit der Geschwindigkeit bei 38400 sowieso Schluss, da der Fehler danach zu groß wird um eine vernünftige Verbindung hinzubekommen. Die theoretischen Gundlagen dazu findet ihr hier. 

Ich möchte hier in diesem Tutorial die Schaltung des Controllers galvanisch von der des Pi's trennen. Da wir im weiteren Verlauf mit dem Controller eventuell etwas messen wollen, bietet sich dieses an, da wir im schlimmsten Fall nur die Schaltung des Mikrocontrollers zerstören und nicht gleich den Pi mit. Zur Trennung und Konvertierung der Pegel nutze ich einen Liteon LTV 847 Optokoppler. Die zugehörige Schaltung sieht nun folgendermaßen aus.


Es sieht erstmal komplizierter aus als es ist.  Der Optokoppler verfügt intern eigentlich über 4 Optokopple,r wobei jeder aus einer LED (Eingang) und einem Fototransistor (Ausgang) besteht. Ein Blick in das Datenblatt gibt uns Auskunft über die benötigte Spannung und den benötigten Strom der LED. Hier können wir einen Vorwiderstand von 330 Ohm (R1;R2;R3;R5) verwenden. Diesen sollten sowohl im 5V und im 3,3V Logikpegel die LED's ordentlich versorgen ohne das wir an die Grenze beim Pi kommen. Die Widerstände R4 und R5 sind einfache 10 kOhm Pullup-widerstände an den Ausgängen.
Wie ihr seht nutze ich für beide Richtungen jeweils 2 Optokoppler. Würden wir nur einen Koppler nutzen würde unser Signal invertiert beim Pi oder beim Atmel ankommen. Aus einer "1" wird wozusagen eine "0" und umgekehrt. Um den entgegenzuwirken nutzen wir einfach einen zweiten Koppler, da uns ja sowieso 4 zur Verfügung stehen. Ihr könnt die Invertierung natürlich auch mit Transistoren oder softwareseitig realisieren. Aufgebaut sieht unsere Schaltung nun wie folgt aus.




Der erste Kontakt

 

Damit unser Pi mit dem Atmel auch Kontakt aufnehmen kann müssen wir den Controller nun dazu bringen empfangen und senden zu können. Die Grundlagen für die Serielle Schnittstelle findet ihr im Tutorial auf mikrocontroller.net, sodass ich diese hier nur anschneiden werde. Schauen wir uns dazu das folgende Programm an.

/*
 * mega8pi1.c
 *
 * Created: 17.02.2013 14:50:45
 *  Author: Kai Mildner
 */
//Festlegen der Frequenz (Quarz) und der Baudrate
#define F_CPU 16000000UL
#define BAUD 38400UL

//Berechnen der Register für die UART und des Fehlers bei gewählter Baudrate
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD)

//Überprüfen des Fehlers und eventueller Abbruch wenn Fehler > 1%
#if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
#error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch!
#endif

#include <avr/io.h>
#include <util/delay.h>

//Initialisierung der UART
void uart_init(void)
{
    UBRRH = UBRR_VAL >> 8;
    UBRRL = UBRR_VAL & 0xFF;
    UCSRB |= (1<<TXEN);
    UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
}

//Einzelnes Zeichen via UART senden
int uart_putc(unsigned char c)
{
    while (!(UCSRA & (1<<UDRE)))
    {
    }
    UDR = c;
    return 0;
}

//Zeichenkette via UART senden
void uart_puts (char *s)
{
    while (*s)
    {
        uart_putc(*s);
        s++;
    }
}

int main(void)
{
    uart_init();
    while(1)
    {
        uart_puts("Hallo hier ist dein Mega8\n");
        _delay_ms(100);
        //TODO:: Please write your application code
    }
}


Als erstes Betrachten wir den Abschnitt

//Festlegen der Frequenz (Quarz) und der Baudrate
#define F_CPU 16000000UL
#define BAUD 38400UL


Wie im Kommentar beschrieben legen wir hier unsere Taktfrequenz des Controllers sowie die von uns verwendete Baudrate fest. Ich empfehle dies am Anfang des Programmes zu erledigen, da zum Beispiel die Taktrate auch von der "delay.h" benötigt wird.


//Berechnen der Register für die UART und des Fehlers bei gewählter Baudrate
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD)


In diesem Abschnitt berechnen wir die einzelnen Werte welche wir für unsere gewählte Baudrate benötigen, sowie den Fehler bei selbiger.


//Überprüfen des Fehlers und eventueller Abbruch wenn Fehler > 1%
#if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
#error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch!
#endif


Hier legen wir fest bis zu welcher Fehlerquote wir arbeiten. Liegt der berechnete Fehler über 1% so bricht die Compilierung des Programms an dieser Stelle ab.


//Initialisierung der UART
void uart_init(void)
{
    UBRRH = UBRR_VAL >> 8;
    UBRRL = UBRR_VAL & 0xFF;
    UCSRB |= (1<<TXEN);
    UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
}


Diese Funktion dient der Initialisierung der seriellen Schnittstelle und legt in unserem Fall folgendes Fest.
  1. Wir können senden, jedoch noch nicht empfangen.
  2. Wir senden mit einem 8Bit Frame

//Einzelnes Zeichen via UART senden
int uart_putc(unsigned char c)
{
    while (!(UCSRA & (1<<UDRE)))
    {
    }
    UDR = c;
    return 0;
}

//Zeichenkette via UART senden
void uart_puts (char *s)
{
    while (*s)
    {
        uart_putc(*s);
        s++;
    }
}


Diese zwei Funktionen dienen nun zum Senden einzelner Zeichen und Zeichenketten.


´int main(void)
{
    uart_init();
    while(1)
    {
        uart_puts("Hallo hier ist dein Mega8\n");
        _delay_ms(100);
        //TODO:: Please write your application code
    }
}



Dies ist unsere Hauptfunktion. Wir initialisieren mittels Funktionsaufruf "uart_init()" unsere Schnittstelle und lassen aller 100 Millisekunden den Text "Hallo hier ist dein Mega8" senden, wobei wir mit " \n" jedesmal einen Zeilenumbruch mitsenden. Schließen wir nun unsere Schaltung am Pi an und starten zum Beispiel "Cutecom" so sieht es mit eingestellten Werten (Baudrate, Datenbits, Stopbit) folgendermaßen aus.



Solltet ihr nun dauerhaft den Text empfangen können, so wisst ihr das zumindest der Sendeteil des Controllers und der Empfangsteil des Pi's klappt. Ihr seit nun in der Lage Werte vom Mikrocontroller an den Pi zu senden.

Da wir jedoch, am Pi, nicht nur empfangen Wollen sondern auch Befehle senden möchten so müssen wir unser Programm noch etwas überarbeiten. Wir brauchen also am Controller eine Ausgabe, damit wir überhaupt sehen können ob alles klappt. Weiterhin bietet es sich auch an am Controller ein bis zwei Eingabebauteile anzubringen, wenn wir sowieso schonmal am Bauen sind. Dazu habe ich einfach 2 Taster (S2;S3) und eine LED (LED1) gewählt. Die Schaltung des Controllers sieht nun wie folgt aus.



Nun können wir Eingaben und Ausgaben am Controller tätigen.
Um jedoch Befehle vom Pi zu senden müssen wir unser Programm noch etwas erweitern.
Dazu können wir natürlich wieder auf das Wissen aus den Tutorials von Microkontroller.net zurückgreifen und uns die Makros zur Tasterentprellung sowie die Interruptroutine für den UART guttenbergen. Ein fertig geschriebenes Programm für den Atmel könnte wie folgt ausschauen.

mega8pi.c

Hier das Atmel Studio Projekt

Mega8Pi.zip

Den Code im einzelnen durchzugehen würde jetzt an dieser stelle den Rahmen sprengen und ist meiner Meinung nach nicht nötig, da dies alles schon 1000mal im Netz gemacht wurde und man das Rad nicht neu erfinden muss. Außerdem empfehle ich euch selbst einen Weg zu suchen und diesen so oft wie möglich zu verwenden. Habt ihr ersteinmal das Grundgerüst so könnt ihr es jederzeit in anderen Projekten verwenden.
Hier noch ein kurzes Video wie die Schaltung der LED Ausschauen könnte. Der Code aus der mega8pi.c wurde dabei verwendet.


Sehr gute Tutorials zur Programmierung der UART auf dem Atmel findet ihr, wie schon so häufig erwähnt, auf mikrocontroller.net. Ich empfehle jeden sich in die Thematiken Interrupts, Timer, Taster Entprellung und UART einzulesen, da dieses das Grundwissen für die Arbeit mit den kleinen Controllern bietet.

Ich hoffe ihr konntet mir einigermaßen folgen und habt nun euren Controller mit dem Pi seriell verbunden. Ich würde mich freuen wenn ihr ab und zu wieder reinschaut.
Es sind noch einige Messaufgaben geplant, sowie eine Stromversorgung des Pi mit Batteriebackup, "An/Aus Taster" und automatischem herunterfahren bei Stromnetzverlust.
Wie ihr seht ist noch einiges in Arbeit. Schaut also einfach mal wieder rein.

MFG

Kai


Keine Kommentare:

Kommentar veröffentlichen