Inhalt

1. Grundlagen Programmierung der AVR in C

2.1 Unser erstes C-Programm für einen AVR

2.2 Der UART der AVR

2.3 Der TWI/ I2C- Bus

2.3.1 AVR als Master im I2C- Bus

2.3.2 AVR als Slave im I2C- Bus

2.4 Die Timer der AVR

2.4.1 Die Timerregister der AVR

2.4.2 Timer im Normal Mode mit Overflow Interrupt

2.4.3 Kalibrierung des internen RC-Oszillators

2.4.4 Timer im CTC Mode mit Compare Interrupt

2.5 PWM mit einem AVR

2.5.1 Hardware-PWM

2.5.2 Soft-PWM

2. Grundlagen zum Raspberry Pi

3.1 Was ist der Raspberry Pi

3.2 Die Betriebssysteme für den Raspberry Pi

3.4 SD-Karte für den Raspberry Pi vorbereiten

3.5 Der GPIO- Port des Raspberry Pi

3.6 Die Serielle Schnittstelle des Raspberry Pi

3.7 MD49 am Raspberry Pi

3. Raspberry Pi: Betriebssystem Raspbian

Raspi-Config

Hostname ändern

Username ändern

WLAN einrichten und Edimax WLAN-Stick Powersafe-Mode deaktivieren

Raspberry mit Composite/ AV- Ausgang betreiben

VNC-Server installieren

LAMP (Standard) unter Raspbian

LAMP mit Nginx und SQLite

SSH

C/C++ Crosscompiler für Raspberry Pi mit Eclipse (Linux PC)

Qt5 Crosscompile-Toolchain für Raspbian unter Linux

4. Arduino (Linux)

5. ROS Grundlagen

ROS Groovy auf RPi

ROS Groovy auf Ubuntu Workstation

ROS im Netzwerk

ROS C++ Programmierung mit QtCreator

ROS Catkin Workspace erstellen

ROS Package erstellen

Erste ROS Node, “helloworld”

ROS Sourcecode Management mit Git

ROS Workspace von github.com klonen

Joystick ROS

ROS Custom Messages

ROS: Arduino als ROS-Node

ROS: Web-Konnektivität mit ROSBridge

6. Git Grundlagen

7. Web Development

Django Webframework

1. Grundlagen Programmierung der AVR in C

Vorzeichenbehaftete int-Typen

Typname

Bit-Breite

Wertebereich

C-Entsprechung (avr-gcc)

int8_t

8

−128 ⋯ 127

−27 ⋯ 27−1

signed char

int16_t

16

−32768 ⋯ 32767

−215 ⋯ 215−1

signed short, signed int

int32_t

32

−2147483648 ⋯ 2147483647

−231 ⋯ 231−1

signed long

int64_t

64

−9223372036854775808 ⋯ 9223372036854775807

−263 ⋯ 263−1

signed long long

Vorzeichenlose int-Typen

Typname

Bit-Breite

Wertebereich

C-Entsprechung (avr-gcc)

uint8_t

8

0 ⋯ 255

0 ⋯ 28−1

unsigned char

uint16_t

16

0 ⋯ 65535

0 ⋯ 216−1

unsigned short, unsigned int

uint32_t

32

0 ⋯ 4294967295

0 ⋯ 232−1

unsigned long

uint64_t

64

0 ⋯ 18446744073709551615

0 ⋯ 264−1

unsigned long long

Die Programmierung der einzelnen AVR erfolgt in C. Dazu verwende ich als Entwicklungsumgebung unter Windows , wie im Kapitel: Die Toolchain beschrieben, das Atmel Studio und unter Linux die Eclipse-CDT IDE mit dem Eclipse AVR-Plugin.

Im weiteren Verlauf, sollen nach und nach grundlegende Funktionen erstellt werden. Erstes Ziel ist dabei die serielle Kommunikation zwischen dem UART eines AVR und dem UART, erstmal eines PCs. Danach werden wir den I2C-Bus auf den AVR zum laufen bringen, um so dann mehrere AVRs miteinander vernetzen zu können.

2.1 Unser erstes C-Programm für einen AVR

Projektname:

01_Standardprogramm

Erstellt:

23.02.2013

IDE:

AVR Studio 5.1

Controller

ATMega32

Beschreibung:

Beschreibt den Aufbau eines Standardprojekts in C für AVR- Mikrocontroller

C-Quelldateien

Funktion

Standardprogramm.c

Hauptprogramm mit Hauptschleife

H-Headerdateien

Funktion

avr/io.h

Headerdatei für Standard-IO- Definitionen.

Download: Standardprogramm.zip

Schauen wir uns den Quellcode Standardprogramm.c dieses ersten Projekts mal an:

/*

 * Standardprogramm.c

 *

 * Created: 23.02.2013 22:24:17

 *  Author: Scheik

 */

#include <avr/io.h>                                    //(1)

int main(void)                                         //(2)

{

    while(1)

    {

        //TODO:: Please write your application code    //(3)

    }

}

(1): Zuerst wird standardmäßig  per #include <avr/io.h> die in der Entwicklungsumgebung bereits vorhandene Header Datei “io.h” aus der AVR-libc eingebunden. Durch dieses Standard- Headerfile und da wir im Studio beim Erstellen des Projekts bereits angegeben haben, welchen Typ Controller wir verwenden, sind somit z.B alle zum programmieren nötigen Registernamen richtig ansprechbar. Es werden also automatisch alle nötigen Headerdateien eingebunden die zum gewählten AVR gehören.

(2): Das eigentliche C- Programm beginnt mit den Anweisungen in der Funktion main(void){}. Jedes ausführbare C- Programm muss genau eine main()- Funktion haben. Sie legt für den Compiler den so genannten Programmeinstiegspunkt fest.

(3): Den eigentliche Programmcode finden wir dann in der Main()-Funktion. Diese enthält hier eine so genannte Hauptschleife (Main-Loop). In dieser Hauptschleife while(1){} würde der Programmcode nun also in einer Endlosschleife abgearbeitet werden. Der Controller würde also in diesem Programm ganz einfach ununterbrochen nichts tun.

2.2 Der UART der AVR

Schematische Darstellung der Verbindung eines AVR mit der seriellen Schnittstelle des PC

Ziele und Vorgaben:

Um das umzusetzen muss nun ein Versuchsaufbau her, das kann ein gekauftes Experimentierboard oder eine eigene Schaltung mit einem beliebigen AVR sein. Ich verwende hier für Versuchszwecke einen ATMega32, getaktet mit einem externen 16MHz- Quarz.

Bei der direkten Verbindung des UART eines AVR mit dem PC müssen wir die Sende- und Empfangsleitung, des bei 5 Volt betriebenen Controllers, auf den +/-12V-Pegel der RS232- Schnittstelle des PCs bringen. Dazu kann am einfachsten ein Schnittstellentreiber wie z.B. der MAX232 benutzt werden. Die Schaltung dazu sieht dann so aus:

Schaltung für die Verbindung eines AVR mit der seriellen Schnittstelle des PC mittels Pegelwandler MAX232

Die Verbindung zum PC wird dann mit einem 9- poligem Modem-Kabel hergestellt, also einem normalen Verlängerungskabel, keinem Nullmodemkabel mit gekreuzter Sende- und Empfangsleitung.

Hier ein einfaches Programm, mit der Funktionsbibliothek rs232.c, zum testen der seriellen Kommunikation zwischen dem UART des AVR und der RS232-Schnittstelle des PC:

Projektname:

02_RS232_Testprogramm

Erstellt:

23.02.2013

IDE:

AVR Studio 5.1

Controller

ATMega32

Beschreibung:

Testprogramm für die Kommunikation zwischen AVR und PC mittels Hardware- UART

C-Quelldateien

Funktion

RS232_Testprogramm.c

Hauptprogramm mit Main- Funktion

rs232.c

Funktionsbibliothek für serielle Kommunikation mittels Hardware-UART

H-Headerdateien

Funktion

avr/io.h

Headerdatei für Standard-IO- Definitionen.

avr/interrupt.h

Headerdatei für Standard-Definitionen für die Benutzung von Interrupts

global.h

Headerdatei für Globale Deklarationen wie Taktfrequenz, etc.

rs232.h

Headerdatei für die Bibliothek RS232.c

Download

https://github.com/scheik/AVR_UART_Sample.git

Die Einstellungen für die serielle Schnittstelle werden in der, zur Funktionsbibliothek gehörenden, Headerdatei rs232.h vorgenommen. Dazu muss aber zuvor im Code die Taktfrequenz des AVR definiert sein. Ich mache das hier in der Headerdatei global.h. Die Einstellungen für die serielle Kommunikation sind hier im Beispiel auf 19.200 Baud, 8 Bit pro Zeichen, keinem Paritäts- und einem Stoppbit , kurz 8N1, eingestellt.

Was passiert mit diesem Programm?

Ist der AVR korrekt mit dem PC verbunden und werden Daten von der seriellen Schnittstelle des PC an den UART des AVR übertragen, unterbricht der AVR bei jedem neu empfangenen Zeichen sein Hauptprogramm und springt in eine Interruptroutine (ISR- Routine). In dieser ISR- Routine wird dann immer das zuletzt empfangene Byte im RX- Puffer gespeichert bzw. angehängt. Ist dabei letzte empfangene Zeichen ein Zeilenumbruch (CR= Carriage Return) oder ist der Eingangspuffer voll, wird ein Merker (Flag) gesetzt.


Funktionsprinzip der Interruptgesteuerten Programmierung mit Interruptroutine

Im eigentlichen Hauptprogramm des AVR, der Main-Funktion wird dieses Flag in einer Endlosschleife ausgewertet. Ist es gesetzt (also ein CR empfangen oder RX- Puffer voll), werden die empfangenen Daten zur Kontrolle einfach über den UART des AVR an die RS232- Schnittstelle des PC zurück gesendet.

Man erkennt hier bereits den Vorteil der Interruptgesteuerten Programmierung, das Programm läuft quasi auf zwei Ebenen ab. Eine, in der durch Interrupts ausgelöster Code abläuft und eine Ebene, mit dem eigentlichen Hauptprogramm, der Main- Funktion. Das Hauptprogramm wird durch die Interrupts unterbrochen und erst nach Abarbeitung der Anweisungen in der zugehörigen Interruptroutine wieder aufgenommen. In diesem Programm werden Daten von der RS232-Schnittstelle im Hintergrund in einer Interruptroutine empfangen und in ein Array zwischengespeichert. Diese Interruptroutine wird immer dann ausgelöst, wenn ein Byte korrekt vom UART des AVR empfangen wurde, und zur Abholung im Daten- Regigster des UART bereit steht. Somit könnte man im Hauptprogramm nebenbei noch anderen, zeitunkritischen Code ausführen. Eine Alternative wäre dazu nur das ständige Polling der seriellen Schnittstelle. Der AVR wäre dann aber nur damit beschäftigt auf die Schnittstelle zu lauschen. In diesem Fall hier wäre das noch egal, der AVR tut hier auch noch nichts anderes. Später wird man noch mehr Möglichkeiten sehen, für die man die Interrupts eines AVR sinnvoll einsetzten kann.

Wir können nun mithilfe eines einfachen Terminal- Programms testen, ob die Kommunikation zwischen PC und AVR über die serielle Schnittstelle, mithilfe unserer Funktionsbibliothek rs232.c, richtig funktioniert. Ich benutze hier als Terminal- Programm die Freeware Putty :

2.3 Der TWI/ I2C- Bus

Kommen wir zur Vernetzung mehrerer AVR mittels I2C- Bus. Zum Aufbau und Test brauchen wir also zunächst mindestens einen Master und einen Slave.

AVR2PC_I2C.png

Vernetzung zweier AVR mittels I2C- Bus

Wie man sieht wurde die Testumgebung aus dem vorigen Projekt um einen zweiten AVR erweitert. Beide AVR sind über den I2C- Bus miteinander verbunden. Der erste AVR, aus dem vorigen Projekt, mit Verbindung zum PC soll jetzt zusätzlich als Master im I2C- Bus funktionieren. Der zweite AVR,  der Slave, kann einfach ein weiteres Experimentierboard oder eine einfache Selbstbauschaltung mit einem beliebigen AVR sein. Ich verwende dafür ein identisches Board wie für den Master, ebenfalls mit einen ATMega32 ausgestattet und mit einem externen 16MHz- Quarz getaktet.

Die Ziele und Vorgaben sind ähnlich wie im Projekt für die seriellen Schnittstelle, erst mal wollen wir diesen Teil der Hard- und Software, also die Kommunikation des Masters mit zunächst einem Slave im I2C- Bus, zum laufen bringen:

Wir werden im folgenden zwei neue Funktionsbibliotheken mit ihren zugehörigen Headerdateien erstellen. Eine für einen AVR als Master im I2C-Bus, TWI_Master.c und eine für einen AVR als Slave im Bus, die Bibliothek  TWI_Slave.c. Will man später kommerzielle I2C- Bausteine, z.B. einen  PCF8574- Portexpander, am Bus betreiben, reicht die Bibliothek  TWI_Master.c aus.

Bleiben wir aber bei der Verbindung zweier AVR mittels I2C- Bus.

Hardwareseitig müssen für die Verbindung nur die Daten- und Taktleitung des jeweiligen TWI-Interface, also die SDA- und SCL- Portpins der beiden AVR miteinander verbunden werden. Zusätzlich muss dafür gesorgt sein, dass beide Controller auf einem gemeinsamen Massepotential hängen. Um für saubere Pegel an der Daten- und Taktleitung zu sorgen werden noch Pull-Up-Widerstände an den Busleitungen benötigt.

PLATZHALTER SCHALTUNG

 2.3.1 AVR als Master im I2C- Bus

Kommen wir zuerst zum Master, sein Programm muss nebst den Funktionen für den Betrieb als Master im I2C- Bus zusätzlich die Schnittstelle zum PC mittels UART des AVR bereitstellen, dazu können wir einfach die bereits vorhandene Funktionsbibliothek rs232.c verwenden. Alle Funktionen für den Betrieb des AVR als Master im I2C-Bus sind in der wiederverwendbaren Bibliothek TWI_Master.c zu finden. Mit deren Hilfe kann der AVR als Master im Bus initialisiert werden und dann, als Mastertransmitter im MT- Mode oder Masterreceiver im MR- Mode Daten an Slaves übertragen oder von ihnen zum Empfangen anfordern.

Projektname:

03a_I2C_Master_Testprogramm

Erstellt:

23.02.2013

IDE:

AVR Studio 5.1

Controller

ATMega32

Beschreibung:

Testprogramm für AVR (ATMEGA32) als Master im I2C-Bus.

C-Quelldateien

Funktion

Main_I2C_Master.c

Hauptprogramm mit Main- Funktion

rs232.c

Funktionsbibliothek für serielle Kommunikation mittels Hardware-UART

TWI_Master.c

Funktionsbibliothek für Nutzung eines AVR als Master im I2C-Bus, mittels Hardware-TWI-Interface

H-Headerdateien

Funktion

global.h

Headerdatei für Globale Deklarationen wie Taktfrequenz, etc.

rs232.h

Headerdatei für die Bibliothek rs232.c

TWI_Master.h

Header zu Funktionsbibliothek TWI_Master.c

Download:

https://github.com/scheik/AVR_I2C_Master_Sample.git

Alle nötigen Einstellungen für den I2C- Bus wie z.B die Taktrate des Bus werden in der, zur Funktionsbibliothek TWI_Master.c gehörenden Headerdatei TWI_Master.h definiert. Globale Definitionen (FCPU, etc.) findet man wie gehabt im Headerfile global.h.

Zum Ablauf des Programms auf dem Master:

Über die RS232- Schnittstelle des PC, kann eine Zeichenfolge an den Master gesendet werden. Diese Daten werden dann über den I2C-Bus, vom Master im MT-Mode an den Slave gesendet. Dieser speichert über den Bus empfangene Daten in einem Array (Puffer) ab. Unten wird die Software des Slave noch näher beschrieben. Zur Kontrolle wird der Master dann, diesmal im MR- Mode, Daten vom Slave anfordern, einlesen und wieder zur Kontrolle, zurück über die RS232- Schnittstelle, am PC ausgeben.

 2.3.2 AVR als Slave im I2C- Bus

Hier nun das Programm für den AVR, der als Slave im I2C-Bus agiert. Alle für diesen Betrieb nötigen Funktionen sind dazu in der Bibliothek i2c_slave.c enthalten. Zuerst wird der AVR damit als Slave im Bus initialisiert und ihm eine Adresse gegeben. Dann kann der AVR, als Slavetransmitter (ST- Mode) oder Slavereceiver (SR- Mode), von ihm angeforderte Daten senden oder empfangen.

Projektname:

03b_I2C_Slave_Testprogramm

Erstellt:

2

3.02.2013

IDE:

AVR Studio 5.1

Controller

ATMega32

Beschreibung:

Testprogramm für AVR (ATMEGA32)  als Slave im I2C-Bus.

C-Quelldateien

Funktion

Main_I2C_Slave.c

Hauptprogramm mit Main- Funktion

TWI_Slave.c

Funktionsbibliothek für Nutzung eines AVR als Slave im I2C-Bus, mittels Hardware-TWI-Interface

H-Headerdateien

Funktion

“global.h”

Headerdatei für Globale Deklarationen wie Taktfrequenz, etc.

“TWI_Slave.h”

Headerdatei für die Bibliothek TWI_Slave.c mit TWI- Definitionen

Download:

https://github.com/scheik/AVR_I2C_Slave_Sample.git

Test der Kommunikation I2C- Bus

Hat man nun den Master und den Slave programmiert und alles nach obigem Schema aufgebaut, können wir die Funktion des I2C-Bus mit diesen beiden einfachen Testprogrammen erproben. Um den Programmcode übersichtlich zu halten ist die Funktion hier noch sehr einfach. Sendet man Daten, hier einen “String” mit abschließendem Carriage-Return (CR) über die RS232-Schnittstelle des PC an den UART des Master, wird dieser zuerst die empfangenen Daten per I2C als Mastertransmitter an den Slave senden und dann als Masterreceiver wieder von ihm lesen. Zur Kontrolle werden die empfangenen und gesendeten Daten vom Master wieder auf die serielle Schnittstelle des PC ausgegeben. Wir schicken hier also einfach mal Daten im Kreis herum. Dafür reicht auf dem PC oder Raspberry Pi erst mal wieder ein einfaches Terminalprogramm, mit Putty sieht das dann z.B so aus:

Wie man sieht, funktioniert die Kommunikation über den I2C-Bus mittels TWI-Interface der AVR.

Für die weitere Entwicklung haben wir nun, nebst der Funktionsbibliothek rs232.c für die serielle Schnittstelle, zwei weitere Funktionsbibliotheken TWI_Master.c undTWI_Slave.c für die Nutzung des I2C-Bus zur Verfügung.

Kommen wir zu einem weiteren wesentlichen Element mit dem man bei der Programmierung der AVR immer arbeiten wird, die Timerbausteine der AVR.

2.4 Die Timer der AVR

Timer/Counter sind Bausteine/Register eines AVR, bei deren Verwendung ein Zähler in Form des jeweiligen Timer-Registers z.B. im Systemtakt hochgezählt wird. Erreicht dieser Zähler seinen maximal erreichbaren Wert, läuft also über (Overflow), wird er zurückgesetzt und beginnt wieder von vorne hoch zu zählen. Dies alles passiert dabei im Hintergrund, allein durch die Hardware des AVR, zunächst ohne das eigentliche Programm zu beeinflussen.

Das alleine wäre aber noch nicht nützlich. Deswegen kann man aber z.B. bei jedem Überlauf des Zählers oder immer wenn der Zähler einen bestimmten Wert erreicht, das so genannte Compare Match, vom Timerbaustein einen Interrupt auslösen lassen. Auf diesen Interrupt kann man dann im eigentlichen Programm reagieren.

Da zum hochzählen des Timer-Registers entweder der Systemtakt des AVR oder eine externe Taktquelle verwendet werden kann, können diese Bausteine sowohl als Zeitgeber (Timer) als auch als Zähler (Counter) agieren.

Die heutigen Mikrocontroller und insbesondere die RISC-AVRs sind für viele Steuerungsaufgaben zu schnell. Wenn wir beispielsweise eine LED oder Lampe blinken lassen wollen, können wir selbstverständlich nicht die CPU-Frequenz verwenden, da ja dann nichts mehr vom Blinken zu bemerken wäre. Wir brauchen also eine Möglichkeit, Vorgänge in Zeitabständen durchzuführen, die geringer als die Taktfrequenz des Controllers sind. Selbstverständlich sollte die resultierende Frequenz auch noch möglichst genau und stabil sein

Der Vorteiler dient dazu, den CPU-Takt vorerst um einen einstellbaren Faktor zu reduzieren. Die so geteilte Frequenz wird den Eingängen der Timer zugeführt. Wenn wir mit einem CPU-Takt von 4 MHz arbeiten und den Vorteiler auf 1024 einstellen, wird der Timer mit einer Frequenz von 4 MHz / 1024, also mit etwas weniger als 4 kHz versorgt. Wenn der Timer läuft, wird das Zählregister (TCNTx) mit dieser Frequenz inkrementiert.

Die Möglichkeiten für die Anwendung von Timerbausteinen sind vielfälltig, mögliche Beispiele wären,

Übersicht über die gängigen Timer der AVR

Timer0

Timer1

Timer2

8-Bit- Timer

16-Bit-Timer

8-Bit-Timer

ATMega8

ATMega16, 32, 644

Normal Mode

Normal Mode

CTC Mode

Fast PWM Mode

Phase Correct PWM Mode

ATMega8, 32

ATMega16

Normal Mode

Normal Mode

CTC Mode

CTC Mode

Fast PWM Mode

Fast PWM Mode

Phase Correct PWM Mode

Phase Correct PWM Mode

Phase and Frequency

Correct PWM Mode

ATMega8, 16, 32, 644

Normal Mode

CTC Mode

Fast PWM Mode

Phase Correct PWM Mode

2.4.1 Die Timerregister der AVR

Die Timer werden, wie oben beschrieben, über bestimmte Register angesprochen, gesteuert und konfiguriert. So besitzt jeder Timer ein eigenes Timer/Counter Control Register, TCCRn oder sein eigentliches Zählregister das so genannte Timer/Counter Register, TCNTn. Im folgenden werden alle zum Betrieb der Timer nötigen Register näher vorgestellt, schauen wir zunächst aber das Kontrollregister der 8 Bit Timer genauer an, mit dessen einzelnen Bits die eigentliche Funktion des jeweiligen Timers eingestellt wird.

Das Kontrollregister TCCRn der 8 Bit Timer

Bit

7

6

5

4

3

2

1

0

FOCn

WGMn0

COMn1

COMn0

WGMn1

CSn2

CSn1

CSn0

Read/Write

W

R/W

R/W

R/W

R/W

R/W

R/W

R/W

Initial Value

0

0

0

0

0

0

0

0

Wie man sieht sind die Kontrollregister TCCR0 und TCCR2 der beiden 8-Bit-Timer analog aufgebaut.

Neben den drei Clock-Select-Bits, für die Wahl des Prescalers findet man in den Kontrollregistern der 8-Bit Timer die beiden Bits WGMn0 und WGMn1, sie bestimmen den so genannten Waveform Generation Mode, also den Betriebsmodus, in dem der Timer arbeiten soll. Daneben findet man die beiden Bits COMn0 und COMn1, mit denen der  Compare Match Output Mode des Timers konfiguriert wird.

Eine Ausnahme ist hier der Timer0 des ATMega8, in dessen Kontrollregister TCCR0 nur die Clock-Select-Bits beschrieben werden können, da er keine speziellen Modi zulässt:

Bit

7

6

5

4

3

2

1

0

-

-

-

-

-

CS02

CS01

CS00

Read/Write

R

R

R

R

R

R/W

R/W

R/W

Initial Value

0

0

0

0

0

0

0

0

Schauen wir uns nun die einzelnen Bits des Kontrollregisters und ihre Funktion für die Timer genauer an, betrachten wir zuerst die Clock-Select-Bits:

Clock-Select: Die Bits CS0, CS1 und CS2

Werden die Timer über den eigenen Systemtakt gesteuert, kann dieser über einen Vorteiler, den so genannten Prescaler herunterskaliert werden. Mögliche Werte für den Prescaler sind 1, 8, 64, 256 und 1024. So würde zum Beispiel ein Timer, bei einem Systemtakt von 8 MHz und einem gewählten Prescaler von 8, mit 8MHz/8 = 1MHz laufen.

Timer0

Timer1

Timer2

CS02

CS01

CS00

Funktion

0

0

0

Timer Aus

0

0

1

Clk/1 = kein Prescaler

0

1

0

Clk/8

0

1

1

Clk/64

1

0

0

Clk/256

1

0

1

Clk/1024

1

1

0

ext. Takt an T0 -> fallende Flanke

1

1

1

ext. Takt an T0 -> steigende Flanke

CS12

CS11

CS10

Funktion

0

0

0

Timer Aus

0

0

1

Clk/1 = kein Prescaler

0

1

0

Clk/8

0

1

1

Clk/64

1

0

0

Clk/256

1

0

1

Clk/1024

1

1

0

ext. Takt an T1 -> fallende Flanke

1

1

1

ext. Takt an T1 -> steigende Flanke

CS22

CS21

CS20

Funktion

0

0

0

Timer Aus

0

0

1

clk/1 = kein Prescaler

0

1

0

clk/8

0

1

1

clk/32

1

0

0

clk/64

1

0

1

clk/128

1

1

0

clk/256

1

1

1

clk/1024

Kontrollregister: TCCR0

Kontrollregister: TCCR1B

Kontrollregister: TCCR2

Mit den Clock-Select-Bits wird also der Prescaler festgelegt und der Timer Ein- und Aus geschaltet.

Bei den beiden 8 Bit Timern Timer0 und Timer2 findet man die Clock-Select-Bits in den Registern TCCR0 und TCCR2. Da der Timer1 ein 16- Bit Timer ist, ist auch das Kontrollregister dieses Timers 16- Bit breit. Bedingt durch die 8-Bit Architektur der AVR wurde es in die zwei 8-Bit Register TCCR1A und TCCR1B aufgeteilt. Die Clock-Select-Bits für Timer1 findet man also im Register TCCR1B.

Neben den Clock-Select-Bits, spielen für die Funktion eines Timers aber noch andere Bits im Kontrollregister eine Rolle, bleiben wir hier zunächst bei den 8 Bit Timern, also den zugehörigen Registern TCCR0 und TCCR2.

Waveform Generation Mode: Die Bits WGM0 & WGM1 der 8-Bit Timer

Die Wahl des Betriebsmodus der 8-Bit Timer erfolgt durch die Bits WGMn0 und WGMn1 im Kontrollregister TCCRn

Mode

WGMn1

WGMn0

Betriebsmodus

TOP

Update von OCRn

TOVn Flag gesetzt wenn

0

0

0

Normal

0xFF = 255

Immediate

MAX

1

0

1

PWM, Phase Correct

0xFF

TOP

BOTTOM

2

1

0

CTC

OCRn

Immediate

MAX

3

1

1

Fast PWM

0xFF

BOTTOM

MAX

Compare Match Output Mode: Die Bits COM0 & COM1 der 8-Bit Timer

Kommen wir zu zwei weiteren Bits im Kontrollregister, die für die Funktion des Timers eine Rolle spielen, den beiden Bits COMn0 und COMn1. Mit diesen beiden Bits kann der jeweilige Hardware- Ausgang (z.B. OC0 für Timer0) an den Timer gekoppelt werden, z.B. wenn der Timer zur Signalerzeugung (Hardware-PWM) benutzt wird. Ihre Funktion hängt dabei von dem, durch die Bits WGMn0 und WGMn1, gewählten Betriebsmodus des Timers ab.

Normal und CTC Mode

Fast PWM Mode

Phase Correct PWM Mode

Kommen wir zum Timer1, wie gesagt besitzt der 16-Bit Timer ein 16 Bit- breites Kontrollregister aufgeteilt in zwei 8 Bit Register. Außerdem bietet er mehr Funktionalität als die 8-Bit Timer, betrachten wir seine beiden Kontrollregister TCR1A und TCR1B werden wir feststellen dass seine Inbetriebnahme aber analog zu den 8-Bit Timern geschieht.

TCCR1A

Bit

7

6

5

4

3

2

1

0

COM1A1

COM1A0

COM1B1

COM1B0

FOC1A

FOC1B

WGM11

WGM10

Read/Write

R/W

R/W

R/W

R/W

W

W

R/W

R/W

Initial Value

0

0

0

0

0

0

0

0

TCCR1B

Bit

7

6

5

4

3

2

1

0

ICNC1

ICES1

-

WGM13

WGM12

CS12

CS11

CS10

Read/Write

W

R/W

R/W

R/W

R/W

R/W

R/W

R/W

Initial Value

0

0

0

0

0

0

0

0

Der Start des Timers und die Wahl des Prescalers erfolgt wie bekannt durch die drei Clock-Select-Bits, CS10, CS11 und CS12

Waveform Generation Mode: Die Bits WGM10, WGM11, WGM12 und WGM13 des 16-Bit Timers

Der Betriebsmodus des Timers wird beim Timer1 durch die 4 Bits WGM13, WGM12, WGM11 und WGM10 bestimmt

Compare Match Output Mode: Die Bits COM1A0 & COM1A1, COM1B0 und COM1B1 des 16-Bit Timers

Timerinterrupts und das Timer/Counter Interrupt Mask Register - TIMSK

Bei der seriellen Kommunikation mit dem UART des AVR haben wir bereits einen Interrupt des AVR kennengelernt und die Funktion von Interrupts kurz beschrieben. Bei einem bestimmten Ereignis, oben war es immer dann, wenn im UART ein Zeichen korrekt empfangen wurde, wird ein so genannter Interrupt ausgelöst und wir können in einer Interruptroutine darauf reagieren. Auch die Timer der AVR bieten uns eine Menge nützlicher Interrupts.

Adresse

Interruptname

Beschreibung

0x003

TIMER2_COMP

Timer/Counter2 Compare Match

0x004

TIMER2_OVF

Timer/Counter2 Overflow

0x005

TIMER1_CAPT

Timer/Counter1 Capture Event

0x006

TIMER1_COMPA

Timer/Counter1 Compare Match A

0x007

TIMER1_COMPB

Timer/Counter1 Compare Match B

0x008

TIMER1_OVF

Timer/Counter1 Overflow

0x009

TIMER0_OVF

Timer/Counter0 Overflow

Das Timer/Counter Interrupt Mask Register - TIMSK

Bit

7

6

5

4

3

2

1

0

OCIE2

TOIE2

TICIE1

OCIE1A

OCIE1B

TOIE1

.

TOIE0

Read/Write

R/W

R/W

R/W

R/W

R/W

R/W

R/W

R/W

Initial Value

0

0

0

0

0

0

0

0

In den folgenden praktischen Beispielen wollen wir anhand einfacher Codebeispiele die verschiedenen Betriebsmodi und die gängigen Interrupts der Timer eines AVR kennenlernen. Beginnen wir mit dem einfachsten aller Fälle, dem Timer0 im Normal Mode, dabei werden wir den Overflow Interrupt kennenlernen, welchen man auch bei jedem anderen Timer finden kann.

2.4.2 Timer im Normal Mode mit Overflow Interrupt

Dieses Programm soll zeigen, wie man den Timer0 im Normal Mode konfiguriert und auf einen seiner Interrupts, hier dem Overflow Interrupt reagiert wird. Ich habe dazu einen ATMega8 mit einer internen Taktfrequenz von 8MHz, verwendet.

#include <avr/io.h>

#include <avr/interrupt.h>

int main(void)

{

   // Timer0 initialisieren

   TCCR0 = (1<<CS02)|(1<<CS00);                                                // (1) Prescaler 1024

   TIMSK |= (1<<TOIE0);                                                                // (2) Overflow Interrupt erlauben

   sei();                                                                                // (3) Globale Interrupts ein

        

   DDRB |= (1 << PB1);                                                            // (4) Pins PB1 als Ausgang definieren        

        

   while(1)

   {

                                                                                        // (5) Programm-Endlosschleife

   }

}

ISR (TIMER0_OVF_vect)

{ // Interrupt alle (8000000MHz/1024)/256 Hz = 30,5175Hz

  // bzw. 1/30,5175s = 32,8ms

  PORTB ^= (1 << PB1);                                                          // (6) Ausgang PB1 toggeln

}

Zum Programm: Nach dem Einbinden der beiden Standard- Includes avr/io.h und avr/interrupt.h aus der avr-gcc Library folgt auch schon die eigentliche Mainfunktion “int main(void“ des Programms. Diese startet auch direkt mit der Initialisierung des Timer0.

(1) Wie wir wissen, muss man zum Starten des Timers und wählen des Prescalers die richtigen Clock-Select-Bits im Kontrollregister des Timers setzten. Wir initialisieren den Timer hier im Beispiel mit einem Prescaler=1024. Aus dem Datenblatt, oder obiger Tabelle, kann man entnehmen, dass dazu die beiden Bits CS00 und CS02 im Kontrollregister TCCR0 gesetzt werden müssen:

TCCR0 = (1<<CS02)|(1<<CS00);                                                // (1) Prescaler 1024

Ab nun läuft der Timer und zählt dabei sein Timer/Counter Register hoch. Mit einer Frequenz von 8Mhz/1024, also mit 7812,5Hz immer von 0 bis 256 und wieder von vorn. Für einen Schritt wird dabei also 1/7812,5Hz = 0,128ms benötigt. Allgemein ist die Frequenz des Timers also immer gleich dem Quotient aus Taktfrequenz des AVR und dem eingestellten Prescaler.

(2)+(3) Nun wollen wir den Overflow Interrupt des Timers einschalten. Dieser wird  jedesmal ausgelöst, wenn der Timer überläuft, also seinen höchsten Wert, hier 255 erreicht und wieder bei 0 anfängt zu zählen. Dem Datenblatt des ATMega8 kann man entnehmen, dass man diesen Interrupt aktiviert, indem man das Bit TOIE0 im Timer/Counter Interrupt Mask Register, TIMSK setzt. Natürlich müssen für die korrekte Funktion des Interrupts, diese auch wieder durch die Funktion sei() global erlaubt sein. Beides erreicht man mit den folgenden Codezeilen:

TIMSK |= (1<<TOIE0);                                                        // (2) Overflow Interrupt erlauben

sei();                                                                        // (3) Globale Interrupts ein

Dieser Overflow Interrupt wird von nun an, bei den insgesamt 256 Schritten des Timers, rechnerisch mit einer Frequenz von 7812,5Hz/256 = 30,52Hz erfolgen, also alle 32,8ms ausgelöst.

(4) Zur Kontrolle der Funktion des Programms wollen wir einfach bei jedem Timerüberlauf einen als Ausgang geschalteten Pin des AVR  toggeln. Dieser Pin muss also noch als Ausgang initialisiert werden, was in der folgenden Zeile geschieht.

DDRB |= (1 << PB1);                                                            // (4) Pins PB.1 als Ausgang definieren

(5) In der Programm- Endlosschleife “while(1){}”, dem eigentlichen Hauptprogramm passiert gar nichts. Das Hauptprogramm wird von nun an aber bei jedem Overflow Interrupt des Timer0 für die Interruptroutine ISR (TIMER0_OVF_vect) zur Abhandlung dieses Ereignisses unterbrochen.

(6) Die Interruptroutine für den Timerüberlauf von Timer0 finden wir dann nach der Hauptfunktion des Programms. In Ihr passiert hier nichts weiter, als dass der Pin PB.1 des AVR getoggelt, also abwechselnd An- und Aus geschaltet wird.

ISR (TIMER0_OVF_vect)

{ // Interrupt alle (8000000MHz/1024)/256 Hz = 30,5175Hz

  // bzw. 1/30,5175s = 32,8ms

  PORTB ^= (1 << PB1);                                                // (6) Ausgang PB.1 toggeln

}

Dieses Programm zeigt eine einfache Möglichkeit bestimmte Funktionen und Operationen, hier im Beispiel das toggeln eines Portpins, in regelmäßigen Zeitabständen, mit Hilfe eines Timers ausführen zu lassen. Die Periode und Frequenz des Timers und des Overflow Interrupts hängt dabei von der Taktrate des AVR, der Bitbreite des Timers und dem eingestellten Prescaler ab

Ftimer= Fclk / N

Fovfl= Fclk/ N x TCNTn

Timer0 bei 8MHz

Clock-Select-Bits

CS02 CS01 CS00

Prescaler

Timertakt

Periodendauer Timer

Overflow-Takt

Periodendauer Overflow

 0 0 1

1

8 000 000 Hz

0,125 µs

31 250 Hz

32 µs

0 1 0

8

1 000 000 Hz

1 µs

3906,25 Hz

256 µs

0 1 1

64

125 000 Hz

8 µs

488,28 Hz

2,05ms

1 0 0

256

31 250 Hz

32 µs

122,07 Hz

8,19 ms

1 0 1

1024

 7 812,5 Hz

128 µs

30,55 Hz

32,77ms

Timer0 bei 16MHz

Clock-Select-Bits

CS02 CS01 CS00

Prescaler

Timertakt

Periodendauer Timer

Overflow-Takt

Periodendauer Overflow

 0 0 1

1

16 000 000 Hz

0,0625 µs

62,5 kHz

16 µs

0 1 0

8

2 000 000 Hz

2 µs

ca 8kHz

128 µs

0 1 1

64

250 000 Hz

4 µs

ca 1 kHz

ca 1 ms

1 0 0

256

62 500 Hz

16 µs

ca. 244 Hz

ca, 4 ms

1 0 1

1024

15 625 Hz

64 µs

ca 60 Hz

ca 16 ms

Will man dabei eine hohe Genauigkeit der Timer erreichen, ist man dabei auf eine ausreichend hohe Genauigkeit der Taktfrequenz des AVR angewiesen. Verwendet man den internen RC- Oszillator eines AVR muss hierzu eine Besonderheit beachtet werden.

2.4.3 Kalibrierung des internen RC-Oszillators

Ein AVR kann mit seinem internen RC- Oszillator bei 1, 2, 4 oder 8MHz betrieben werden. Dies wird über die Fusebits eingestellt. Der AVR ist werksseitig auf einen internen RC- Oszillator von 1MHz voreingestellt und laut Datenblatt wird bei jedem Reset, also auch Neustart des Prozessors das Calibration Byte für 1MHz in das Register OSCCAL geladen. Diese Calibration Bytes werden werksseitig festgelegt und sind zwar für alle 4 möglichen Taktfrequenzen, also auch für die Werte 2, 4 und 8MHz in der Signaturreihe des AVR hinterlegt, nur leider wird bei Verwendung des internen RC- Oszillator bei den AVR der ATMega- Reihe, egal mit welcher Taktfrequenz wir sie letztlich betreiben, immer automatisch das Calibration Byte für 1MHz in das Register OSCCAL geladen. Dies führt laut Datenblatt insgesamt zu einer Abweichung bei den möglichen Taktfrequenzen  von bis zu +/-3% bei 25° und 5V.

Messen wir nun einmal das tatsächliche Timing unseres Programms mit einem Oszilloskop oder Frequenzzähler an PB.1. In unserem Beispiel mit 8MHz Taktfrequenz und einem Prescaler = 1024 müsste an ihm ein Rechtecksignal wie oben dargestellt mit einer Ein- und Ausschaltzeit von rechnerisch 32,8ms zu messen sein.

Messwerte

theoretische Werte

Abweichung

F = 31,43Hz

T = 31,81ms

F = 30,52Hz

T = 32,77ms

2,92%

Messwerte bei einem AVR, bei 5V, 20° und internem RC Oszillator von 8MHz ohne Anpassung von OSCCAL

Wie man sieht ist die gemessene Abweichung, hier bei 8MHz, schon bei ca.3%. Verwendet man eine andere Taktfrequenz als die werksseitig eingestellten 1MHz und will die Genauigkeit des internen RC-Oszillators noch erhöhen, muss dazu das passende Calibration Byte, aus der Signaturreihe des AVR, in das Programmregister OSCCAL geladen werden. Das Datenblatt verspricht unter Anwendung dieser Methode dann eine Genauigkeit der Taktfrequenz von +/-1% bei 25° und 5V.

Dazu müssen wir das richtige Calibration Byte aber zuerst mal aus der Signaturreihe des verwendeten AVR auslesen, da es sie sich von Controller zu Controller unterscheiden kann. Ich benutze dafür unter Windows das AVR Studio 4, da das Studio ab Version 5 das Auslesen der Calibration Bytes leider nicht mehr unterstützt.

Wie man sieht, hat das werksseitig festgelegte Calibration Byte für den hier verwendeten AVR, für eine Taktfrequenz von 8MHz mit Verwendung des internen RC-Oszillators, den Wert 0xAB. Diesen Wert laden wir nun also nur noch im Programmcode in das Register OSCCAL. Wir machen das gleich zu Beginn des eigentlichen Programms, noch vor der Initialisierung des Timers.

Das Programm wurde also nur um  den Befehl OSCCAL=0xAB; erweitert und sieht nun folgendermaßen aus:

#include <avr/io.h>

#include <avr/interrupt.h>

int main(void)

{

   OSCCAL=0xAB;                                            // Calibration-Byte für intern 8 MHz

   // Timer0 initialisieren

   TCCR0 = (1<<CS02)|(1<<CS00);                                                // (1) Prescaler 1024

   TIMSK |= (1<<TOIE0);                                                                // (2) Overflow Interrupt erlauben

   sei();                                                                                // (3) Globale Interrupts ein

        

   DDRB |= (1 << PB1);                                                            // (4) Pins PB1 als Ausgang definieren        

        

   while(1)

   {

                                                                                        // (5) Programm-Endlosschleife

   }

}

ISR (TIMER0_OVF_vect)

{ // Interrupt alle (8000000MHz/1024)/256 Hz = 30,5175Hz

  // bzw. 1/30,5175s = 32,8ms

  PORTB ^= (1 << PB1)| (1 << PB2);                                                // (6) Ausgang PB1 toggeln

}

Messen wir nun wieder das Timing unseres Programms mit einem Oszilloskop oder Frequenzzähler an PB.1, diesmal mit angepassten Register OSCCAL, so stellen wir fest, dass sich die Genauigkeit des Timers wesentlich erhöht hat:

Messwerte

theoretische Werte

Abweichung

F = 30,51Hz

T = 32,78ms

F = 30,52Hz

T = 32,77ms

0,03%

Messwerte bei einem AVR, bei 5V, 20° und internem RC Oszillator von 8MHz mit Anpassung von OSCCAL

Mit dem Register OSCCAL kann die Taktfrequenz des internen RC-Oszillator auch noch exakter kalibriert werden, das wäre dann aber auch bei weitem aufwendiger und soll hier nicht das Thema sein.

Wie man im obigen Beispiel sieht, ist die Timerfrequenz und damit auch die Frequenz mit der der Overflow Interrupt auftritt recht starr, nur von der Taktfrequenz des AVR und dem Prescaler abhängig. Eine Möglichkeit die Frequenzen und Perioden feiner anzupassen besteht darin, den Zählerstand des Registers TCNT0 nach einem Overflow auf einen Wert größer null zu setzten, quasi

vorzustellen. Somit bleiben bis zum nächsten Überlauf weniger Schritte und die Frequenz des Timer Overflow würde sich erhöhen. Da das eigentliche Timer/Counter Register TCNT0 vollen Schreib- und Lesezugriff bietet können wir so auch im Normal Mode die Frequenz des Timer Overflows anpassen.

TCNT0 = Fclk / Fovfl x N

Im Programm machen wir dazu mal folgendes, wir stellen den Zähler des Timers, also das Registers TCNT0, in der Overflow Interruptroutine auf einen bestimmten Wert vor, so dass alle folgenden Overflow Interrupts mit einer exakten Frequenz von 50Hz, also mit 20ms Periodendauer auftreten sollen. Nach obiger Gleichung müsste unser Timer also bei 8MHz Taktfrequenz des AVR, einer gewünschten Frequenz von 50Hz und einem Prescaler = 1024 nur auf 156 Schritte zählen. Da der Timer0 im Normal Mode aber immer bis zu einer festen Obergrenze TOP = 255 zählt, müssen wir den Timer also um 255-156 = 99 Schritte vorstellen.

#include <avr/io.h>

#include <avr/interrupt.h>

int main(void)

{

   OSCCAL=0xAB;                                            // Calibration-Byte für intern 8 MHz

   // Timer0 initialisieren

   TCCR0 = (1<<CS02)|(1<<CS00);                                                // (1) Start T0 mit Prescaler 1024

   TIMSK |= (1<<TOIE0);                                                                // (2) Overflow Interrupt erlauben

   sei();                                                                                // (3) Globale Interrupts ein

        

   DDRB |= (1 << PB1);                                                            // (4) Pin PB1 als Ausgang definieren        

        

   while(1)

   {

                                                                                        // (5) Programm-Endlosschleife

   }

}

ISR (TIMER0_OVF_vect)

{ // Interrupt alle (8000000MHz/1024)/256 Hz = 30,5175Hz

  // bzw. 1/30,5175s = 32,8ms

   

  TCNT0 = 99;                                                     // (*) Timer Overflow alle 20ms

  PORTB ^= (1 << PB1);                                                         // (7) Ausgang PB1 toggeln

}

Projektname:

04a_T0_Normal_Mode_Overflow_Interrupt

Erstellt:

23.02.2013

IDE:

AVR Studio 5.1

Controller

ATMega8

Beschreibung:

Testprogramm für AVR als Slave im I2C-Bus.

C-Quelldateien

Funktion

xxxx.c

Hauptprogramm mit Main- Funktion

H-Headerdateien

Funktion

avr/io.h

Headerdatei für Standard-IO- Definitionen.

avr/interrupt.h

Headerdatei für Standard-Definitionen für die Benutzung von Interrupts

Download:

xxxxx.zip

Modernere AVRs kennen allerdings noch einen eleganteren Weg zur Anpassung der Interrupt-Frequenz: den CTC Modus

2.4.4 Timer im CTC Mode mit Compare Interrupt

Noch kein Inhalt

2.5 PWM mit einem AVR

Im Zuge dieses Abschnitts werden wir die Ansteuerung von Modellbauservos mittels eines Pulsweitenmodulierten Signals, kurz PWM- Signal, kennenlernen. Für einen Servocontroller, als erstes Slave-Projekt für die Modulare Steuerung, habe ich mich entschieden, weil es in der Robotik viele denkbare Anwendungsmöglichkeiten für Modelbauservos gibt. Nebst den Schrittmotoren wird man in vielen Robotikanwendungen immer wieder auf Modellbauservos stoßen. Diese Stellmotoren sind verhältnismäßig leicht anzusteuern, da sie die für den Betrieb nötige Regel- und Leistungelektronik bereits integriert haben. Die Ansteuerung erfolgt dabei aber bei allen Modellbauservos gleich, über ein PWM- Signal.

Die Position gängiger Servos wird dabei über einen Impuls mit bestimmter Länge, im Bereich zwischen 1 bis 2ms bei einer Wiederholrate von z.B. 20ms gesteuert, also bei einer Frequenz von 50Hz. Die Wiederholrate des PWM-Signals kann aber generell zwischen 10 bis 30ms liegen. Entscheidender für die Genauigkeit des Servos ist dabei das so genannte Tastverhältnis des PWM-Signals, also die möglichst genaue Erzeugung des variablen Impulses mit einer Ein- und Ausschaltzeit im Bereich zwischen 1 und 2ms, bei einer ausreichend hohen Auflösung.

Wir müssen mit unserem AVR, zur Ansteuerung eines Modellbauservos, also ein Rechtecksignal mit einer variablen Ein- und Ausschaltzeit und mit hohen Auflösung im Bereich zwischen 1-2ms, bei einer festen Grundfrequenz von 20ms erzeugen. Bei einem Blick ins Datenblatt und mit dem Wissen aus Kapitel zu den Timern eines AVR sieht man, dass sich dafür die Timerbausteine eines AVR anbieten. Die Timer eines AVR bieten verschiedene Möglichkeiten zur Erzeugung von PWM- Signalen, reines Hardware- PWM ist dabei die bequemste Lösung mit dem geringsten Programmieraufwand. Man ist dann aber, je nach verwendetem AVR, auf eine durch die Hardware begrenzte Anzahl von PWM- Kanälen beschränkt. Will man eine größere Anzahl Modellbauservos gleichzeitig ansteuern kann man die PWM- Signale aber auch an den Portpins des AVR per Soft- PWM erzeugen, auch hierzu nutzt man die Timer eines AVR.

2.5.1 Hardware-PWM

Zuerst wollen wir uns anschauen, wie man mit einem AVR auf ganz einfache Weise, mithilfe des Timer1 im Fast PWM Mode, bis zu zwei Modellbauservos gleichzeitig per reinem Hardware-PWM ansteuern kann. Dieser 16 Bit Timer steht auch allen kleinen AVR, z.B. dem ATMega8 oder den ATinys zur Verfügung. Die 16 Bit Auflösung des Timer1 ermöglichen uns die bequemste und einfachste Art Modellbauservos mit hoher Auflösung anzusteuern, indem wir dazu reines Hardware- PWM benutzen. Der dafür nötige Code wird sich dann auf wenige Zeilen beschränken. Ist der Timer1 erst mal richtig konfiguriert, kann die Ansteuerung der Servos dann nämlich ganz einfach, über das Schreiben von Werten in die richtigen Register des AVR erfolgen. Die Erzeugung der PWM- Signale wird uns dann komplett von der Hardware des AVR abgenommen und stehen dann an den beiden Timerausgängen OCRA und OCRB des AVR zur Verfügung. Der Nachteil dabei ist aber, dass an Timer1 so, ohne weiteren Aufwand, maximal zwei Servos gleichzeitig betrieben werden können. Zudem ist dann der, oft einzige 16 Bit Timer des AVR vergeben, und kann keine weiteren Aufgaben mehr übernehmen.

Ein einfacher Versuchsaufbau zum Betrieb von zwei Modellbauservos an einem ATMega8 mit dem 16 Bit Timer1

Ein einfaches C- Programm, dass die bequeme Art der  Ansteuerung von bis zu zwei Modellbauservos im Fast PWM Mode mit dem 16 Bit Timer1 eines AVR demonstriert:

Projektname:

04b_Servo_Timer1_Testprogramm

Erstellt:

23.02.2013

IDE:

AVR Studio 5.1

Controller

ATMega8

Beschreibung:

Testprogramm für Ansteuerung von zwei Modellbauservos mittels “Fast PWM Mode” am 16 Bit Timer1 eines AVR

C-Quelldateien

Funktion

Servo_Timer1_Testprogramm.c

Hauptprogramm mit Main- Funktion

H-Headerdateien

Funktion

avr/io.h

Headerdatei für Standard-IO- Definitionen.

avr/interrupt.h

Headerdatei für Standard-Definitionen für die Benutzung von Interrupts

global.h

Headerdatei für Globale Deklarationen wie Taktfrequenz, etc.

Download:

Servo_Timer1_Testprogramm.zip

Im Programm wird zunächst der Timer1 für den Fast PWM Mode konfiguriert und im Register ICR eine neue Obergrenze eingestellt, bis zu der der Timer nun, statt auf TOP, zählen wird. Damit legt man die Wiederholrate von 20ms bei 50Hz fest. Durch schreiben eines Wertes in die Vergleichsregister OCR1A und OCR1B zwischen 1000 (ganz Links) und 2000 (ganz Rechts) können die Servos nun in eine beliebiege Position, in theoretisch 1000 Schritten, gesteuert werden.

DIAGRAMM FAST PWM OCR1A/OCR1B

In diesem Testprogramm werden, in einer Endlosschleife, die beiden Register einfach immer um eins bis zu ihrem Maximum hoch-und anschließend wieder heruntergezählt, die angeschlossenen Servos bewegen sich somit zwischen ihren Endstellungen hin- und her.

STRUCT SERVO_TIMER1_TESTPROGRAMM

2.5.2 Soft-PWM

Zieht man in Betracht, zusätzliche Servos direkt an den weiteren Timern Timer0 und Timer2 zu betreiben, wird man feststellen, dass reines Hardware-PWM z.B. im Fast PWM Mode bei 8 Bit einen gravierenden Nachteil hat, man wird so keine hohe Auflösung bei der Positionierung der Modellbauservos erreichen. Will man mehrere Modellbauservos gleichzeitig bei großer Auflösung betreiben muss man einen anderen Weg einschlagen, den, des so genannten Soft-PWM. Die Erzeugung der Signale erfolgt dann zwar auf der Zeitbasis der Timer, aber an beliebigen Portpins des AVR. Geschickt programmiert lassen sich so auch mehrere Servos gleichzeitig ansteuern, prinzipiell gibt es dazu verschiedene Lösungsansätze, wir wollen hier folgende Idee verfolgen,:

Dabei können bis zu 8 Servos gleichzeitig, an einem Port des AVR betrieben werden. Folgende Grafik soll die Funktion des Programms verdeutlichen:

Wahl der Timer und deren Betriebsarten

Für die Grundfrequenz von 50Hz wird Timer0 benutzt. Da die Software auch auf einem ATMega8 laufen soll, und dessen Timer0 keinen CTC Mode unterstützt wird der Timer0 im Normal Mode betrieben. Die Frequenz des genutzten Overflow Interrupts von Timer0 wird dabei durch setzten seines Timer/Counter Registers TCCNT0 in der Interruptroutine auf exakt 50Hz angepasst.

Um Timer0 im Normal Mode zu starten müssen wir die Clock- Select- Bits im Kontrollregister TCNT0 entsprechend des gewählten Prescalers setzten:

TCCR0 = (1<<CS02)|(1<<CS00);                // (1) Prescaler 1024

Da wir den Overflow Interrupt des Timer0 nutzen wollen, müssen wir nun noch das entsprechende Bit TOIE0 im Timer Status Register TIMSK setzten und Interrupts global erlauben:

TIMSK |= (1<<TOIE0);                                // (2) Overflow Interrupt erlauben

sei();                                                // (3) Globale Interrupts ein

Schon wird die Interruptroutine für den Timer Overflow des Timer0 bei jedem Überlauf aufgerufen, damit dies alle 20ms geschieht, passen wir in ihr noch das Timer/Counter Register TCNT0 entsprechend an:

ISR (TIMER0_OVF_vect)

{

  TCNT0 = 0x63;                                        // (6) Timer-Overflow alle 20ms 

}

In dieser Interruptroutine, sollen nun hintereinander die Impulse zur Ansteuerung der 8 Servos erzeugt werden. Dazu wird der 16 Bit Timer1 im CTC Mode verwendet. Schauen wir dazu zuerst den Ablauf für einen einzelnen Servoimpuls an. Wir schalten dazu in der Overflow Interruptroutine des Timer0 einen Pin ein, starten dann den Timer1 im CTC Mode. Sein Vergleichsregister OCR1A wird so gesetzt, dass dieses nach einer bestimmten Zeit, hier 1,5ms für die Mittelstellung eines Servos, erreicht wird.  Anschließend warten wir nur noch bis das Bit OCF1A  für das Compare Match im Register TIFR gesetzt ist, schalten dann den Pin und Timer1 wieder aus und setzten das Bit OCF1A zurück. Fertig ist der eine Servoimpuls, hier an PD.1, alle 20ms.

ISR (TIMER0_OVF_vect)

{

  TCNT0 = 0x63;                                        // Timer-Overflow alle 20ms 

  // 1 Servoimpuls erzeugen

  PORTD |= (1 << PD1);                        // Ausgang PD.1 einschalten

  TCCR1B |= (1<<WGM12)|(1<<CS11);        // Timer1 einschalten, CTC Mode, Prescaler 8

  OCR1A = 1500;                                        // Servoposition zwischen 1000(1ms)-2000(ms), 1500 Mitte

  while (!(TIFR & (1<<OCF1A)))       // Solange Bit für Compare Match Timer1 nicht gesetzt

  {

    //Warte bis Servoimpulslänge erreicht

  }

  PORTD = 0x00;                                        // PortD = 0, alle Pins ausschalten

  TCCR1B = 0x00;                                // Timer1 aus

  TIFR|=(1<<OCF1A);                                // Clear flag

}

Um nun 8 Servoimpulse in Folge, an den Pins 0-7 des PortD zu erzeugen packen wir die Befehle für die Erzeugung eines Signals in eine For-Next-Schleife:

ISR (TIMER0_OVF_vect)

{

  TCNT0 = 0x63;                                        // Timer0-Overflow alle 20ms 

  // 8 Servoimpulse erzeugen

  uint8_t i;                         // Hilfsvariable für For-Next-Schleife

  For (i=0;i<8;i++)

  {

     PORTD |= (1 << i);                     // Ausgang PD.i einschalten

     TCCR1B |= (1<<WGM12)|(1<<CS11);        // Timer1 einschalten, CTC Mode, Prescaler 8

     OCR1A = 1500;                                // Servoposition zwischen 1000(1ms)-2000(ms), 1500 Mitte

     while (!(TIFR & (1<<OCF1A)))    // Solange Bit für Compare Match Timer1 nicht gesetzt

     {

       //Warte bis Servoimpulslänge erreicht

     }

     PORTD = 0x00;                                // PortD = 0, alle Pins ausschalten

     TCCR1B = 0x00;                                // Timer1 aus

     TIFR|=(1<<OCF1A);                        // Clear flag

  }

}

Hier das komplette Codebeispiel für die Erzeugung von 8 PWM-Signalen, mit einer Grundfrequenz von 50Hz, an den Pins 0-7 des PortD eines ATMega8 (getaktet mit seinem internem RC- Oszillator bei 8MHz), mit Hilfe der beiden Timer Timer0 und Timer1. Hier im Codebeispiel wird nacheinander, ca. alle 20ms, auf allen 8 Kanälen derselbe PWM- Impuls von 1500µs = 1,5ms Länge, also für die Servomittelstellung erzeugt. Die Stellung aller 8 Servos wird hierzu durch den Wert des Vergleichsregisters OCR1A des Timer1 bestimmt. Ein Schritt entspricht dabei einer Impulslänge von 1µs. Für ein Signal von 1500µs=1,5ms für die Servomittelstellung also OCR1A =1500. Für den für Modellbauservos gängigen Impuls zwischen 1-2ms (0-90°) haben wir also eine Auflösung von 1000 Schritten.

#include <avr/io.h>

#include <avr/interrupt.h>

int main(void)

{

   OSCCAL=0xAB;                                        // Calibration-Byte 8 MHz, ausgelesen mit AVR Studio 4

   DDRD = 0xFF;                                     // PortD alle 8 Bit als Ausgänge definieren

   // Timer0 initialisieren

   TCCR0 = (1<<CS02)|(1<<CS00);                // Timer0: Prescaler 1024

   TIMSK |= (1<<TOIE0);                        // Timer0: Overflow Interrupt erlauben

   sei();                                                // Globale Interrupts ein

                

   while(1)

    {

                                                        // Programm-Endlosschleife

    }

}

ISR (TIMER0_OVF_vect)

{

  TCNT0 = 0x63;                                        // Timer0-Overflow alle 20ms 

  // 8 Servoimpulse erzeugen

  uint8_t i;                         // Hilfsvariable für For-Next-Schleife

  For (i=0;i<8;i++)

  {

     PORTD |= (1 << i);                     // Ausgang PD.i einschalten

     TCCR1B |= (1<<WGM12)|(1<<CS11);        // Timer1 einschalten, CTC Mode, Prescaler 8

     OCR1A = 1500;                                // Servoposition zwischen 1000(1ms)-2000(ms), 1500 Mitte

     while (!(TIFR & (1<<OCF1A)))    // Solange Bit für Compare Match Timer1 nicht gesetzt

     {

       //Warte bis Servoimpulslänge erreicht

     }

     PORTD = 0x00;                                // PortD = 0, alle Pins ausschalten

     TCCR1B = 0x00;                                // Timer1 aus

     TIFR|=(1<<OCF1A);                        // Clear flag

  }

}

Platzhalter: Code 05a ATMega8_Servocontroller

2. Grundlagen zum Raspberry Pi

3.1 Was ist der Raspberry Pi

http://de.wikipedia.org/wiki/Raspberry_Pi

Die Spezifikationen des verwendeten Rasberry Pi  Model B zusammengefasst:

 RaspiModelB-1024x902.png

Chip

Broadcom BCM2835 SoC full HD multimedia applications processor

CPU

700 MHz Low Power ARM1176JZ-F Applications Processor

GPU

Dual Core VideoCore IV®

Multimedia Co-Processor

Memory

512MB SDRAM

Ethernet

onboard 10/100 Ethernet RJ45 jack

USB2.0

Dual USB Connector

Video Output

HDMI (rev 1.3 & 1.4) Composite RCA (PAL and NTSC)

Audio Output

3.5mm jack, HDMI

Onboard Storage

SD, MMC, SDIO card slot

Abmaße

8.6cm x 5.4cm x 1.7cm

Offizielle Seite der Raspberry Pi Foundation: http://www.raspberrypi.org

3.2 Die Betriebssysteme für den Raspberry Pi

Inzwischen gibt es 6 offizielle von der Raspberry Pi Foundation unterstützte Betriebssysteme. Diese sind, immer in der aktuellsten Version, direkt erhältlich unter:  http://www.raspberrypi.org/downloads/

Anfänger finden hier einen guten, ersten Überblick über die offiziell unterstützten Betriebssysteme:

http://www.raspberry-pi-geek.de/Magazin/2013/05/Distributionen-fuer-den-Raspberry-Pi-im-Ueberblick

Im Netz sind natürlich inzwischen auch viele weitere freie Betriebssysteme für den Raspberry Pi auf unterschiedlichster Basis, meist auch Linux, zu finden. Als Anfänger, gerade als unerfahrener Linux User, sollte man am Anfang aber erst mal bei den offiziell unterstützten Betriebssystemen bleiben. Gerade Rasbpian, ein OS auf Basis der aktuellsten Debianversion “Wheezy”, ist für Anfänger am ehesten geeignet. Es lässt sich einfach Menügeführt konfigurieren und bootet direkt in eine graphische GUI (LXDE), Raspbian ist schon mit viel zusätzlicher Software ausgestattet. Damit kann auch ein unerfahrener Linuxuser direkt mit seinem Raspberry Pi loslegen um das Gerät kennen zu lernen.

Die bisher offiziell unterstützen Betriebssysteme für den Raspberry Pi zusammengefasst:

OS

Basis/ Derivat

Link

Raspbian

Debian Wheezy

http://raspbian.org/

Pidora

Fedora Remix

http://pidora.ca/

OPENELEC

XMBC Mediacentre

wiki.openelec.tv

RASPBMC

XMBC Mediacentre

http://www.raspbmc.com/

RISC OS

Einziges offizielles OS nicht auf Linuxbasis

riscosopen.org

Arch Linux

ARCH Linux ARM

archlinuxarm.org

Ich benutze zur Zeit RASPBIAN, eine Debian Wheezy Distribution oder ArchLinux ARM eine ArchLinux Distribution für den Raspberry Pi. Die zur Zeit der Dokumentation aktuellsten Versionen sind beides Hardfloat-Varianten vom Januar 2014 bzw. Mai 2014. Die folgenden Anleitungen beziehen sich, soweit nicht anders erwähnt, auf diese Distributionen. Solange nicht anders erwähnt reicht eine Grundinstallation des Betriebssystems mit den korrekten Ländereinstellungen und einer eingerichtete Internetverbindung.

Im Kapitel Toolchain der Dokumentation gehe ich näher auf die beiden Betriebssysteme Raspian und ArchLinux ARM ein.

3.4 SD-Karte für den Raspberry Pi vorbereiten

Die SD-Karte ist für den Raspberry das Pendant zur Festplatte eines Desktop-PC. Sie beherbergt also auch das Betriebssystems des Raspberry und muss vor Gebrauch formatiert werden. Hier zeige ich je eine mögliche Art, die SD-Karte unter Windows und unter Linux zu formatieren, und das Image mit dem Betriebssystem auf die Karte zu schreiben.

Windows

SD-Karte formatieren

Image auf SD schreiben

SDFormatter.png

Zum formatieren der SD benutze ich unter Windows7(64Bit) SDFormatter V4.0.

Ein Tool der SD Association, auf deren Internetseite können auch für andere Windows-Versionen passende Varianten und auch eine Version für den Mac heruntergeladen werden.

Pfeil.png

Win32DiskImager.png

Zum schreiben eines Images eines Betriebssystems auf die SD benutze ich unter Windows7(64Bit) dann das Tool Win32DiskImager. Ein Freeware-Programm unter der GNU-GPL.

Linux

SD-Karte formattieren

Image auf SD schreiben

Unter Linux verwende ich das Tool Gparted bzw. dessen Komandozeilenversion Parted um die SD- Karte zu formatieren (FAT).

Pfeil.png

http://developer-blog.net/administration/raspberry-pi-image-auf-sd-karte-kopieren/

3.5 Der GPIO- Port des Raspberry Pi

Der Raspberry Pi wäre für viele Bastlerprojekte nur halb so interessant, wenn er nicht neben seinen Standardschnittstellen wie beispielsweise dem HDMI- oder USB-Port noch den so genannten GPIO-Port zur Verfügung hätte. Durch diesen 26-poligen zweireihigen Pfostenstecker stehen dem Anwender mehrere digitale I/Os, I2C, SPI,  und eine serielle Schnittstelle auf dem Raspberry zur Verfügung.

RPi_GPIO.png

GPIO Raspberry Pi Modell B (Revision 2.0)

3.6 Die Serielle Schnittstelle des Raspberry Pi

In diesem Kapitel zeige ich an einem einfachen Beispiel wie man die serielle Schnittstelle des Raspberry Pi nutzen kann. Dazu werde ich zuerst, analog zum Kapitel Der UART der AVR  zeigen, wie man die serielle Schnittstelle des RPi mit der RS232-Schnittstelle eines PC hardwareseitig verbindet. Danach werde ich darauf eingehen, wie man spezifisch unter Raspian, Archlinux und Pidora Zugriff auf die RS232-Schnittstelle des Raspberry Pi erhält.

Zuerst sollte man wissen, dass der RPi intern mit einer Betriebsspannung von 3,3V betrieben wird, somit haben auch die beiden für die serielle Kommunikation nötigen Signale TXD und RXD an der GPIO- Schnittstelle des RPi diesen Pegel. Um also auf den RS232- Pegel des PC zu kommen benötigen wir wieder, analog zu den AVR, eine Schaltung als Pegelwandler. Der MAX232, den wir bei bei den AVR verwendet haben, ist dazu aber leider ungeeignet, er arbeitet nur bei 5V korrekt. Doch zum Glück gibt es eine, sogar zum MAX232 pin- kompatible Lösung in Form des ICs MAX3232. Dieser Pegelwandler arbeitet auch mit einer Betriebsspannung von 3,3V um RS232-kompatible Pegel für die serielle Kommunikation mit einem PC zu erzeugen.

Die verwendete Schaltung gleicht bis auf das hier verwendete IC MAX3232 und der unterschiedlichen Kapazität der 4 als Chargepump verwendeten Kondensatoren C1-C4 genau der, die wir bereits im Kapitel Der UART der AVR  angewendet haben:

Target_RPi_MAX3232.png
Schaltung für die Verbindung des Raspberry Pi mit der seriellen Schnittstelle des PC mittels Pegelwandler MAX3232

Mit Hilfe dieser Schaltung lässt sich die Verbindung zwischen RPi und dem PC über RS232 mit einem Nullmodemkabelherstellen.

Um den GPIO- Port des RPi bequem verdrahten zu können kann man ein gekauftes Breakout-Kit verwenden:

RPi_GPIO_Adapter.png

Um die GPIO- Schnittstelle des RPi zu nutzen kann man zum Beispiel, wie ich in diesem Fall die T-Platine eines T-Cobbler GPIO Breakout Kit verwenden. Diese wird einfach mittels eines 26poligen Flachbandkabels mit dem RPi verbunden. Die benötigten Signale TXD und RXD und die benötigte Betriebsspannung von 3,3V für die Schaltung sind so leicht zugänglich.

RS232 unter Raspbian einrichten

Hardwareseitig ist die Verbindung zwischen RPi und PC nun also hergestellt, kümmern wir uns um die Softwareseitige Verbindung. Damit man unter RASPBIAN die serielle Schnittstelle des Raspberry Pi nutzen kann müssen systemseitig noch zwei kleine Anpassungen vorgenommen werden.

RASPBIAN ist standardmäßig so konfiguriert, dass der gesamte Bootvorgang über die serielle Schnittstelle protokolliert werden kann. Um das abzustellen, muss man in der Datei ‘/boot/cmdline.txt’  folgende Änderung vorzunehmen:

In der Datei ‘/boot/cmdline.txt’ nach folgender Zeile suchen,

dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

Und den rot markierten Teil in dieser Zeile löschen. Die Zeile sieht dann so aus:

dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

Zu dem ist in den Grundeinstellungen von RASPBIAN die serielle Schnittstelle als Login-Schnittstelle definiert, also bereits von diesem Prozess belegt. Um das abzustellen muss man in der Datei ‘/etc/inittab’ folgende Änderungen vornehmen:

 In der Datei ‘/etc/inittab’ nach folgender Zeile suchen:

T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100gr

und auskommentieren:

#T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100

RS232 unter ArchLinux ARM und Pidora einrichten

Auch Archlinux ARM und Pidora für den Raspberry Pi sind standardmäßig so konfiguriert, dass der gesamte Bootvorgang über die serielle Schnittstelle protokolliert werden kann. Um das abzustellen, muss man in der Datei ‘/boot/cmdline.txt’  folgende Änderung vorzunehmen:

In der Datei ‘/boot/cmdline.txt’ nach folgendem Text suchen,

console=ttyAMA0,115200 kgdboc=ttyAMA0,115200

und löschen.

Bei der von mir verwendeten Version von ArchLinux ARM vom Mai 2014 für den Raspberry Pi war Getty nicht oder falsch konfiguriert, jedenfalls konnte nach obiger Änderung in der /boot/cmdline.txt die serielle Schnittstelle sofort benutzt werden ohne von Getty blockiert zu sein.

Allegemein: Um die serielle Schnittstelle unter Linux nutzen zu können, muss der jeweilige User auch die nötige Berechtigung für die Gruppen ‘dialout’ oder ‘tty’ haben. Beim User Pi unter RASPBIAN ist das standardmäßig der Fall. Prüfen lässt sich das mit dem Befehl ‘groups username’, der alle Gruppen des Users ‘username’ auflistet. Mit dem Befehl ‘sudo usermod -a -G tty username‘ bzw. ‘sudo usermod -a -G dialout username‘kann man dann z.B. einen User ‘username’ der Gruppe ‘tty’ bzw. ‘dialout’ hinzufügen. Alternativ kann man auch mit ‘sudo chmod a+rw /dev/ttyAMA0’ die Zugriffsbeschränkung für die serielle Schnittstelle für alle User freigeben.

Nun steht einem nichts mehr im Weg, für die Verbindung eines PC mit dem RPi mittels RS232, verwendet wird dazu wieder ein RS232- Verbindungskabel, also kein Nullmodemkabel. Ich habe das ganze getestet, indem ich sowohl auf dem verbundenen PC und dem Raspberry als Terminalprogramm Putty verwende. An beiden Rechnern wird dann mit Putty eine Verbindung mit denselben Parametern (z.B. 19200 Baud 8N1) geöffnet und man sollte problemlos Daten über die RS232 in beide Richtungen im geöffneten Terminal senden und empfangen können.

3.7 MD49 am Raspberry Pi

Versuchsaufbau zur Ansteuerung eines MD49 mit dem Raspberry Pi, frei nach einem Beispielprojekt von http://www.robot-electronics.co.uk/.

Bevor wir die serielle Ansteuerung des MD49 Softwareseitig realisieren, müssen wir zuerst den MD49 an den GPIO-Port des Raspberry Pi nach folgendem Schema anschließen:

rpi-md49.png

Auch hier ist wieder zu beachten, dass der Raspberry Pi und das MD49-Board mit unterschiedlichen RS232-Pegeln arbeiten. Beim Raspberry sind es 3,3V, während der MD49 mit 5V-TTL Pegeln arbeitet. Wir können aber auf eine aufwendige Schaltung in diesem Fall verzichten: Die Sendeleitung (TX) des Raspberry kann direkt mit der Empfangsleitung (RX) des MD49 verbunden werden. Da die 3,3V des Raspberry ausreichen um ein High-Pegel am MD49 zu erzeugen. Um aber den 3,3V- Eingang (RX) des Raspberry gegen Überspannung zu schützen, legen wir die Sendeleitung (TX) des MD49 über einen Spannungsteiler an die Empfangsleitung (RX) des Raspberry.

MD49_RPi.jpg

Danach müssen wir folgenden C-Code auf dem Raspberry Pi zum laufen bekommen:

/*
* MD49.c
*
* MD49 example code for raspberry pi.
* Runs the motors to an encoder value and back while displaying the encoder values on the screen.
*
* By James Henderson, 2012
*
*/

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>

void writeBytes(int descriptor, int count);
void readBytes(int descriptor, int count);
void displaySoftwareValue(int fd);
void setMode(int fd, char mode);
void driveMotors(int fd, char speed);
void resetEncoders(int fd);
void displayDecoderValues(int fd);

char serialBuffer[10];                                                // Serial buffer sto store data for I/O
signed int encoder1, encoder2;                                        // To store the encoder values

int main(int argc, char **argv)
{
        int fd;                                                        // File descriptor of port we will talk to
        char *portName = "/dev/ttyAMA0";                        // Name of the UART port on the Raspberry pi
        struct termios options;                                        // Port options
        
        fd = open(portName, O_RDWR | O_NOCTTY);                        // Open port for read and write not making it a controlling terminal
        if (fd == -1)
        {
                perror("openPort: Unable to open port ");        // If open() returns an error
        }
        tcgetattr(fd, &options);
        cfsetispeed(&options, B9600);                                // Set baud rate
        cfsetospeed(&options, B9600);                                        
        cfmakeraw(&options);
        tcflush(fd, TCIFLUSH);
        tcsetattr(fd, TCSANOW, &options);
        
        usleep(10000);                                                // Sleep for UART to power up and set options
        
        displaySoftwareValue(fd);                                // Display the software version of the MD49
        
        setMode(fd, 1);                                                // Set the mode of the MD49 to 1
        
        resetEncoders(fd);                                        // Reset the encode values to 0
        
        do {                                                        // Run motors forward to an encoder value
                driveMotors(fd, 100);
                displayDecoderValues(fd);
        }while(encoder1 < 0x4000);
        
        do {                                                        // Run motors backwards
                driveMotors(fd, -100);
                displayDecoderValues(fd);
        }while(encoder1 > 0);
        
        driveMotors(fd, 0);                                        // Stop motors before exit
        
        close(fd);                                                // Close port
        
        return 0;
}

void writeBytes(int descriptor, int count) {
        if ((write(descriptor, serialBuffer, count)) == -1) {        // Send data out        
                perror("Error writing");
                close(descriptor);                                // Close port if there is an error
                exit(1);
        }
}

void readBytes(int descriptor, int count) {
        if (read(descriptor, serialBuffer, count) == -1) {        // Read back data into buf[]
                perror("Error reading ");
                close(descriptor);                                // Close port if there is an error
                exit(1);
        }
}

void displaySoftwareValue(int fd) {
        serialBuffer[0] = 0;                                        // Sync byte of 0
        serialBuffer[1] = 0x29;                                        // Command to get software version
        
        writeBytes(fd, 2);
        readBytes(fd, 1);
        
        printf("MD49 Software v: %d \n",serialBuffer[0]);        // display the software version
        
}

void setMode(int fd, char mode) {
        
        serialBuffer[0] = 0;
        serialBuffer[1] = 0x34;                                        // Command to set mode
        serialBuffer[2] = mode;                                 // Mode we wish to set
        
        writeBytes(fd, 3);
        
}

void driveMotors(int fd, char speed) {
        
        serialBuffer[0] = 0;
        serialBuffer[1] = 0x31;                                        // Command to set motor speed
        serialBuffer[2] = speed;                                // Speed to be set
        
        serialBuffer[3] = 0;
        serialBuffer[4] = 0x32;
        serialBuffer[5] = speed;
        
        writeBytes(fd, 6);
}

void resetEncoders(int fd) {
        
        serialBuffer[0] = 0;
        serialBuffer[1] = 0x35;                                        // Command to reset encoder values
        
        writeBytes(fd, 2);
}

void displayDecoderValues(int fd) {
        
        serialBuffer[0] = 0;
        serialBuffer[1] = 0x25;                                        // Command to return encoder values
        
        writeBytes(fd, 2);
        readBytes(fd, 8);
        
        encoder1 = serialBuffer[0] << 24;                        // Put together first encoder value
        encoder1 |= (serialBuffer[1] << 16);
        encoder1 |= (serialBuffer[2] << 8);
        encoder1 |= (serialBuffer[3]);
        
        printf("\rencoder 1 : %08X ", encoder1);                // Display it
        
        encoder2 = serialBuffer[4] << 24;                        // Second decoder value
        encoder2 |= (serialBuffer[5] << 16);
        encoder2 |= (serialBuffer[6] << 8);
        encoder2 |= (serialBuffer[7]);
        
        printf("encoder 2 : %08X   ", encoder2);
        
        fflush(stdout);                                                // Flush output to ensure that data is displayed on the screen
}

Ich habe dazu die Datei MD49.c in das Verzeichnis /home/pi/MD49, also in einen Ordner MD49 im Homeverzeichnis des Users pi, kopiert.

Nun ins Verzeichnis mit dem Quellcode wechseln und diesen mit gcc in eine ausführbare Datei MD49 compilieren:

cd /home/pi/MD49

gcc MD49.c -o MD49

Danach können wir den compilierten Code starten indem wir die Datei MD49 mit folgendem Befehl ausführen:

./MD49

Wurde alles richtig ausgeführt, sollte der angeschlossene Motor/die angeschlossenen Motoren sich eine bestimmte Anzahl Umdrehungen Vor und anschließend wieder Zurück zu drehen.

3. Raspberry Pi: Betriebssystem Raspbian

Raspian ist ein Betriebssystem basierend auf Debian Wheezy, angepasst für den Raspberry Pi. Gerade für Linux- Neulinge ist Raspbian die einsteigerfreundlichste Linuxdistribution für den Raspberry. Die aktuellste Version kann auf http://www.raspberrypi.org herunterladen werden. Das zur Zeit der Erstellung der Dokumentation aktuellste Image war vom September 2014 (2014-09-09-wheezy-raspbian). Von Haus aus ist bei Raspian sehr viel nützliche Software vorinstalliert (SSH-Server, LXDE Desktop, Python und vieles mehr) und die Einrichtung des Betriebssystems geht mit dem Tool Raspi-Config einfach von statten.

Im folgenden werde ich ein paar nützliche Kniffe vorstellen und zeigen wie ein paar zusätzliche Tools unter dem Betriebssystem Raspian eingerichtet werden können.

Das aktuelIe Image wurde bei mir im Zuge der Einrichtung von Qt5 für den RPi mittels Crosscompile-Toolchain auf einem Hostrechner (Ubuntu 14.04.1/12.04.5 in einer VM) erstellt. Nachdem Qt5 wie hier beschrieben erfolgreich auf dem Host und dem neuen Image erstellt wurde, hat man das Basisimage der jeweils aktuellsten Raspbian Version mit einer selbst kompilierten Version von Qt5 auf einer SD-Karte.

Raspi-Config

Beim ersten Bootvorgang startet Raspi-Config. Hier zuerst das Filesystem an die Größe der verwendete SD-Karte anpassen und alle länderspeziffischen Einstellungen vornehmen.

Hostname ändern

in etc/hosts und etc/hostname in Pi-on-Robot ändern

sudo nano /etc/hosts

sudo nano /etc/hostname

Username ändern

t

o

d

o

WLAN einrichten und Edimax WLAN-Stick Powersafe-Mode deaktivieren

Unter Raspbian ist das WLAN am einfachsten mit dem grafischen Tool WPA-Suplicant eingerichtet das nach der Installation von Raspbian  als Verknüpfung auf dem Desktop liegt.

Um die Stromsparfunktion des Edimax-Treibers zu deaktivieren, andernfalls wird die Verbindung bei längerer Inaktivität unterbrochen, muss eine Konfigurationsdatei für den Treiber angelegt werden:

sudo nano /etc/modprobe.d/8192cu.conf

hier folgende Zeile einfügen, speichern und den RPi neu starten:

options 8192cu rtw_power_mgnt=0 rtw_enusbss=0

Der Dateiname, hier Fall 8192cu.conf ist abhängig vom vorhandenen Treiber der mit dem Befehl lsusb mit angezeigt wurde und kann gegebenenfalls abweichen.

Raspberry mit Composite/ AV- Ausgang betreiben

config.txt bearbeiten

# Set stdv mode to PAL (as used in Europe)

sdtv_mode=2

# Defines the aspect ratio for composite output to 4:3

# sdtv_aspect=1

VNC-Server installieren

sudo apt-get install tightvncserver

LAMP (Standard) unter Raspbian

Mit LAMP ist ein Softwarepaket unter Linux zu verstehen, bestehend aus den Komponenten Apache Webserver, MySQL und PHP. Es wird genutzt, um dynamische Webseiten und -Anwendungen zu entwickeln und zur Verfügung zu stellen:

Betriebssystem

Linux

Webserver

Apache

Datenbank

MySQL

Programmiersprache

PHP

Ich beschreibe hier, wie man das Paket auf dem Raspberry Pi unter dem Betriebssystem Raspbian installiert.

Um auch die aktuellsten Versionen zu installieren frischen wir unseren Paketmanager erst mal mit den neuesten Informationen auf:

sudo apt-get update

Jetzt können wir die nötigen Softwarepakete für den Betrieb eines Apache-Webservers mit MySQL und PHP installieren:

sudo apt-get install apache2

sudo apt-get install mysql-server

sudo apt-get install php5

sudo apt-get install php5-mysql

Bemerkung: Bei der Installation des Pakets mysql-server wird man nach dem root- Passwort für mySQL gefragt, dieses wird später auch bei D

Nach einem Neustart starten die installierten Services dann auch automatisch:

sudo reboot

Erster Test

Wir können nun testen ob und welche Ports zum jeweiligen Service zugeteilt wurden:

netstat -ntl

Dieses Kommando erzeugt bei mir folgende Ausgabe, aus der sich schließen lässt das alles korrekt installiert und gestartet wurde. Der Webserver lauscht auf den TCP-Port 80, MySQL auf 3306 und der vorher bereits vorhandene SSH-Server auf Port 22:

Aktive Internetverbindungen (Nur Server)

Proto  Recv-Q Send-Q Local Adress                 Foreign Address            State

tcp         0      0.0.0.0.0:22                   0.0.0.0.*                  LISTEN

tcp         0      0.127.0.0.1:3306               0.0.0.0.*                  LISTEN

tcp         0      0.0.0.0.0:80                   0.0.0.0.*                  LISTEN

Zugriff vom lokalen Netzwerk aus:

Befinden wir uns hinter einem Router im DHCP-Netzwerk können wir nun in einem beliebigen Browser auf einem unserer Rechner im Heimnetzwerk testen ob der Apache Webserver auch wirklich auf dem Raspberry läuft. Wir geben dazu einfach die lokale IP des Raspberry in die Adresszeile des Browsers ein. Dieser sollte dann diese vorinstallierte Seite des Apache Webservers anzeigen:

Apache_Test.png

Zugriff aus dem Internet:

Will man den Raspberry Pi nun auch von einem beliebigen Rechner im Internet, also nicht nur vom lokalen Heimnetzwerk aus, ansprechen, so muss man dazu am Router eine so genannte NAT-Regel erstellen, um eine Portweiterleitung einzurichten. In meinem Fall für den Port 80 sieht das ganze bei meinem Speedport 732V Typ B so aus:

Portweiterleitung.png

Ist diese Regel gespeichert und aktiv ist der Webserver auch über das Internet ansprech- aber so natürlich auch leichter angreifbar. Man kann den Webserver nun von jedem beliebigen Rechner der mit dem Internet verbunden ist unter seiner WAN-Adresse, der dynamischen IP-Adresse über die unser Router mit dem Internet verbunden ist, erreichen. Diese kann man auf Seiten wie www.meineip.de oder www.whatsmyip.org ermitteln.

Apache_Test_WAN.png

Wichtig: Wenn man, wie ich, einen Speedport des oben genannten Typs verwendet kann man die Funktion der Weiterleitung nicht aus dem lokalen Netzwerk heraus testen, da dieser Router kein NAT-Loopback ermöglicht und Anfragen aus dem Netzwerk an die eigene WAN-IP-Adresse verwirft. Man muss das also von außerhalb, z.B. per Mobilfunk oder einem anderen DSL-Anschluss aus testen.

PHP Server testen

Um die Funktion des PHP-Servers zu testen erstellen wir eine kleine php-Datei mit folgendem Inhalt:

<?php

   phpinfo();

?>

Diese Datei speichern wir z.b. unter dem Namen ‘phpinfo.php’ und legen sie in den Dokumentenordner [/var/www] des Apache-Webservers ab. Jetzt können wir die Datei im Browser aufrufen indem wir die Adresse zur php-Datei in die Adresszeile des Browsers eingeben. Sofern die Installation korrekt war erhält man dann die folgende Ausgabe im Browser, die durch den Befehl ‘phpinfo()’ im Script erzeugt wird:

phpinfo.png

Der Befehl ‘phpinfo()’ gibt eine große Anzahl von Informationen über den aktuellen Zustand von PHP aus. Dies umfasst Informationen wie die PHP-Version, Server-Informationen und -Umgebung, die PHP-Umgebung selbst, Informationen zum Betriebssystem, Pfade, und einiges mehr.

PHPMyAdmin installieren

MySQL- Datenbanken können zwar auch von der Kommandozeile aus verwaltet werden, komfortabler ist aber die Verwendung von PHPMyAdmin. Die Administration erfolgt dann bequem über den Browser per HTTP. Installiert wird das Tool mit:

sudo apt-get install phpmyadmin

Während der Installation werden wir zuerst nach dem verwendeten Webserver gefragt, hier Apache2 auswählen. Die darauf folgende Frage, “Konfigurieren der Datenbank mit dbconfig-common?”, beantworten wir mit Ja. Dann müssen wir das zuvor bei der Installation von MySQL angegebene Passwort für den root-User angeben und danach noch ein neues Passwort für PHPMyAdmin selbst auszuwählen. Ist die Installation abgeschlossen können wir von einem beliebigen Rechner im Netzwerk auf den Webserver zugreifen und das Tool PHPMyAdmin im Browser starten:

phpmyadmin.png

LAMP mit Nginx und SQLite

Webserver installieren:

sudo apt-get install nginx

und starten:

sudo /etc/init.d/nginx start

PHP und SQLite-Unterstützung installieren:

sudo apt-get install php5-fpm php5-sqlite

sudo nano /etc/nginx/sites-available/default

hier folgende Zeile auskommentieren:

listen 80; ## listen for ipv4; ...

und  index.php dieser Zeile hinzufügen, so dass sie so aussieht:

index index.php index.html index.htm

und folgende Zeilen auskommentieren:

location ~ \.php$ {
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}

Nginx neustarten, und PHP sollte laufen:

sudo /etc/init.d/nginx reload

PHP testen:

cd /usr/share/nginx/www

sudo nano index.php

        <?php

        phpinfo();

        ?>

SQLite3 installieren:

sudo apt-get install sqlite3

sudo apt-get install libsqlite3-dev

phpLiteAdmin installieren:

https://code.google.com/p/phpliteadmin/

pw admin

phpliteadmin.php in Verzeichnis /usr/share/nginx/www kopieren

Benutzer für Webverzeichnis ändern für Zugriff:

sudo chown www-data.www-data /usr/share/nginx/www/

phpliteadmin starten im Browser:

192.168.2.107/phpliteadmin.php

Anderer User statt www-data für nginx web-folder

siehe Aufbau

SSH

wenn bei ssh wegen hardwarewechsel WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!

ssh-keygen -R *ip_address_or_hostname*

C/C++ Crosscompiler für Raspberry Pi mit Eclipse (Linux PC)

Während man weniger aufwendigere und als geübter Programmierer vielleicht auch aufwendige Aufgabenstellungen eigentlich auch problemlos direkt auf dem Raspberry Pi selbst programmieren kann, kann diese Angelegenheit aber auch zeitraubend und nervtötend sein. Gerade wenn man nebenher viele Fenster geöffnet hat (PDF-Files, Browser, etc.), wartet man sehr oft auf die CPU des RPi. Eine Lösung kann hier der Einsatz einer so genannten Cross-Compiler-Toolchain sein. Der Quellcode wird dann einfach auf einem leistungstärkeren Rechner (Host) erstellt, aber für ein anderes Zielsystem, hier den RPi, kompatibel kompiliert.

Hier zeige ich, wie man sich eine Crosscompiler-Toolchain einrichten kann, mit der man bequem, von einem leistungsstarken Desktop PC aus, unter Linux (hier Debian Wheezy und Ubuntu), unseren Raspberry Pi in C/C++ programmieren kann. Als Entwicklungsumgebung auf dem PC wird dazu die leistungsstarke Open-source Software  Eclipse-IDE benutzt.

Ich habe diese Crosscompiler-Toolchain unter Ubuntu 12.04.5,14.04.1 und unter Debian Wheezy getestet.

Vorraussetzungen auf dem Raspberry Pi

Crosscompiler-Toolchain unter Debian/Ubuntu einrichten

Auf dem Hostrechner, also nicht dem Raspberry Pi, richten wir zuerst  die Crosscompiler-Toolchain ein:

#Install the native build tools on your PC along with the GIT tool

#which will be used to download / clone the cross compiling toolchain from GitHub.com:

sudo apt-get install build-essential git

#Create a “rpi” directory in your home directory and switch to it

cd ~/

mkdir rpi

cd rpi

#download (clone) Raspbian’s official cross compiling toolchain from Github:

sudo git clone git://github.com/raspberrypi/tools.git

Damit wir den gewünschten Compiler von nun an auch aus der Kommandozeile aufrufen können, ohne dazu immer erst in dessen Verzeichnis wechseln zu müssen, fügen wir den Pfad zum Compiler der PATH-Variablen des jeweiligen Users hinzu. Unter Debian können wir die PATH-Variable für alle User und root am einfachsten in /etc/login.defs anpassen. Unter Ubuntu editieren wir dazu einfach die .bashrc

DEBIAN WHEEZY

#Die Datei in Nano öffnen

nano /etc/login.defs

#und den beiden Variablen ENV_SUPATH und ENV_PATH den Pfad zum Compiler, bei mir /home/scheik/rpi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin

anhängen.

#Der relevante Ausschnitt aus der Datei /etc/login.defs

#

# *REQUIRED*  The default PATH settings, for superuser and normal users.

#

# (they are minimal, add the rest in the shell startup files)

ENV_SUPATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/scheik/rpi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin

ENV_PATH PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/home/scheik/rpi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin

testen:

arm-linux-gnueabihf-gcc -v

UBUNTU

#.bashrc mit Nano öffnen

cd ~/

sudo nano .bashrc

am Ende der Datei folgende Zeile einfügen

auf einem 32bit System

export PATH=$PATH:$HOME/rpi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin

auf einem 64bit System

export PATH=$PATH:$HOME/rpi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin

reload .bashrc

source .bashrc

testen:

arm-linux-gnueabihf-gcc -v

C++ Projekt in Eclipse für den RPi erstellen

Hier nun die Anleitung, um ein neues C/C++- Projekt in Eclipse für den RPi auf dem Hostrechner anzulegen:

Damit das compilierte Programm nicht immer händisch auf den RPi zu transferiert werden muss, habe ich dafür ein Skript geschrieben, dass automatisch nach dem kompilieren aufgerufen wird und die kompilierte, also ausführbare Datei per SSH vom PC auf den RPi in den Ordner /home/pi/projects kopiert. Dazu muss aber zuerst noch eine Komponente installiert werden, damit beim Transfer der Datei per Befehl scp das Passwort für die Verbindung automatisch übergeben werden kann:

sudo apt-get install sshpass

Das Skript transfer.sh habe ich nun mit folgendem Inhalt im Homeverzeichnis des Users scheik erstellt:

#!/bin/bash

PASSWD="xxxxxxxxx"

RPIIP="192.168.2.107"

USER="pi"

sshpass -p "$PASSWD" scp $1 "$USER"@"$RPIIP":$2

Im Skript die drei Variablen PASSWD, RPIIP und USER anpassen. Dann das Skript ausführbar machen mit:

sudo chmod +x transfer.sh

Nun müssen wir nur noch in Eclipse  einstellen, dass das Skript immer nach dem kompilieren aufgerufen wird, dazu unter Project->Prosperties->C/C++Build->Settings->Tab Build Steps im Feld Command: bei Post-build steps einfügen:

/home/scheik/transfer.sh $(OutDir)${ProjName} /home/pi/projects/${ProjName}

Jetzt wird nach jedem Build die erfolgreich kompilierte, ausführbare Datei direkt per SSH auf den RP in das Verzeichnis /home/pi/projects übertragen.

Eclipse aus dem Debian Repository installieren

Wir können Eclipse unter Linux am einfachsten mit folgendem Befehl direkt aus dem offiziellen Repository installieren:

#Eclipse aus dem Standard-Repository installieren:

sudo apt-get install eclipse-cdt

Da Debian & Ubuntu aber eher eine Distribution ist, die auf Stabilität setzt, als immer zwingend jedes Paket auf dem neuesten Stand zu haben, wurde bei mir zum Zeitpunkt der Erstellung der Dokumentation Eclipse Juno (3.8) installiert, während Eclipse Luna (4.4) die zur Zeit der Dokumentation aktuellste Version war. Will man mit einer neueren Version von Eclipse arbeiten kann man wie im folgenden beschrieben vorgehen.

Eclipse-CDT Luna (4.4) unter Debian installieren

Die zur Zeit der Erstellung der Dokumentation aktuellste Version von Eclipse war Luna (4.4) und lässt sich leider nicht ohne Umwege unter Debian installieren, da Eclipse Luna Java 1.7 benötigt. Nachdem Oracle aber leider seit 1.7 die Java-Lizenzierung geändert hat, ist das derzeit letzte Java-Paket für Debian die Version 1.6.0_32, welches noch unter der alten Lizenz veröffentlicht wurde.

Um Java 1.7 auf Debian (beim mir x64) zu installieren, muss dazu das Java Development Kit 7(JDK7) als Archiv von Oracle heruntergeladen und installiert werden. Bei mir war das die Archivdatei jdk_7u67-linux-x64.tar.gz von hier.

Anschliessend erstellen  wir noch die Ordner /usr/local/lib64/jvm und /usr/local/lib/jvm.

Danach können wir den Download entpacken:

tar zxvf jdk-7u67-linux-x64.tar.gz -C /usr/local/lib64/jvm

Dann zwei Symlinks anlegen:

ln -s /usr/local/lib64/jvm/jdk1.7.0_45 /usr/local/lib64/jvm/jdk1.7

ln -s /usr/local/lib64/jvm/jdk1.7 /usr/local/lib/jvm/jdk1.7

Schlieslich:

update-alternatives --install /usr/bin/java java /usr/local/lib/jvm/jdk1.7/jre/bin/java 64

update-alternatives --install /usr/bin/javac javac /usr/local/lib/jvm/jdk1.7/bin/javac 64

update-alternatives –config java  → /usr/local/lib/jvm/jdk1.7/jre/bin/java wählen

update-alternatives –config javac → /usr/local/lib/jvm/jdk1.7/bin/javac wählen

und Java 7 sollte statt Java 6 unter Debian installiert sein.

Nun das Archiv Eclipse-IDE for C/C++ Developers z.B. Eclipse Luna (4.4) von hier herunterladen und in ein beliebiges Verzeichnis entpacken. Ohne weiteres konnte ich Eclipse so aber noch nicht auf meinem Debian System (LXDE) starten, ich musste Eclipse im GTK2-Mode starten und dazu zuerst die Datei eclipse.ini bearbeiten indem ich die beiden hier rot gekennzeichneten Zeilen an der gezeigten Stelle der Datei einfügte:

-startup

plugins/org.eclipse.equinox.launcher_1.3.0.v20140415-2008.jar

--launcher.library

plugins/org.eclipse.equinox.launcher.gtk.linux.x86_64_1.1.200.v20140603-1326

-product

org.eclipse.epp.package.cpp.product

--launcher.defaultAction

openFile

-showsplash

org.eclipse.platform

--launcher.XXMaxPermSize

256m

--launcher.defaultAction

openFile

--launcher.GTK_version

2

--launcher.appendVmargs

-vmargs

-Dosgi.requiredJavaVersion=1.7

Nun konnte ich Eclipse Luna (4.4) unter Debian Wheezy starten.

Eclipse-CDT Luna (4.4) unter Ubuntu installieren

Unter Ubuntu eine neuere Version von Eclipse, als die in den Repositories enthaltene Version zu installieren geht zugegebenermaßen einfacher vonstatten als unter Debian. Dazu die z.B. 64-Bit Version von hier downloaden und in ein Verzeichnis seiner Wahl entpacken (hier ins Homeverzeichnis des angemeldeten Users):

tar -xvzf eclipse-cpp-luna-R-linux-gtk-x86_64.tar.gz -C ~/

Unter Ubuntu 12.04.5 und 14.04.1 ist standardmäßig kein Java JDK installiert, für Eclipse Luna muss mindestens 1.7 installiert sein:

sudo apt-get install openjdk-7-jdk

sudo update-alternatives --config java (nur wenn vorher schon eine andere Version von Java installiert war nötig) hier dann Java 7 wählen.

Nun ist die Luna Eclipse-CDT IDE auch schon mit Ubuntu(getestet mit 12.05.1 und 14.04.1) nutzbar

Qt5 Crosscompile-Toolchain für Raspbian unter Linux

Ich habe das folgende auch unter Ubuntu 12.04.5 oder 14.04.1 in einer VM (VirtualBox) unter Windows 7 getestet. Hier dann vor der Installation ‘Geräte’ - ‘Medium mit Gasterweiterungen einlegen’ anwählen und ausführen.

Update System

Install g++

Install the openGL library

Install git

Install ia32-libs to use the 32-bit cross compiler and other tools

Install Qt 5 Native SDK

Install Qt 5.x.x for Linux 64-bit (423 MB). This provides the QtCreator IDE which will be used to write the applications to be run on the raspberry pi  (and the option of developing native Linux apps for the host machine). (i tryed qt 5.2.0 and latest version 5.3.1 sucessfully).

Run the installer with read&write permission for writing to the /usr/local directory. Install in this path: /usr/local/Qt5.x.x

Configure QtCreator for Native Building

Add to your default path the path to qtcreator, which should be: /usr/local/Qt5.x.x/Tools/QtCreator/bin/

Now run QtCreator and select the native g++ compiler.  Go to Extras/Einstellungen/einstellungen und  Ausführung/Compilers. Add a new manual compiler, and name it “GCC Native”. Browse to /usr/bin/g++. Now you should be able to create and run a native hello world program.

Download and unzip latest Raspian imgage on Host

Mount this image

Der Offset kann falls nötig ermittelt werden mit:

$ sudo /sbin/losetup /dev/loop0 raspberry-working-image.img
$ sudo /sbin/fdisk -l /dev/loop0
...
     Device Boot      Start         End      Blocks   Id  System
/dev/loop0p1            8192      122879       57344    c  W95 FAT32
/dev/loop0p2        
122880     3788799     1832960   83  Linux
...
$ sudo /sbin/losetup -d /dev/loop0

wobei 122880 x 512 = 62914560

Download the Qt5 git source code repository

Initialize the repo

Get the cross-compiler,  tools, and install a patch

start the compilation of Qt5 for rpi & host (time to get some coffee no)

We have all the modules installed in the wheezy image, now we can copy it to the sdcard using dd:

(Check the entry point of your sdcard. Mine was /dev/sdb)

Insert SD into Pi and Boot

Setup with raspi-config and set up wlan with WiFi Config in LXDE (startx)

Setting Up Qt Creator on host

You do not need a special build of Qt Creator to build for the Raspberry Pi. Qt Creator uses Kits to select build configurations.

4. Arduino (Linux)

Arduino IDE installieren

sudo apt-get install arduino

Arduino Programmierung per Kommandozeile mit Inotool

Mit Inotool kann man Arduino-Projekte ohne die Arduino IDE (die muss aber installiert sein) unter Linux per Kommandozeile erstellen, kompilieren und übertragen.

Zuerst installieren wir pip, einen Python Package-Installer:

sudo apt-get install python-pip

Nun kann Inotool installiert werden:

sudo pip install ino

sudo apt-get install picocom

Quickstartguide für Inotool

http://inotool.org/quickstart#tweaking-parameters

Neues Beispielprojekt mit Inotool für den Arduino Mega2560 erstellen:

[build]

board-model = mega2560

[upload]

board-model = mega2560

serial-port = /dev/ttyACM0

[serial]

serial-port = /dev/ttyACM0

For the full list of board names refer to

Beispielprogramm erstellen, kompilieren und uploaden:

int led = 13;

void setup()

{

        Serial.begin(9600);

        pinMode(led, OUTPUT);

}

void loop()

{

        Serial.println("High");

        digitalWrite(led, HIGH);

        delay(1000);

        Serial.println("Low");

        digitalWrite(led, LOW);

        delay(1000);

}

LED an Pin 13 des Arduino müsste nun im Sekundentakt blinken, die serielle Ausgabe kann z.B. mit folgendem Befehl oder in jedem anderen seriellen Terminal getestet werden:

ROSserial mit Arduino

Hier zeige ich einen Weg, mit dem wir Programme auf einem Arduino erstellen können, die direkt als ROS-Node arbeiten, also z.B. direkt Messages publishen oder subscriben, oder Services starten können.

http://wiki.ros.org/rosserial_arduino 

http://wiki.ros.org/rosserial_arduino/Tutorials

Dazu müssen wir zuerst einige Komponenten installieren,

Arduino IDE Setup:

sudo apt-get install ros-indigo-rosserial-arduino
sudo apt-get install ros-indigo-rosserial

cd ~/sketchbook/libraries
rm -rf ros_lib
rosrun rosserial_arduino make_libraries.py .

Nach dem Neustart der Arduino-IDE sollte ros_lib unter File -> Examples zu finden sein.

Erster Test mit der Arduino IDE:

http://wiki.ros.org/rosserial_arduino/Tutorials/Hello%20World

Wir können nun in der Arduino IDE z.B. das Beispielprogramm HelloWorld laden, welches als einfacher Chatter fungiert und in einer Endlosschleife eine Message an ROS sendet. Wir laden es dazu einfach per Arduino IDE auf den Controller.

Dann starten wir in einem neuen Terminalfenster den Roscore:

roscore

Als nächstes starten wir die rosserial Anwendung welche die Nachrichten vom Arduino an ROS weiterleitet (Hier den den richtigen seriellen Port angeben!):

rosrun rosserial_python serial_node.py /dev/ttyACM0

Nun können wir uns die ROS-Nachrichten, die der Arduino sendet in einem neuen Terminalfenster anzeigen lassen:

rostopic echo chatter

Arduino Firmware per CMake im ROS-Workspace:

http://wiki.ros.org/rosserial_arduino/Tutorials/CMake

Bei größeren Projekten kann das arbeiten mit der Arduino IDE müßig sein, deshalb zeige ich im hier anhand einfacher Beispiele, wie man Arduino Firmware per CMake kompilieren, und somit in unseren ROS-Workspace integrieren kann. Arduinos können so auch direkt als Nodes für ROS programmiert werden. Beispielcodes dazu sind im ROS-Workspace im Package arduino_examples zu finden.

5. ROS Grundlagen

ROS (Robot Operating System) stellt Bibliotheken und Werkzeuge zur Verfügung, um damit Roboteranwendungen zu erstellen. Visualisierungen, Nachrichtenvermittlung, Paketverwaltung und andere Komponenten sind bereits vorhanden und müssen nicht neu ‘erfunden’ werden. Zudem ist ROS unter der Open Source BSD Lizenz veröffentlicht und sein Inhalt und die Anzahl der Anwender wächst stetig.

Folgendes einfache Setup für ROS stellt die grundsätzliche Funktionsweise von ROS ganz gut dar:

ROS_Master_workflow.png

Für unser Setup müssen wir also ROS sowohl auf dem RPi und einer Ubuntu Workstation installieren. Im folgenden beschreibe ich die Installation von ROS Groovy auf dem RPi und der Ubuntu 12.04.5- Workstation:

ROS Groovy auf RPi

Use the following steps:

add the repository to your apt sources:

add the apt key:

reload apt sources

install ros packages - ros_comm is a good fit, it has dependencies on roscpp, rospy, and all the core ROS tools, so that they will all be installed.

Now, you have to tell the Pi, where ROS is located and remind him every time, a terminal session is started.

Now you are good to go. Try..

ROS Groovy auf Ubuntu Workstation

1.) Setup your computer to accept software from packages.ros.org.

sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu precise main" > /etc/apt/sources.list.d/ros-latest.list'

2.) Set up your keys

wget http://packages.ros.org/ros.key -O - | sudo apt-key add -

3.) make sure your Debian package index is up-to-date:

sudo apt-get update

4.) install ROS Groovy

Desktop-Full Install: (Recommended) : ROS, rqt, rviz, robot-generic libraries, 2D/3D simulators, navigation and 2D/3D perception

sudo apt-get install ros-groovy-desktop-full

Desktop Install: ROS, rqt, rviz, and robot-generic libraries

sudo apt-get install ros-groovy-desktop

ROS-Base: (Bare Bones) ROS package, build, and communication libraries. No GUI tools.

sudo apt-get install ros-groovy-ros-base

Individual Package: You can also install a specific ROS package (replace underscores with dashes of the package name):

sudo apt-get install ros-groovy-PACKAGE

e.g.

sudo apt-get install ros-groovy-slam-gmapping

To find available packages, use:

apt-cache search ros-groovy

Before you can use ROS, you will need to initialize rosdep. rosdep enables you to easily install system dependencies for source you want to compile and is required to run some core components in ROS.

sudo rosdep init
rosdep update

It's convenient if the ROS environment variables are automatically added to your bash session every time a new shell is launched:

echo "source /opt/ros/groovy/setup.bash" >> ~/.bashrc
source ~/.bashrc

If you have more than one ROS distribution installed, ~/.bashrc must only source the setup.bash for the version you are currently using.

If you just want to change the environment of your current shell, you can type:

source /opt/ros/groovy/setup.bash

rosinstall is a frequently used command-line tool in ROS that is distributed separately. It enables you to easily download many source trees for ROS packages with one command.

To install this tool on Ubuntu, run:

sudo apt-get install python-rosinstall

to test your installation, please proceed to the ROS Tutorials.

Zusätzliche Pakete:

sudo apt-get install ros-groovy-joystick-drivers

ROS im Netzwerk

Wir wenden ein gängiges Setup an, indem wir einen Singleboard- Computer (Raspberry Pi) auf dem Roboter und einen stationären Desktop-PC für Monitoringzwecke mit ROS einsetzen. Unter ROS ist es relativ einfach im lokalen Netzwerk von beiden Rechnern aus auf dieselben Services, Topics und Parameter zuzugreifen. Dazu muss natürlich auf beiden Rechnern ROS installiert sein, aber nur auf einem der ROS Master (roscore) gestartet werden. Ich halte es für sinnvoll den ROS-Master dabei auf dem Roboter selbst laufen zu lassen, er soll ja autonom, auch ohne laufende Workstation funktionieren.  Auf beiden Rechnern sollte die selbe ROS Version laufen (bei mir derzeit Groovy)

Läuft die Ubuntu-Workstation in einer VM, muss bei dieser in den Einstellungen das Netzwerk auf ‘bridged Network’ eingestellt werden.

Damit ROS im Netzwerkbetrieb reibungslos läuft, sollten beide Rechner zeit-synchronisiert werden. Ein einfacher Weg ist einfach auf beiden Rechnern chrony zu installieren. Ein Tool, das die Uhr des Computers mit einem Server im Internet synchronisiert und somit auf beiden Rechner die Zeit abgleicht:

sudo apt-get install chrony

Um in Zukunft nicht die lokale IP des jeweiligen Rechners verwenden zu müssen nutzen wir zeroconf, auf dem RPi muss dieser Dienst vorher aber noch nachinstalliert werden unter Ubuntu12 ist dieses Paket schon installiert. Ohne dass ein DNS-Server im Netzwerk existiert oder IP-Adressen über DHCP oder manuell zugewiesen wurden, können Rechner so über ihren Rechnernamen angesprochen werden. Dazu wird ein .local an den Rechnernamen gehängt, also beispielsweise rechnername.local. Jeder Dienst des Rechners kann dann über diesen Namen angesprochen werden. Die Namensauflösung über Avahi wird nur bei den Desktop-Installationen von Ubuntu installiert. Bei einem Ubuntu Server lässt sich die Funktionalität über das Paket sudo apt-get install libnss-mdns nachinstallieren.

Mit dem Befehl hostname können dann die Namen der beiden Rechner im Netzwerk ausfindig gemacht werden, bei mir für den Pi ‘Pi-on-Robot’ und für die Workstation ‘Ubuntu12VM-Workstation’ für die VM oder für den Ubuntu Desktop-PC  ‘Ubuntu12-Desktop’.

ping Ubuntu12VM-Workstation.local und ping Pi-on-Robot.local sollte dann zeigen dass die Verbindung im Netzwerk hergestellt werden kann.

Nun ein einfaches Beispiel um zu sehen wir ROS im Netzwerk funktioniert:

Auf dem mobilen Gerät (RPi):

roscore

in einem neuen Terminalfenster:

export ROS_IP="Pi-on-Robot.local"

export ROS_HOSTNAME="Pi-on-Robot.local"

export ROS_MASTER_URI=http://raspberrypi:11311

die MASTER_URI kann man im Fenster mit gestartetem roscore finden. Nun lassen wir einen einfachen Listener laufen:

rosrun rospy_tutorials listener.py (zuvor sudo apt-get install ros-groovy-rospy-tutorials, falls nicht installiert.)

Auf der Ubuntu-Workstation (VM):

Zuerst machen wir dem System die MASTER_URI bekannt unter welcher der zuvor gestartete ROS Master läuft:

export ROS_MASTER_URI=http://raspberrypi:11311

Für den reibungslosen Ablauf muss auch auf diesem System die IP und der Hostname des eigenen Rechners dem ROS-System bekannt gemacht werden:

export ROS_IP="Ubuntu12VM-Workstation.local"

export ROS_HOSTNAME="Ubuntu12VM-Workstation.local"

Lassen wir nun einen einfachen Talker auf der Workstation laufen, sollten die von ihm versendeten Nachrichten mit dem Listener auf dem RPi empfangen werden:

rosrun rospy_tutorials talker.py

Wollen wir uns sparen in jedem neuen Terminalfenster die selben 3 Befehle einzugeben, können wir die Daten auch bei jedem Systemstart automatisch zu den globalen Umgebungsvariablen in /etc/environment hinzufügen: (Vielleicht ist ein Launchfile besser?)

Auf der Ubuntu Workstation:

sudo nano /etc/environment

dort folgende Zeilen anhängen

export ROS_IP="Ubuntu12VM-Workstation.local"

export ROS_HOSTNAME="Ubuntu12VM-Workstation.local"

export ROS_MASTER_URI=http://robotOS.local:11311

Auf dem RPi

sudo nano /etc/environment

dort folgende Zeilen anhängen

export ROS_IP="robotOS.local"

export ROS_HOSTNAME="robotOS.local"

export ROS_MASTER_URI=http://robotOS.local:11311

SSH

Standardmässig arbeitet ROS nur mit SSH Hosts die auch im known_hosts file des Systems definiert wurden. Um das zu ändern, weil man z.B. eine Node über das lokale Netzwerk per ROS Launchfile auf einem anderen Rechner starten will, kann man mit:
export ROSLAUNCH_SSH_UNKNOWN=1 dieses Verhalten abschalten. Will man das dauerhaft in jedem neuen Terminal erreichen muss dieser Befehl der ~/.bashrc hinzugefügt werden. Die saubere Lösung ist natürlich die jeweiligen Host in known-hosts zu definieren

ROS C++ Programmierung mit QtCreator

Qt wie im Kapitel Die Grundlagen beschrieben auf der Ubuntu-Workstation installieren.

QtCreator muss mit ‘bash -i -c’ gestartet werden damit das shell environment mit den Abhängigkeiten von ROS beim Start der IDE geladen wird. Gilt auch für Eclipse.

Desktop-Verknüpfung für QtCreator

[Desktop Entry]

Type=Application

Exec= sudo bash -i -c /usr/local/Qt5.3/Tools/QtCreator/bin/qtcreator %F

Icon=QtProject-qtcreator

Terminal=true

Name=Qt Creator

GenericName=Integrated Development Environment

MimeType=text/x-c++src;text/x-c++hdr;text/x-xsrc;application/x-designer;application/vnd.nokia.qt.qmakeprofile;application/vnd.nokia.xml.qt.resource;

Categories=Qt;Development;IDE;

Ein C++ Package bearbeiten mit dem QtCreator

‘File->Open Project’ und zur CMakeLists.txt des Packages navigieren (/path/to/catkin_ws/src/Package/CMakeLists.txt). QtCreator will nun ein ‘build directory’, dazu einfach das ‘build directory’ im erstellten catkin Workspace angeben (/path/to/catkin_ws/build/). Nun CMake ausführen.

ROS Catkin Workspace erstellen

ROS Package erstellen

weitere Dependencies: rosconsole, roslib, rostime, std_srvs

Erste ROS Node, “helloworld”

helloworld_node.cpp in ~/ROS-Groovy-RPi-Workspace/src/base_controller/src/helloworld erstellen:

// Include the ROS C++ APIs

#include <ros/ros.h>

// Standard C++ entry point

int main(int argc, char** argv) {

  // Announce this program to the ROS master as a "node" called "hello_world_node"

  ros::init(argc, argv, "hello_world_node");

  // Start the node resource managers (communication, time, etc)

  ros::start();

  // Broadcast a simple log message

  ROS_INFO_STREAM("Hello, world!");

  // Process ROS callbacks until receiving a SIGINT (ctrl-c)

  ros::spin();

  // Stop the node's resources

  ros::shutdown();

  // Exit tranquilly

  return 0;

}

Kompilieren des gesamten Packages

Dazu sind folgende Schritte notwendig:

  1. Declaring dependencies:

Hier sind bisher keine weiteren Einträge in CMakeLists.txt und package.xml nötig, da beim erstellen des Packages die bisher nötigen Dependencies (roscpp, etc.) schon mit angegeben wurden. Prinzipiell gilt aber:

in CMakeLists.txt

find_package(catkin REQUIRED COMPONENTS package-names)

in package.xml

<build_depend>package-name</build_depend>

<run_depend>package-name</run_depend>

  1. Declaring Executable

        in CMakeLists.txt

        Prinzipiell müssen alle neue Sourcefiles (Nodes) am Ende der Datei eingefügt werden:

        add_executable(executable-name source-files)

target_link_libraries(executable-name ${catkin_LIBRARIES})

        Hier also für helloworld.cpp in CMakeLists.txt einfügen:

        add_executable(helloworld src/helloworld/helloworld.cpp)

target_link_libraries(helloworld ${catkin_LIBRARIES})

  1. Workspace kompilieren

im Workspace- Directory, hier also ~/ROS-Groovy-RPi-Workspace        

catkin_make

  1. Sourcing setup.bash

                source devel/setup.bash

Testnode helloworld_node

Nun kann mit gestartetem roscore die Node helloworld getestet werden:

        source devel/setup.bash

rosrun base_controller helloworld

ROS Sourcecode Management mit Git

Ich habe derzeit den Inhalt meines ROS Catkin Workspace (alles im Ordner /src des Workspaceordners) als Repository auf github.com gespeichert.

Dazu die Datei .gitignore erstellt und commited:

.catkin_workspace

build/

devel/

# QtCreator files

*.*.user

# Compiled Object files

*.slo

*.lo

*.o

# Compiled Dynamic libraries

*.so

*.dylib

# Compiled Static libraries

*.lai

*.la

*.a

#temporary files

*~

# vi stuff

.*.sw?

# Python stuff

*.pyc

# Data files

*.dat

*.csv

# Emacs temp files

.#*

# OSX Files

.DS_Store

ROS Workspace von github.com klonen

Um den Code auf einen anderen Rechner zu klonen folgendermaßen vorgehen:

Test: run roscore, then in another terminal rosrun base_controller helloworld.

Joystick ROS

sudo apt-get install ros-groovy-joystick-drivers

Den angeschlossenen Joyrick ermitteln mit:

ls /dev/input/

Joyrick testen mit:

sudo jstest /dev/input/js2

in der Liste erscheint dann z.B. js0 oder j2. Nun müssen wir die Berechtigungen für das Gerät prüfen:

ls -l /dev/input/jsX

Ausgabe:

Wenn XX = rw: das Gerät ist richtig konfiguriert

Wenn XX =  -- oder r-: das Gerät ist nicht richtig konfiguriert, dann mir:

$ sudo chmod a+rw /dev/input/jsX

To get the joystick data published over ROS we need to start the joy node. First let's tell the joy node which joystick device to use- the default war js0/js2

rosparam set joy_node/dev "/dev/input/jsX"

Now we can start the joy node.

rosrun joy joy_node

Now in a new terminal you can rostopic echo the joy topic to see the data from the joystick:

$ rostopic echo joy

/joy listener example:

#include <ros/ros.h>
#include <sensor_msgs/Joy.h>

void myCallback (const sensor_msgs::Joy::ConstPtr& msg)
{
 
for (unsigned i = 0; i < msg->axes.size(); ++i) {
   ROS_INFO
("Axis %d is now at position %f", i, msg->axes[i]);
 
}
}

int main(int argc, char **argv)
{

 ros
::init(argc, argv, "listener");
 ros
::NodeHandle n;

 ros
::Subscriber sub = n.subscribe("joy", 1000, myCallback);
 ros
::spin();

 
return 0;
}

ROS Custom Messages

http://wiki.ros.org/ROS/Tutorials/DefiningCustomMessages

http://wiki.ros.org/ROS/Tutorials/CreatingMsgAndSrv#Creating_a_msg

ROS: Arduino als ROS-Node

http://wiki.ros.org/rosserial_arduino/Tutorials

Mit dem Arduino Nano sollen auf dem Roboter z.B. folgende Aufgaben erfüllt werden:

Bevor wir uns aber an eine echte Anwendung mit einem Arduino in unserem Roboter machen, will ich hier anhand einfacher Beispiele aufzeigen, wie wir Arduinos als ROS-Nodes programmieren und anwenden können. Welche Schritte also notwendig sind, ein Arduino Programm in unserem Catkin ROS-Workspace zu erstellen, zu kompilieren, es auf den Arduino zu uploaden und per rosserial_node mit ROS zu verbinden.

Dazu wird als Arduino-Firmware zu Demonstrationszwecken zuerst ein einfaches Beispiel erstellt, das einfach im Sekundentakt eine Standardmessage publishen soll. Statt der serial_node aus dem Package rosserial verwenden wir aber eine eigene Node connect_arduino_nano1.py, die wir in unseren eigenen ROS-Workspace im Package arduino_sourcecodes erstellen werden. Es handelt sich dabei eigentlich um die serial_node aus dem offiziellen Package rosserial_python, importiert in unseren Workspace. Das hat den Hintergrund, dass wenn später mehrere Arduino-Plattformen ihren dienst im Roboter tun sollen, mehrere Instanzen dieser Node verwendet werden müssen, dass ließe sich zwar auch verwirklichen, wenn diese Node in verschiedenen Namespaces gestartet werden würde (da nur so dieselbe Node mehrfach gestartet werden kann), der Übersichtlichkeit halber habe ich mich aber für den Weg entschieden für jeden Arduino eine separate serial_node zu verwenden/erstellen.

Example Publisher

Vorraussetzung für die folgenden Schritte ist, dass wie im Kapitel “Betriebssystem (robotOS) für pcDuino einrichten” beschrieben, die Arduino Toolchain installiert wurde.

Schritt 1, Package arduino_examples erstellen:

Schritt 2, Arduino Sourcecode erstellen_

#include <ros.h>
#include <std_msgs/String.h>
#include <Arduino.h>

ros::NodeHandle nh;

std_msgs::String str_msg;
ros::Publisher arduino_example_chatter("chatter", &str_msg);

char hello[13] = "hello world!";

void setup()
{
 
nh.initNode();
 
nh.advertise(arduino_example_chatter);
}

void loop()
{
 
str_msg.data = hello;
 
arduino_example_chatter.publish( &str_msg );
 
nh.spinOnce();
 
delay(1000);
}

Schritt 3, CMakeLists.txt Dateien anpassen:

cmake_minimum_required(VERSION 2.8.3)

project(arduino_examples)

## Find catkin macros and libraries

## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)

## is used, also find other catkin packages

find_package(catkin REQUIRED COMPONENTS

  rosserial_client

  std_msgs

)

## System dependencies are found with CMake's conventions

# find_package(Boost REQUIRED COMPONENTS system)

## Uncomment this if the package has a setup.py. This macro ensures

## modules and global scripts declared therein get installed

## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html

# catkin_python_setup()

################################################

## Declare ROS messages, services and actions ##

################################################

## To declare and build messages, services or actions from within this

## package, follow these steps:

## * Let MSG_DEP_SET be the set of packages whose message types you use in

##   your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).

## * In the file package.xml:

##   * add a build_depend tag for "message_generation"

##   * add a build_depend and a run_depend tag for each package in MSG_DEP_SET

##   * If MSG_DEP_SET isn't empty the following dependency has been pulled in

##     but can be declared for certainty nonetheless:

##     * add a run_depend tag for "message_runtime"

## * In this file (CMakeLists.txt):

##   * add "message_generation" and every package in MSG_DEP_SET to

##     find_package(catkin REQUIRED COMPONENTS ...)

##   * add "message_runtime" and every package in MSG_DEP_SET to

##     catkin_package(CATKIN_DEPENDS ...)

##   * uncomment the add_*_files sections below as needed

##     and list every .msg/.srv/.action file to be processed

##   * uncomment the generate_messages entry below

##   * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)

## Generate messages in the 'msg' folder

# add_message_files(

#   FILES

#   Message1.msg

#   Message2.msg

# )

## Generate services in the 'srv' folder

# add_service_files(

#   FILES

#   Service1.srv

#   Service2.srv

# )

## Generate actions in the 'action' folder

# add_action_files(

#   FILES

#   Action1.action

#   Action2.action

# )

## Generate added messages and services with any dependencies listed here

# generate_messages(

#   DEPENDENCIES

#   std_msgs

# )

################################################

## Declare ROS dynamic reconfigure parameters ##

################################################

## To declare and build dynamic reconfigure parameters within this

## package, follow these steps:

## * In the file package.xml:

##   * add a build_depend and a run_depend tag for "dynamic_reconfigure"

## * In this file (CMakeLists.txt):

##   * add "dynamic_reconfigure" to

##     find_package(catkin REQUIRED COMPONENTS ...)

##   * uncomment the "generate_dynamic_reconfigure_options" section below

##     and list every .cfg file to be processed

## Generate dynamic reconfigure parameters in the 'cfg' folder

# generate_dynamic_reconfigure_options(

#   cfg/DynReconf1.cfg

#   cfg/DynReconf2.cfg

# )

###################################

## catkin specific configuration ##

###################################

## The catkin_package macro generates cmake config files for your package

## Declare things to be passed to dependent projects

## INCLUDE_DIRS: uncomment this if you package contains header files

## LIBRARIES: libraries you create in this project that dependent projects also need

## CATKIN_DEPENDS: catkin_packages dependent projects also need

## DEPENDS: system dependencies of this project that dependent projects also need

catkin_package(

#  INCLUDE_DIRS include

#  LIBRARIES arduino_firmware

#  CATKIN_DEPENDS rosserial_client std_msgs

#  DEPENDS system_lib

)

###########

## Build ##

###########

## Specify additional locations of header files

## Your package locations should be listed before other locations

# include_directories(include)

#include_directories(

#  ${catkin_INCLUDE_DIRS}

#)

## Declare a C++ library

# add_library(arduino_firmware

#   src/${PROJECT_NAME}/arduino_firmware.cpp

# )

## Add cmake target dependencies of the library

## as an example, code may need to be generated before libraries

## either from message generation or dynamic reconfigure

# add_dependencies(arduino_firmware ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Declare a C++ executable

# add_executable(arduino_firmware_node src/arduino_firmware_node.cpp)

## Add cmake target dependencies of the executable

## same as for the library above

# add_dependencies(arduino_firmware_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Specify libraries to link a library or executable target against

# target_link_libraries(arduino_firmware_node

#   ${catkin_LIBRARIES}

# )

#############

## Install ##

#############

# all install targets should use catkin DESTINATION variables

# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html

## Mark executable scripts (Python etc.) for installation

## in contrast to setup.py, you can choose the destination

# install(PROGRAMS

#   scripts/my_python_script

#   DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}

# )

## Mark executables and/or libraries for installation

# install(TARGETS arduino_firmware arduino_firmware_node

#   ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}

#   LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}

#   RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}

# )

## Mark cpp header files for installation

# install(DIRECTORY include/${PROJECT_NAME}/

#   DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}

#   FILES_MATCHING PATTERN "*.h"

#   PATTERN ".svn" EXCLUDE

# )

## Mark other files for installation (e.g. launch and bag files, etc.)

# install(FILES

#   # myfile1

#   # myfile2

#   DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}

# )

#############

## Testing ##

#############

## Add gtest based cpp test target and link libraries

# catkin_add_gtest(${PROJECT_NAME}-test test/test_arduino_firmware.cpp)

# if(TARGET ${PROJECT_NAME}-test)

#   target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})

# endif()

## Add folders to be run by python nosetests

# catkin_add_nosetests(test)

rosserial_generate_ros_lib(

  PACKAGE rosserial_arduino

  SCRIPT make_libraries.py

)

rosserial_configure_client(

  DIRECTORY firmware

  TOOLCHAIN_FILE ${ROSSERIAL_ARDUINO_TOOLCHAIN}

)

rosserial_add_client_target(firmware arduino_example_chatter ALL)

rosserial_add_client_target(firmware arduino_example_chatter-upload)

cmake_minimum_required(VERSION 2.8.3)

include_directories(${ROS_LIB_DIR})

# Remove this if using an Arduino without native USB (eg, other than Leonardo)
add_definitions(-DUSB_CON)

generate_arduino_firmware(arduino_example_chatter
 
SRCS arduino_example_chatter.cpp ${ROS_LIB_DIR}/time.cpp
 
BOARD nano328
 
PORT /dev/ttyUSB0
)

Schritt 4, Arduino Sourcecode kompilieren:

Schritt 5, Programm auf Arduino uploaden:

Schritt 6, Verbinden mit dem Arduino mittels Node connect_arduino_nano1.py aus dem Package arduino_sourcecodes

Dazu wurde das File ~/ROS-Workspace/src/arduino_sourcecodes/src/arduino_serial_nodes/connect_arduino_nano1.py erstellt:

#!/usr/bin/env python

import rospy

from rosserial_python import SerialClient, RosSerialServer

import multiprocessing

import sys

   

if __name__=="__main__":

    rospy.init_node("serial_node_arduinoNano1")

    rospy.loginfo("ROS Serial Python Node")

    port_name = rospy.get_param('~port','/dev/ttyUSB0')

    baud = int(rospy.get_param('~baud','57600'))

    # TODO: should these really be global?

    tcp_portnum = int(rospy.get_param('/rosserial_embeddedlinux/tcp_port', '11411'))

    fork_server = rospy.get_param('/rosserial_embeddedlinux/fork_server', False)

    # TODO: do we really want command line params in addition to parameter server params?

    sys.argv = rospy.myargv(argv=sys.argv)

    if len(sys.argv) == 2 :

        port_name  = sys.argv[1]

    if len(sys.argv) == 3 :

        tcp_portnum = int(sys.argv[2])

   

    if port_name == "tcp" :

        server = RosSerialServer(tcp_portnum, fork_server)

        rospy.loginfo("Waiting for socket connections on port %d" % tcp_portnum)

        try:

            server.listen()

        except KeyboardInterrupt:

            rospy.loginfo("got keyboard interrupt")

        finally:

            rospy.loginfo("Shutting down")

            for process in multiprocessing.active_children():

                rospy.loginfo("Shutting down process %r", process)

                process.terminate()

                process.join()

            rospy.loginfo("All done")

    else :          # Use serial port

        rospy.loginfo("Connecting to %s at %d baud" % (port_name,baud) )

        client = SerialClient(port_name, baud)

        try:

            client.run()

        except KeyboardInterrupt:

            pass

Es kann dann folgendermaßen gestartet werden:

Schritt 7, Test:

Example Subscriber

Als nächstes wollen wir ein weiteres Beispiel erstellen und unserem bestehenden Package arduino_firmware hinzufügen. Diesmal soll ein einfacher Subscriber erstellt werden.

Ein neues Package muss diesmal nicht erstellt werden, wir fügen das neue Beispiel dem bestehenden Package arduino_sourcecodes hinzu. Dazu erstellen wir zuerst ein neues Sourcefile  im Ordner /firmware des Packages. /home/user/ROS-Workspace/src/arduino_sourcecodes/firmware/arduino_example_subscriber.cpp:

/*

 * rosserial Subscriber Example

 * Blinks an LED on callback

 */

#include <ros.h>

#include <std_msgs/Empty.h>

ros::NodeHandle nh;

void messageCb( const std_msgs::Empty& toggle_msg){

  digitalWrite(13, HIGH-digitalRead(13));   // blink the led

}

ros::Subscriber<std_msgs::Empty> sub("toggle_led", &messageCb );

void setup()

{

  pinMode(13, OUTPUT);

  nh.initNode();

  nh.subscribe(sub);

}

void loop()

{

  nh.spinOnce();

  delay(1);

}

Nun muss man die CMakeLists.txt Dateien anpassen, um die neue Quelle für cmake einzubinden:

rosserial_add_client_target(firmware arduino_example_subscriber ALL)

rosserial_add_client_target(firmware arduino_example_subscriber-upload)

generate_arduino_firmware(arduino_example_subscriber
 SRCS arduino_example_subscriber.cpp ${ROS_LIB_DIR}/time.cpp
 BOARD nano328
 PORT /dev/ttyUSB0

)

        

Jetzt kann wie beim ersten Beispiel schon geschehen der Arduino Sourcecode kompiliert werden:

Das Programm dann wie gehabt auf den Arduino uploaden:

Es kann dann mit derselben serial_node wie im Beispiel mit dem Publisher gestartet werden:

Test:

Die LED am Arduino sollte nun im Sekundentakt blinken

ROS: Web-Konnektivität mit ROSBridge

http://wiki.ros.org/rosbridge_suite

http://cs.brown.edu/research/pubs/theses/masters/2012/lee.pdf

http://wiki.ros.org/rosbridge_suite/Tutorials/RunningRosbridge

http://wiki.ros.org/roslibjs

http://wiki.ros.org/roslibjs/Tutorials/BasicRosFunctionality

http://iguanatronics.com/simple-tutorial-on-rosbridge-and-roslibjs/

http://www.ist.tugraz.at/ais-wiki/roswebuserinterface

http://wiki.ros.org/ros2djs/

http://wiki.ros.org/ros3djs/

http://wiki.ros.org/webui

http://students.asl.ethz.ch/upl_pdf/289-report.pdf

Die wichtigsten Komponenten für ein Webinterface für den Roboter sind rosbridge_suite und roslibjs.

rosbridge_suite stellt die JSON API für ROS-Funktionalität für nicht ROS Anwendungen bereit, ausgestattet mit einer Vielzahl an Front Ends, welche mit rosbridge interagieren. Unter anderem einem Websocket für Webbrowser.

roslibjs ist eine JavaScript Bibliothek zur Interaktion von ROS mit dem Webbrowser. Sie benutzt WebSockets für die Verbindung mit rosbridge und bietet die Funktionalität für publishing, subscribing, für service calls, URDF parsing und andere essentielle ROS Funktionen.

Falls noch nicht geschehen, installieren wir rosbridge-suite:

Nun kann rosbridge gestartet werden mit:

Dieser Befehl startet rosbridge und generiert ein WebSocket mit dem dafür vorgesehenen Standardport 9090.

Der Port kann auch anders konfiguriert werden indem der ~/port Parameter für ROS per launchfile geändert wird. Dazu habe ich ein launch-File ~/ROS-Workspace/launch/rosbridge_websocket.launch mit folgendem Inhalt erstellt:

<launch>
 <include file="$(find rosbridge_server)/launch/rosbridge_websocket.launch" >
    <arg name="port" value="9090"/>
 </include>
</launch>

Dieses kann man dann folgendermaßen starten, rosbridge wird dann mit Port 9090 gestartet, man kann den Port aber auch anpassen:

Um das ganze zu nutzen schauen wir uns einmal das Beispiel html aus dem Tutorial des ROS-Wikis an:

http://wiki.ros.org/roslibjs/Tutorials/BasicRosFunctionality.

In dieser Datei wird gezeigt, wie man aus einem html-Dokument ROS-Topics subscriben, publishen und Service-Calls aufrufen kann.

Das genannte Beispiel wurde zu Dokumentationszwecken im root-Verzeichnis unseres Webservers auf der Workstation (workstationOS) erstellt: https://github.com/Scheik/ROS-Workspace/data/rosbridge/rosbridge_tutorial.html.

Die für die Funktion nötigen JavaScript- Files (eventemitter2.min.js und roslib.min.js)werden hier lokal aufgerufen, nachdem sie mit wget von http://cdn.robotwebtools.org/EventEmitter2/current/eventemitter2.min.js und 

http://cdn.robotwebtools.org/roslibjs/current/roslib.min.js 

in den Ordner ~/ROS-Workspace/data/rosbridge/js heruntergeladen wurden.

Test des Beispiels:

Zuerst muss am Router noch eine Portweiterleitung für den verwendeten Port des Websockets (z.B. 9090) eingerichtet werden.

Portweiterleitung_rosbridge#.jpg

Nun starten wir den Webbrowser (Chrome) und können das Beispiel mit folgender Adresse aufrufen:

http://robotik.ddns.net/rosbridge/rosbridge_tutorial.html

Das ganze setzt natürlich eine vollständige Installation des workstationOS inklusive ROS, NGinx und unseres ROS-Workspace vorraus. In Chrome starten wir die Entwicklertools (Einstellungen -> Weitere Tools -> Entwicklertools) und wechseln dort in die Konsole um die Ausgaben zu sehen.

2015-09-27-125332_1366x768_scrot.png

Ob durch den Aufruf des html-Files auch das an das Topic /cmd_vel gepublished wird kann in der Konsole mit rostopic echo /cmd_vel getestet werden, und ob Messages vom Topic /listener auch subsrcibed werden sehen wir z.B. mit rostopic pub std_msgs/String hello.

Web-UI Design mit jQuery

Live Charts mit http://www.flotcharts.org/

GPS ROS

https://www.youtube.com/watch?v=k5DGWy55SA4

6. Git Grundlagen

Clone Repository, commit and push changes:

git clone https://github.com/Scheik/ROS-Workspace.git ~/ROS-Workspace

git status

git add .

git commit -m “Message”

git push -u origin master

show branches:

git branch -l

show tags/releases

git tag -l

New branch:

git branch newbranch
git checkout newbranch

oder kürzer:

git checkout -b newbranch

merge branch

git checkout branch_to _merge_into

git merge branch_to_merge_in

git push

delete branch local

git branch -d branch_to_delete

delete remote branch

git push origin :the_remote_branch

7. Web Development

Django Webframework

Django ist ein Python  Webframework welches weit verbreitet und gut dokumentiert ist.

Welche Version von Django ist installiert:

Django installieren:

Neues Django Projekt

Dieser Befehl legt das neue Djangoprojekt, hier namens djangoproject an. Dabei wird der Ordner djangoproject mit folgender Dateistruktur angelegt:

  • djangoproject
  • djangoproject
  • __init__.py
  • settings.py
  • urls.py
  • wsgi.py
  • manage.py

Testen wir nun mal, ob das Projekt richtig angelegt wurde. Dazu starten wir mittels manage.py den Development- Server:

Auf dem Rechner lokal sollte nun unter localhost:8080 folgende Seite erscheinen. Im lokalen Netzwerk müsste die selbe Seite unter ip_im _Netzwerk:8080, z.B. 192.168.178.201:8080 laden:

Django_First_Project.jpg

Standardmässig benutzt Django SQLite3 als Datenbankanwendung. Möchte man das ändern, muss man das in der settings.py ändern.

Möglich ist z.B. auch MySQL, Oracle, oder PostgreSQL. Für unser Beispiel belassen wir es bei SQLite, mehr Informationen dazu findet man in der Dokumentation zu settings.py.

Damit nun die in settings.py definierte Datenbank erstmals erzeugt, oder später Änderungen an der Datenbank automatisch vollzogen werden kann folgender Befehl verwendet werden:

Um später das Projekt administrieren zu können legen wir einen so genannten Superuser fest und vergeben eine Email- Adresse und ein Passwort für diesen:

Erstellen wir nun unsere erste App in unserem Djangoprojekt:

Wie man sehen kann wurde somit ein neuer Ordner namens Blogpage mit folgendem Inhalt in unserem Djangoprojekt angelegt:

  • migrations
  • __init__.py
  • __init__.py
  • admin.py
  • apps.py
  • models.py
  • tests.py
  • views.py

Unsere App Blogpage basiert auf einer Datenbank, in welcher die Inhalte (posts) gespeichert werden. Deswegen definieren wir diese in
/Blogpage/models.py:

from __future__ import unicode_literals

from django.db import models

from tinymce import models as tinymce_models

class posts(models.Model):

        author = models.CharField(max_length = 40)

        title = models.CharField(max_length = 200)

        bodytext = tinymce_models.HTMLField()

        timestamp = models.DateTimeField()

        def __unicode__(self): #Python 3 is __str__

                return self.title

In der Klasse posts ist nun die Struktur der Datenbankfelder definiert, für den bodytext verwenden wir den Type HTMLField, dieser ist kein Standardtype von Django, deswegen muss das Package django-tinymce noch installiert werden:

Nun fügen wir in settings.py noch unser App Blogpage und tinymce zu den INSTALLED_APPS hinzu:

...u

INSTALLED_APPS = (

    'django.contrib.admin',

    'django.contrib.auth',

    'django.contrib.contenttypes',

    'django.contrib.sessions',

    'django.contrib.messages',

    'django.contrib.staticfiles',

    'tinymce',

    'Blogpage',

)

...

und fügen die tinymce-Konfiguration hinzu

...

TINYMCE_DEFAULT_CONFIG = {

    'theme': "advanced",

}

Da wir nun Änderungen an unserer Datenbank vornehmen führen wir folgenden Befehle aus:

Um Änderungen an der Datenbank vorzunehmen bietet das Django ein Admintool, starten wir dazu zuerst wieder den Entwicklungserver:

Unter http://192.168.178.201:8080/admin/ sollte man nun mit dem Superuseraccount einloggen können. Die Blogposts können aber noch nicht angelegt, bzw. bearbeitet werden. dazu nehmen wir folgende Änderungen an admin.py vor:

from django.contrib import admin

from .models import posts

class postsAdmin(admin.ModelAdmin):

        list_display = ["__unicode__", 'timestamp']

        fields = ['timestamp', 'author', "title", 'bodytext']

        class Meta:

                model = posts

admin.site.register(posts, postsAdmin)

Unter 192.168.178.201:8080/admin/ sollte nun folgende Seite laden:

Django_Admint.jpg

Jetzt können wir bequem neue Blogposts anlegen und bereits erstellte Posts editieren.

Um unsere Blogposts später in einer HTML- Datei anzuzeigen müssen wir aber zuerst in views.py definieren, auf welcher Seite die Posts, also das zuvor definierte Model verwendet werden:

from django.shortcuts import render

from .models import posts

def blogpage(request):

        entries = posts.objects.all()[:100]

        return render(request, "blogpage.html",{ 'posts' : entries })

In einer neu anzulegenden  Datei  urls.py im Verzeichnis /Blogpage unserer App mappen wir den View nun zu einer URL:

from django.conf.urls import url

from . import views

urlpatterns = [

    url(r'^$', views.blogpage, name='blogpage'),

]

Diese Datei binden wir nun in die Datei urls.py unseres Djangoprojekts ein:

from django.conf.urls import include, url

from django.contrib import admin

urlpatterns = [

    url(r'^Blogpage/', include('Blogpage.urls')),

    url(r'^admin/', admin.site.urls),

]

Jetzt legen wir noch die zugehörige HTML-Seite blogpage.html im Ordner /templates unseres Djangoprojekts an:

<!DOCTYPE html>

<html>

<head>

        <title> News </title>

        <meta charset="utf-8">

</head>

<!DOCTYPE html>

<html>

<head>

        <title> Blog </title>

        <meta charset="utf-8">

        <style>

                body {font-family: Verdana, sans-serif; font-size:0.8em;}

                header, nav, section, article, footer

                {border:1px solid grey; padding:8px;}

                nav ul {margin:0; padding:0;}

                nav ul li {display:inline; margin:5px;}

        </style>

</head>

<body style="background-color:#000000">

        <div class="container" style="width:60%; margin:0 auto;" >

                <header style="background-color:#adc2eb">

                        <h1 style="text-align:left">Blogpage</h1>

                </header>

                <nav style="background-color:#e5f2ff">

                        <ul>

                                <li><a href="/admin">Admin</a></li>

                        </ul>

                </nav>

                <section  style="background-color:#adc2eb">

                {% for post in posts %}

                        <article  style="background-color:#e5f2ff">

                                <h2>{{ post.title }}</h2>

                                <h3>Posted on {{ post.timestamp }} by {{ post.author }}</h3>

                                <p>{{ post.bodytext|safe }}</p>

                        </article>

                {% endfor %}

                </section>

                <footer style="background-color:#e5f2ff">

                <p>&copy; Fabian Prinzing. Visit my github @ <a href="https://github.com/scheik" target="_blank">https://github.com/scheik</a></p>

                </footer>

        </div>

</body>

</html>

Damit Django die Seite im Ordner /templates auch findet muss in settings.py noch folgende Änderung(rot) im Abschnitt TEMPLATES vorgenommen werden:

...

TEMPLATES = [

    {

        'BACKEND': 'django.template.backends.django.DjangoTemplates',

        'DIRS': [os.path.join(BASE_DIR, 'templates')],

        'APP_DIRS': True,

        'OPTIONS': {

            'context_processors': [

                'django.template.context_processors.debug',

                'django.template.context_processors.request',

                'django.contrib.auth.context_processors.auth',

                'django.contrib.messages.context_processors.messages',

            ],

        },

    },

]

...

Nachdem wir wieder den Development- Server gestartet haben, müsste nun unter http://localhost:8080/Blogpage unsere Seite laden:

Django_Blogpage.jpg

Die Seite mit Nginx und uwsgi hosten

Wollen wir die Seite über einen Webserver hosten müssen wir dem Projekt zuerst die Static-Files hinzufügen,

und in settings.py den Pfad zu den Static-Files(rot) angeben:

...

STATIC_URL = '/static/'

STATIC_ROOT = os.path.join(BASE_DIR, "static/")

...

.

.to

.do

.