Overo: First Steps

1. Einarbeiten ins Embedded System

Beim Projekt Daedalus, in dem ich meine Ingenieurpraxis absolvierte, geht es darum, einen ferngesteuerten Zeppelin mit Sensoren und Bordcomputer auszustatten, sodass dieser später seine Position in der Luft möglichst genau halten kann, um präzise Messungen (Windgeschwindigkeit, Luftdruck, etc.) durchführen zu können. Als Ziel meiner Arbeit setzte ich mir daher, die Messwerte des zum Halten der Position erforderlichen Gyrometers (Lagesensor) auszulesen.

Die größte Herausforderung während des gesamten Praktikums stellte die mangelhafte Dokumentation der Komponenten dar. Ein Handbuch oder vergleichbare Informationen zum verwendeten Embedded System vom Typ Gumstix Overo waren nicht vorhanden – die einzig verfügbare Informationsquelle war das Internet und dort im Speziellen das Gumstix-User-Wiki (http://wiki.gumstix.org).

Da sowohl für mich als auch für Hubert Zimmermann, der zeitgleich seine Ingenieurpraxis absolvierte, ein Zugriff auf den Overo und ein funktionierender Compiler notwendig waren, arbeiteten wir zunächst im Team.

Unser erster Schritt, um mit dem Overo-Chip kommunizieren zu können, bestand daher darin, ihn per Terminal anzusprechen (http://gumstix.org/getting-started.html). Dazu setzten wir ihn auf die Gallop43-Erweiterungsplatine, die unter anderem über einen USB zu Seriell-Konverter eine Terminal-Verbindung zum Overo bereitstellte.

Nach der Installation des Terminal-Programmes Kermit gelang der Zugriff auf die Kommandozeile. Auch das Kompilieren von Python-Programmen war möglich. Für die geplanten Aufgaben jedoch sahen wir Python als eher ungeeignet an, stattdessen wollten wir C verwenden. Hierfür jedoch war der Compiler gcc notwendig, der im Auslieferungszustand noch nicht mitgeliefert war. Zum Nachinstallieren war eine Internetverbindung für den Overo erforderlich, die jedoch nicht vorhanden war.

Dieses Problem ließ sich dadurch lösen, dass wir den Overo auf die Erweiterungsplatine Tobi setzten, die über einen Ethernet-Anschluss, eine HDMI-Buchse und einen USB-Anschluss verfügte. Somit ließen sich ein Standard-Computerbildschirm, eine Maus, eine Tastatur und Netzwerk anschließen und der Overo über die grafische Benutzeroberfläche bedienen. Das Nachinstallieren des gcc-Compilers scheiterte allerdings am fehlenden Speicherplatz, denn bisher lief die vorinstallierte Linux-Distribution vom Typ Ångström auf dem Overo im fest eingebauten Flash-Speicher, der für solche Extras nicht genügend Platz bot.

Der Ausweg war die Benutzung des SD-Kartenlesers auf dem Overo: Wir formatierten (http://www.sakoman.com/OMAP/preparing-a-bootable-sd-card.html) eine 8GB SD-Karte mit einer kleinen, bootfähigen FAT-Partition und einer großen ext3-Partition und spielten das Overo-Image aus dem Internet (http://gumstix.org/download-prebuilt-images.html) auf. Anschließend starteten wir den Overo von dieser Karte – zumindest dachten wir das: Später sollte sich das noch als Trugschluss herausstellen. Der C-Compiler war nun verfügbar, und nach einer kurzen Umbenennung einer Library-Datei kompilierte er problemlos C-Programme, wie ein kurzer Test mit einem „Hello World“-Programm zeigte.



2. Auslesen des Accelerometers

Wider Erwarten konnte ich nicht mit dem Auslesen des Gyrometers auf der Robovero-Erweiterungsplatine fortfahren, da das dafür bestellte Netzteil (die Robovero-Platine verfügt über eine andere Stromversorgungsbuchse als die Gallop43-Platine) noch nicht angekommen war. Jedoch ließ sich in Erfahrung bringen, dass das dort verwendete Gyrometer über den I²C-Bus an den Hauptprozessor angebunden war, ebenso wie das Accelerometer auf der Gallop43-Platine und der von Hubert Zimmermann auszulesende GPS-Empfänger.

Deshalb entschieden wir uns, erst einmal zum Gewinnen von Erfahrung das Accelerometer per Kommandozeile auszulesen. Zunächst funktionierte das Skript nicht, doch nach Hinzufügen einer Initialisierung des Accelerometers konnten wir die Messwerte in milli-g anzeigen lassen. Nach Entfernen des konstant vorhandenen Offset von den Signalen simulierten wir durch Hin- und Herbewegen der Platine eine Bewegung. Diese ließ sich auch auf den gelieferten Messwerten erkennen. Einziges Problem war der Sensor in der y-Achse, der offensichtlich defekt war: Er lieferte abwechselnd einmal einen Wert von 0g und dann wieder 4,6g, unabhängig davon, ob die Platine unbewegt auf dem Tisch lag oder bewegt wurde.

Als nächstes sollte nun der Beschleunigungssensor mit einem C-Programm eingelesen werden (http://wiki.gumstix.org/index.php?title=Category:How_to_-_i2c). Dies scheiterte jedoch daran, dass der gcc-Compiler die Header-Datei „sys/i2c-dev.h“ nicht finden konnte. Auch das manuelle Einkopieren dieser Datei von extern löste das Problem nicht – jetzt gab es unzählige andere Fehlermeldungen beim Kompilieren.



3. Probleme mit dem Overo

Wir hofften, dass ein generelles Update („opkg upgrade“), um das System auf den neuesten Stand zu bringen, das Problem lösen würde. Am Ende war allerdings das Gegenteil der Fall: Der Overo startete überhaupt nicht mehr richtig. Beim Starten per Kommandozeile hängte er sich auf, beim Starten auf dem Bildschirm wurde immer wieder die grafische Oberfläche kurz gestartet, nur um gleich darauf wieder beendet zu werden und neu zu starten. Eine erneutes Formatieren und Bespielen der SD-Karte brachte keine Besserung.

Offensichtlich war es anfangs doch nicht gelungen, das System von der SD-Karte zu starten – tatsächlich war der Start immer noch vom eingebauten Speicher erfolgt, wo das vorinstallierte Betriebssystem wohl durch den Updatevorgang Schaden genommen hatte.

Zusammen mit Hubert Zimmermann versuchte ich nun, auf einer SD-Karte Ubuntu zu installieren und den Overo damit wieder zu starten. Dies scheiterte jedoch, da der Overo trotz eingesteckter SD-Karte weiterhin die vorinstallierte Ångström-Distribution ausführte.

Nach einigen Fehlversuchen gelang es schließlich, die SD-Karte korrekt zu formatieren, indem von der verwendeten Anleitung abgewichen wurde: Die bootfähige FAT-Partition ließen wir nicht ganz am Anfang der Karte, sondern erst ab Block 128 beginnen.

Der Overo bootete nun von der SD-Karte, aber die Ergebnisse waren sowohl mit Ubuntu als auch mit der originalen Ångström-Distribution auf der Speicherkarte nicht zufriedenstellend, denn keines von beiden startete korrekt, sondern blieb während des Bootvorganges hängen.

Darum versuchten wir stattdessen, die originale Ångström-Distribution auf dem internen Speicher wieder instand zu setzen. Hierfür formatierten wir die Speicherkarte mit einer großen FAT-Partition, platzierten dort Bootloader und ein Ångström-Abbild und luden dieses zurück in den Flash-Speicher. Die Aktion glückte und der Overo funktionierte wieder.



4. Auslesen des Gyrometers

Da mittlerweile nun das für die Robovero-Erweiterungsplatine benötigte Netzteil angekommen war, machte ich mich als nächstes an das Auslesen des Gyrometers auf der Robovero-Platine.

Ich stellte fest, dass die Robovero-Platine über einen eigenen, fest eingebauten Mikrocontroller verfügte und somit auch ohne aufgesteckten Overo lauffähig war. Ich verband mich über das Terminalprogramm auf den Robovero und ließ mir die Liste der Befehle ausgeben. Es waren auch einige Befehle mit Bezug zum I²C-Bus enthalten, doch da mir die entsprechende Referenz fehlte, entschied ich mich zunächst, die Sensoren über den aufgesteckten Overo auszulesen.

Dazu versuchte ich – ähnlich wie beim erfolgreichen Auslesen des Accelerometers – das Gyrometer über ein Kommandozeilen-Skript anzusprechen. Obwohl ich die korrekten Adressen von Sensor und Registern gemäß Datenblatt (http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/DATASHEET/CD00265057.pdf) eintrug, scheiterte dies. Ein Blick in den Schaltplan (http://pubs.gumstix.com/boards/ROBOVERO/PCB30019.pdf) zeigte auch, warum: Anders als auf der Gallop43-Platine war der I²C-Bus, an dem der Sensor hing, nicht bis zum Overo durchverbunden, sondern endete beim internen Mikrocontroller der Robovero-Platine. Somit war ein direktes Auslesen des Sensors vom Overo aus unmöglich.

Deshalb versuchte ich wieder, die Robovero-Platine direkt vom Computer aus anzusprechen. Im dem Gumstix-Wiki fand sich ein Beispiel-Programm in Python, das das Gyrometer, das Accelerometer und das Magnetometer auslas. Dieses funktionierte allerdings nicht – es brach ab mit der Fehlermeldung „I2C Write Error“. Ich nahm mir den Quelltext vor und entfernte alle Befehle, die das Accelerometer oder das Magnetometer betrafen. Danach funktionierte es und ich konnte die Messwerte des Gyromteters auf dem Bildschirm sehen.

Leider fiel kurz darauf die Robovero-Platine aus. Nachforschungen mithilfe des Spannungsmessgerätes ergaben, dass wohl der Spannungsregler, der aus 5V 3,3V erzeugt, defekt sein musste, denn die 5V waren auf der Platine zu finden, die 3,3V jedoch nicht. Ich versuchte, als Ersatz die 3,3V mit einem externen Netzteil in die Platine einzuspeisen, doch dies funktionierte ebenfalls nicht. Aus Sicherheitsgründen hatte ich die Strombegrenzung des verwendeten Netzteils auf 0,4A gestellt und beim Anschließen der Robovero-Platine sprach diese an. Um Schäden zu vermeiden, wollte ich die Strombegrenzung nicht auf einen höheren Wert einstellen. Offensichtlich war der verbaute Spannungsregler nicht für eine Speisung von außen geeignet.



5. Temperaturmessung

Mein Betreuer und ich entschieden, die Robovero-Platine zur Reparatur zurückzuschicken. Weil dies erfahrungsgemäß mehrere Wochen dauert, war mit einer Ankunft der neuen Platine nicht vor Ende meiner Ingenieurpraxis zu rechnen. Da nun die Grundlage für meine bisherige Arbeit weggefallen war, entschieden wir, dass ich mich stattdessen der Temperaturmessung widmen sollte.

Um den Rechenaufwand gering zu halten, sollte der verwendete Temperatursensor eine möglichst lineare Kennlinie besitzen. Solch ein Sensor war schwer zu finden – allerdings stellte ich fest, dass ein PTC-Widerstand sich sehr gut mit einem Vorwiderstand linearisieren ließ: Für einen PTC vom Typ KTY81-210 und einen Vorwiderstand von 1,35K? betrug beim Ausgehen von einem Arbeitspunkt bei 20°C der Fehler zwischen -10°C und +30°C weniger als 1°C und zwischen -20°C und +50°C immer noch weniger als 3°C, wie ich mithilfe des Datenblattes (http://www.datasheetcatalog.org/datasheet/philips/KTY81-251.pdf) des PTC-Widerstandes und einer Tabellenkalkulation errechnete.

Eine solche Messbrücke bot obendrein den Vorteil, dass der Messfehler von Schwankungen in der Versorgungsspannung unabhängig war, da sowohl die Messbrücke als auch die Referenzspannung des Analog-Digital-Wandlers aus derselben Quelle versorgt wurden. Außerdem war der Strom durch den PTC mit weniger als 1,3mA oberhalb von -30°C sehr gering, sodass eine Verfälschung der Messung durch Eigenerwärmung des Sensors vernachlässigbar sein sollte.

Als Nächstes widmete ich mich dem Auslesen des Analog-Digital-Wandlers, der direkt auf der Overo-Platine saß und durch die Erweiterungsplatinen nur galvanisch zu den Stiftleisten durchverbunden war. Hierfür waren im Gumstix-Wiki zwei Varianten aufgeführt: Eine Einfachere (http://wiki.gumstix.org/index.php?title=ADC_overo_2.6.39%2B), verwendbar ab Linux-Kernel 2.6.39 und höher sowie eine Kompliziertere (http://wiki.gumstix.org/index.php?title=ADC_overo_2.6.38-), verwendbar für Kernel- Version 2.6.38 oder niedriger.

Dank der mittlerweile erneuerten Ångström-Installation im eingebauten Flash-Speicher (Kernel- Version 2.6.39) wollte ich eigentlich die einfachere Anleitung verwenden, doch der AD-Wandler war dort im System nicht zu finden. Somit blieb nur noch das zwischenzeitlich durch Hubert Zimmermann auf der SD-Karte installierte Ubuntu mit Kernel 2.6.34.
Der Beispiel-Code zum Auslesen des ADC produzierte beim Kompilieren zunächst nur Fehlermeldungen. Durch nachträgliches Einfügen einer Variablendeklaration ließen sich diese jedoch beheben und ich konnte auf dem Terminal die ausgelesenen Werte des AD-Wandlers anzeigen lassen.

Zum Errechnen der Temperatur multiplizierte ich die gemessene Spannung mit dem errechneten Temperaturkoeffizienten der Messbrücke bei 25°C (151K/V) und ließ diese – in °C umgerechnet – ausgeben. Allerdings war das Messergebnis Schwankungen unterworfen. Ich führte dies auf Rauschen am Sensoreingang zurück, da ich den ADC mit dem PTC-Widerstand nahe seiner Auflösungsgrenze (10 Bit) betrieb. Ich löste das Problem durch Bildung eines Mittelwerts über 100 Einzelmessungen.

Nun war der Temperatursensor zwar linearisiert, aber ein eventuell vorhandener konstanter Offset noch nicht entfernt. Zur Kalibrierung wählte ich eine Schale mit Eiswasser, das eine Temperatur von genau 0°C haben sollte. Beim Messen der Wassertemperatur ergab sich dann auch ein Messwert von 0°C, sodass eine Korrektur nicht notwendig war.



6. Einbindung in Qt, Gyrometer

Nun waren die Temperatur-Messwerte zwar auf dem Robovero selber vorhanden, doch für die geplante Verwendung im Zeppelin mussten sie weitergegeben und zur Bodenstation geschickt werden. Hierfür hatte das WLAN-Team im Projekt Daedalus bereits in Qt eine Software entwickelt, die alle Sensoren der Reihe nach abfragte und die Messwerte zum Funkmodul weitergab.

Da Qt auf C++ basiert, sollte die Einbindung meines Codes zur Temperaturmessung in C aufgrund der Abwärtskompatibilität kein Problem darstellen. Zusammen mit der WLAN-Gruppe fügte ich auf dem Overo meinen Code in das bestehende Qt-Projekt ein, doch bei der Kompilierung traten Unmengen von Fehlermeldungen auf, die sich trotz verschiedenster Versuche zur Abhilfe nicht in den Griff bekommen ließen.

Kurz vor Ende der Ingenieurpraxis traf die reparierte Robovero-Platine wieder ein, sodass ich mich wieder mit meiner ursprünglichen Zielsetzung beschäftigen konnte. Nachdem ich das Gyromter bereits mit dem Computer unter Python ausgelesen hatte, wollte ich dasselbe nun auch auf dem Overo schaffen. Unvorhersehbare Schwierigkeiten bereitete mir hier allerdings, dass der Overo die per USB angeschlossene Robovero-Platine nicht erkennen wollte: Normalerweise erschien der Robovero-Controller unter „/dev/ttyACM0“, hier jedoch war nichts zu finden. Auch ein direktes Aufstecken des Overo auf die Robovero-Platine half nichts. Diesem Problem konnte ich aufgrund des Endes meiner Ingenieurpraxis nicht weiter auf den Grund gehen.

Anhang



Auslesen des Accelerometers auf der Gallop43-Platine:
(nach http://old.nabble.com/3D-Accelerometer-on-Palo35-advice--td27933777.html und
http://comments.gmane.org/gmane.linux.distributions.gumstix.general/55721):

Zuerst: In der Kommandozeile ausführen:

i2cset -y 3 0x1d 0x20 0x47 b

Anschließend folgendes Kommandozeilen-Skript ausführen:


#!/bin/bash
regOutX=0x29
regOutY=0x2B
regOutZ=0x2D
outX=`i2cget -y 3 0x1d $regOutX b`
outY=`i2cget -y 3 0x1d $regOutY b`
outZ=`i2cget -y 3 0x1d $regOutZ b`
let outXg=${outX}
let outYg=${outY}
let outZg=${outZ}
if (($outXg > 127)); then let outXg=$outX-256; fi
if (($outYg > 127)); then let outYg=$outY-256; fi
if (($outZg > 127)); then let outZg=$outZ-256; fi
let outXg=${outXg}*18
let outYg=${outYg}*18
let outZg=${outZg}*18
echo "outX: $outX [$outXg milli g]"
echo "outY: $outY [$outYg milli g]"
echo "outZ: $outZ [$outZg milli g]"

Auslesen des Gyrometers auf der Robovero-Platine in Python:__
(nach http://wiki.gumstix.org/index.php?title=RoboVero)

Zuerst wird die Python-Entwicklungsumgebung benötigt. Kommandozeile:

git clone git://github.com/robovero/python.git

Anschließend bei angeschlossenem Robovero Python-Programm ausführen:

"""Example application that outputs accelerometer, compass and gyro readings.
"""

from robovero.extras import Array, roboveroConfig
from robovero.lpc17xx_i2c import I2C_M_SETUP_Type, I2C_MasterTransferData, \
I2C_TRANSFER_OPT_Type
from robovero.lpc17xx_gpio import GPIO_ReadValue
from robovero.LPC17xx import LPC_I2C0
from robovero.lpc_types import Status
import time

__author__ = "Neil MacMunn"
__email__ = "Diese E-Mail-Adresse ist vor Spambots geschützt! Zur Anzeige muss JavaScript eingeschaltet sein!
 "
__copyright__ = "Copyright 2010, Gumstix Inc"
__license__ = "BSD 2-Clause"
__version__ = "0.1"

gyro_ctrl_reg1 = 0x20
gyro_ctrl_reg2 = 0x21
gyro_ctrl_reg3 = 0x22
gyro_ctrl_reg4 = 0x23
gyro_ctrl_reg5 = 0x24
gyro_status_reg = 0x27
gyro_x_low = 0x28
gyro_x_high = 0x29
gyro_y_low = 0x2A
gyro_y_high = 0x2B
gyro_z_low = 0x2C
gyro_z_high = 0x2D
gyro_fifo_ctrl_reg = 0x2E

class I2CDevice(object):
def __init__(self, address):
self.config = I2C_M_SETUP_Type()
self.tx_data = Array(2, 1)
self.rx_data = Array(1, 1)
self.config.sl_addr7bit = address
self.config.tx_data = self.tx_data.ptr
self.config.retransmissions_max = 3
def readReg(self, register):
self.tx_data[0] = register
self.config.tx_length = 1
self.config.rx_data = self.rx_data.ptr
self.config.rx_length = 1
ret = I2C_MasterTransferData(LPC_I2C0, self.config.ptr,
I2C_TRANSFER_OPT_Type.I2C_TRANSFER_POLLING)
if ret == Status.ERROR:
exit("I2C Read Error")
return self.rx_data[0]
def writeReg(self, register, value):
self.tx_data[0] = register
self.tx_data[1] = value
self.config.tx_length = 2
self.config.rx_data = 0
self.config.rx_length = 0
ret = I2C_MasterTransferData(LPC_I2C0, self.config.ptr,
I2C_TRANSFER_OPT_Type.I2C_TRANSFER_POLLING)
if ret == Status.ERROR:
exit("I2C Write Error")
if self.readReg(register) != value:
exit("I2C Verification Error")
return None

# Initialize pin select registers
roboveroConfig()

# configure the gyro
# see L3G4200D Application Note for initialization procedure
gyro = I2CDevice(0x68)
gyro.writeReg(gyro_ctrl_reg3, 0x08) # enable DRDY
gyro.writeReg(gyro_ctrl_reg4, 0x80) # enable block data read mode
gyro.writeReg(gyro_ctrl_reg1, 0x09) # normal mode, enable x-axis

def twosComplement(low_byte, high_byte):
"""Unpack 16-bit twos complement representation of the result.
"""
return (((low_byte + (high_byte << 8)) + 2**15) % 2**16 - 2**15)

while True:

print "g [x, y, z]: ",
print [
twosComplement(gyro.readReg(gyro_x_low), gyro.readReg(gyro_x_high)),
twosComplement(gyro.readReg(gyro_y_low), gyro.readReg(gyro_y_high)),
twosComplement(gyro.readReg(gyro_z_low), gyro.readReg(gyro_z_high))
]
print gyro.readReg(0x26)
print ""
time.sleep(1)

Auslesen des Temperatursensors KTY81-210 am Eingang 4 in C:__
(nach http://wiki.gumstix.org/index.php?title=ADC_overo_2.6.38-)

/* KTY81-210 mit Overo auslesen
* nach Beispiel von Hugo Vincent, 2009
*/
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
typedef uint8_t u8;
typedef uint16_t u16;
#include "twl4030-madc.h"
/* KTY81-210 angeschlossen an den Eingang ADC4 ueber Spannungsteiler
* mit 1,35KOhm Vorwiderstand.
*/

int main(int argc, char **argv)
{
char i = 0;
char j = 4;
float temp = 0;
int d = open("/dev/twl4030-madc", O_RDWR | O_NONBLOCK);
if (d == -1)
{
printf("could not open device /dev/twl4030-madc \n");
EXIT_FAILURE;
}
char k = 1;
float summe = 0;
while (k==1)
{
summe = 0;
for (i=0; i<100; i++)
{
struct twl4030_madc_user_parms *par;
par = malloc(sizeof(struct twl4030_madc_user_parms));
memset(par, 0, sizeof(struct twl4030_madc_user_parms));
par->channel = j;
int ret = ioctl(d, TWL4030_MADC_IOCX_ADC_RAW_READ, par);
float result = ((unsigned int)par->result) / 1024.f;/* 10 bit ADC -> 1024 */
summe = summe + result;
}
temp = summe * 2.5;
temp = temp / 100;
printf ("%.3lfV, ", temp);
temp = temp * 151;
temp = temp - 273;
printf("%.0lf °C\n", temp);
}
return 0;
}