Autor: | Hubert Högl <Hubert.Hoegl@hs-augsburg.de> |
---|---|
Datum: | 2012-01-31 |
URL: http://elk.informatik.fh-augsburg.de/pub/gnublin-lpc3131/appnotes/adc.html
Inhalt
Das Bild zeigt zwei analoge Eingabebauelemente, ein Potentiometer und einen Fotowiderstand.
- Gnublin booten
- Einloggen als root (bzw. automatischer Login)
- Einfache Kommandos auf der Kommandozeile eingeben
- Python Interpreter starten und auf dem Interpreter-Prompt arbeiten
- C Programme kompilieren, auf Gnublin übertragen und starten
- Die Programmdateien zum ADC Versuch sollten in nicht zu ferner Zukunft als Download unter [4] zu finden sein.
Die folgende Abbildung zeigt die Pins des LPC3131, die mit dem ADC zu tun haben. Es gibt vier analoge Eingänge die Spannungen zwischen 0 und 3,3 Volt messen können. Man sollte aufpassen, dass die Spannung direkt an den Eingängen nicht höher als 3,3 Volt werden kann. Zur Not muss man mit einer externen Schaltung sicherstellen, dass dies nicht passieren kann.
Die Auflösung der ADC Eingänge kann per Software in einem Bereich von 2 bis 10 Bit eingestellt werden. Je weniger Bit man zur Auflösung nimmt, um so schneller wird die Wandlung von analog zu digital. Bei 10 Bit Auflösung kann man bis zu 400.000 Abtastungen pro Sekunde machen, bei 2 Bit Auflösung sind es bis zu 1.500000 Abtastungen.
Die ADC Eingänge des LPC3131 messen einen Spannungsbereich zwischen 0 und 3.3 Volt. Bei 10 Bit Auflösung entspricht dies einem digitalen Wertebereich zwischen 0x000 und 0x3ff (dezimal 0 bis 1023).
Vorsicht: Man muss am Anfang den gewünschten ADC Eingang auswählen. Wenn man dies nicht macht, dann kommt zu seltsamen Effekten. Misst man zum Beispiel die Spannung am Analogeingang GPA1 ohne ihn vorher auszuwählen dann kann man auch einen digitalen Spannungswert lesen, der jedoch schon bei 1,8 Volt den maximalen Wert bei 10 Bit Auflösung von 0x3ff hat. Bei unserem Treiber macht man die Auswahl des ADC Einganges wie folgt (am Beispiel GPA1):
echo "1" > /dev/lpc3131_adc
Bereich zwischen 0 und 3,3 Volt sind? Möglichkeiten sind:
Der Treiber für den ADC wurde von Michael Schwarz und Nils Stek geschrieben. Man kann die Entstehungsgeschichte im Forum [1] verfolgen. Der Treiber wird bei Gnublin als Kernelmodul geladen, das macht man so:
root@armv5te:~# mobprobe lpc313x_adc
Beim Laden des Treibers wird folgende Meldung ausgegeben:
[lpc313x adc] driver loaded with major 253 [lpc313x adc] >> $ mknod /dev/lpc313x_adc c 253 0
Die zweite Zeile erinnert daran, dass man eine zugehörige Gerätedatei mit dem Kommando mknod anlegen muss, falls diese noch nicht existiert. Am besten zunächst mit ls /dev nachsehen, ob die Datei bereits existiert.
Es kann auch sein, dass der Treiber bereits beim Booten geladen wurde. Wenn das so ist, dann steht er in der Konfigurationsdatei /etc/modules:
root@armv5te:~# cat /etc/modules ... lpc313x_adc ...
Welche Treiber gerade in den Kernel geladen sind, findet man mit dem Befehl lsmod heraus. Auf meinem Gnublin Board sieht die Ausgabe gerade so aus:
root@armv5te:~# lsmod Module Size Used by lpc313x_adc 3766 0 pegasus 16772 0 mii 3416 1 pegasus
Zunächst verbinden wir das Potentiometer mit Gnublin:
Das Potentiometer entspricht einem variablen Spannungsteiler, der eine Spannung zwischen 0 und 3,3V am Analogeingang GPA1 einzustellen ermöglicht.
ACHTUNG: Die Spannung am GPA1 Eingang darf nicht höher als 3,3V sein!
Am einfachsten kann man mit Shell Kommandos die Analogwerte einlesen. Zunächst muss man den gewünschten Analogeingang GPA1 auswählen, das geht mit
root@armv5te:~# echo "1" > /dev/lpc3131_adc
Danach kann man schon die Analogwerte einlesen mit
root@armv5te:~# cat /dev/lpc313x_adc
Die Ausgabe ist einfach eine Hexzahl, z.B.
0x3b2
Die voreingestellte Auflösung des Wandlers ist 10 Bit. Damit liegen die eingelesenen Werte zwischen 0 und 0x3ff (dezimal 1023). Der Wandler kann auch mit 8- oder 4-Bit Auflösung arbeiten, genaueres kann man [2] entnehmen. Die README Datei des Treiber beschreibt, wie man die Einstellungen des ADC ändern kann.
Wenn man mit cat wiederholt Werte aus einer Datei (in unserem Fall eine "Pseudodatei") herauslesen möchte, dann ist auch das Werkzeug watch sehr praktisch. Es gibt regelmässig neue Werte in der Konsole aus, die alte Ausgabe wird alle 2 Sekunden durch die neue überschrieben:
watch cat /dev/lpc313x_adc
Wenn man den neuen Wert jede Sekunde erhalten möchte, dann gibt man ein:
watch -n 1 cat /dev/lpc313x_adc
Die Ausgabe sieht damit so aus:
Every 1.0s: cat /dev/lpc313x_adc Mon Jul 9 11:07:21 2012 0x1f89
Genaueres sagt wie immer die man page zu watch (man watch).
Ein einfaches Python Programm zum Ansteuern des ADC sieht so aus:
import os DEVICE = '/dev/lpc313x_adc' def select_gpa1(): fd = os.open(DEVICE, os.O_RDWR) os.write(fd, "0x0001") os.close(fd) def get_adc(): fd = os.open(DEVICE, os.O_RDONLY) av = os.read(fd, 256) os.close(fd) return av[:-1] # strip off trailing '\n' if __name__ == "__main__": select_gpa1() ad_val = get_adc() print ad_val
Den zurückgegebenen Hex-String (z.B. 0x37b) kann man einfach in eine Zahl umwandeln. Dazu nimmt man die eingebaute Funktion int(val, 16).
So wie der Treiber momentan programmiert ist, erfolgt das Einlesen des ADC Wertes beim Öffnen der Gerätedatei. Man muss also bei jedem Einlesevorgang den Zyklus Öffnen, Lesen, Schliessen durchlaufen. Wenn man die Zeit misst, die für das Einlesen von 1000 ADC Werten benötigt werden, dann kommt etwa eine Sekunde heraus, das heisst, je Abtastvorgang wird etwa 1 msec benötigt. Es ist klar, dass die mächtige Interpretersprache hier langsamer sein wird als ein in C geschriebenes Programm.
Ein nützliches Muster zum Messen der Zeitspanne in Python ist
import time t1 = time.clock() ... t2 = time.clock() print t2 - t1
Verzögerungen baut man mit time.sleep() ein, z.B. verzögert folgende Zeile um 0,3 Sekunden:
time.sleep(0.3)
Genaueres sagt wie immer die Dokumentation der Python Standardbibliothek zum time Modul [3]. Die Homepage der Sprache Python ist http://www.python.org.
# adc.rb # Gnublin Project www.gnublin.org # <Hubert.Hoegl@hs-augsburg.de>, July 2012 # # This program reads from ADC input pin GPA1. file = File.new("/dev/lpc313x_adc", "w") file.puts("1") # select GPA1 file.close n = 0 while (n < 10) file = File.new("/dev/lpc313x_adc", "r") adc_value = file.gets() file.close puts(adc_value) sleep(1) n = n + 1 end
-- adc.lua -- Gnublin Project www.gnublin.org -- <Hubert.Hoegl@hs-augsburg.de>, July 2012 -- -- This program reads from ADC input pin GPA1. if os.path_exists("/dev/lpc313x_adc") == 1 then f = io.open("/dev/lpc313x_adc", "w") f:write("1") -- select GPA1 f:close() else print("no such file: /dev/lpc313x_adc") os.exit(1) end n = 1000 print("reading " .. n .. " ADC values") repeat f = io.open("/dev/lpc313x_adc", "r") t = f:read("*line") f:close() n = n - 1 until n == 0
In C sieht das Programm wie folgt aus:
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <time.h> unsigned char buffer[256]; void select_gpa1() { /* select ADC 1 */ fd = open("/dev/lpc313x_adc", O_RDWR); write(fd, "1", 2); /* "1\0" */ close(fd); } int get_adc() { fd = open("/dev/lpc313x_adc", O_RDONLY); n = read(fd, buffer, 256); close(fd); return n; }
Dieses Programm kann man durchaus mit dem gcc direkt auf Gnublin übersetzen, allerdings sollte man etwas Zeit mitbringen. Es wird ungefähr 30 Sekunden dauern. Diese lange Wartezeit entsteht durch die geringe Speichergrösse von 8 MByte. Der (riesige) gcc muss zum Kompilieren den Swap-Speicher auf der SD-Karte nutzen und das benötigt viel Zeit. Schneller geht es, wenn man die Kompilierung auf dem Hostrechner mit einem ARM Cross-Compiler erledigt und die ausführbare Datei anschliessend auf das Gnublin Board überträgt. In Zukunft soll es Gnublin auch mit 32 MByte Hauptspeicher geben, was die Zeit zum nativen Kompilieren erheblich reduzieren sollte.
Die Funktion get_adc() gibt die Anzahl der gelesenen Zeichen zurück (inklusive des '\n' Zeichen am Ende). Das C Programm benötigt für 1000 Lesevorgänge etwa 0,18 Sekunden, ist also ungefähr 5 Mal so schnell wie das Python Programm.
Am Schluss des ADC Experiments kann man den Treiber mit dem Befehl
root@armv5te:~# rmmod lpc313x_adc [lpc313x adc] DRIVER UNLOADED
wieder entladen.
[1] Gnublin Forum auf mikrocontroller.net
http://www.mikrocontroller.net/topic/237277
[2] LPC3131 User Manual, siehe das Kapitel über den ADC.
http://www.nxp.com/documents/user_manual/UM10314.pdf
[3] Die Python Standardbibliothek, time Modul
http://docs.python.org/library/time.html
Python Home: http://www.python.org
[4] Gnublin Downloads
http://www.gnublin.org/downloads