Homepage Wiki Forum Buy

AD-Wandler

Aus GNUBLIN

Schwierigkeitsgrad Voraussetzung Gnublin Familie
Gnublin logo easy.png Kommandozeile oder Zugriff per Treiber mit anschliessendem Datei lesen oder schreiben Alle

Inhaltsverzeichnis

AD-Wandler

Der LPC3131 hat einen 4-Kanal Analog zu Digital Wandler, der bei einer Auflösung von 10 Bit eine Abstastrate von 400kSamples/s schafft. Die Auflösung kann zusätzlich manuell von 10 auf bis 2 Bit eingestellt werden. Bei einer 2 Bit Auflösung erreicht man eine Geschwindigkeit von 1500kSamples/s.


Die Schnittstellen der einzelnen GNUBLIN Bauformen und deren Position können hier angesehen werden.

Errata:

Es gibt offensichtlich Boards mit Bestückungsfehlern, die den Wertebereich des AD Wandlers auf 0 .. 1,6V einschränken. R19 ist in diesem Fall mit 10 kOhm statt der korrekten 10 Ohm bestückt. In der Elektor Ausgabe 1/2013 Artikel 'Embedded Linux leicht gemacht (7)' wird statt dessen empfohlen, zwei Kondensatoren von 10µF und 100nF von R19 nach Masse zu löten.

Widerstand gnublin.png

Siehe Forum: http://forum.gnublin.org/viewtopic.php?f=3&t=198

Für Anfänger

Zu aller erst muss der analoge Eingangspin richtig verschalten werden. Hierzu findet sich weiter unten im Artikel ein Schaltplan.

Um nun schnell und einfach den aktuellen Spannungswert am analogen Eingangspin zu lesen gibt es ein Programm.

Um den aktuellen Spannungswert am Pin GPA1 zu lesen wird einfach folgendes eingegeben:

gnublin-adcint -c 1

als Ausgabe sollte folgendes Erscheinen:

3049 mV

Der LPC3131 hat 4 interne AD-Wandler (Channel 0 - 3).

Möchte man zB den AD-Channel 3 (GPA3) auslesen so lautet das Kommando hierfür folgendermaßen:

gnublin-adcint -c 3

Mit der Eingabe

gnublin-adcint -h

erhält man eine Übersicht über alle weiterne Parameter:

-h Show this help -b show raw output -j Convert output to json format -c <x> Select gpa<x> Pin (0 - 3 possible, default is 1) -d Choose Path to device file

Für Fortgeschrittene

Den Treiber laden

Hinweis: Auf der Standard SD-Karte von GNUBLIN sind diese Schritte bereits vom System her durchgeführt. Man kann diesen Schritt, sofern man nicht den Kernel selbst gebaut hat überspringen

Der Treiber für den ADC wurde von Michael Schwarz und Nils Stec geschrieben. Man kann die Entstehungsgeschichte im Forum [1] verfolgen. Der Treiber wird bei Gnublin als Kernelmodul geladen, das macht man so:

root@gnublin:~# modprobe 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@gnublin:~# 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@gnublin:~# lsmod
Module Size Used by
lpc313x_adc 3766 0
pegasus 16772 0
mii 3416 1 pegasus

Die Experimente können beginnen...

Zunächst verbinden wir das Potentiometer mit Gnublin:

Adc-poti.png

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!


Die Shell

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@gnublin:~# echo "1" > /dev/lpc313x_adc

Danach kann man schon die Analogwerte einlesen mit

root@gnublin:~# 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 dem LPC3131 User Manual, siehe das Kapitel über den ADC entnehmen. Die README Datei des Treiber beschreibt, wie man die Einstellungen des ADC ändern kann.

Python

Das folgende Programm ist auch unter /root/examples/misc/adc/adc.py zu finden.

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 [2].

ADC mit C

Beispielfunktionen um den ADC-Kanal zu selektieren und den ADC-Wert auszulesen und in Integer umzuwandeln:

#include <fcntl.h> /* open() O_RDONLY=0 O_RDWR=2 */
#include <stdio.h> /* sscanf() */
#include <stdlib.h> /* EXIT_FAILURE=1 EXIT_SUCCESS=0 */
#include <unistd.h> /* close() read() write() */
 
int selectADC( int chanel ) {
	if ( chanel < 0 || chanel > 3 ) { return EXIT_FAILURE; }
 
	int fd;
	int ret;
	char sChanel[2] = { '0' + chanel , '\0' };
 
	fd = open( "/dev/lpc313x_adc" , O_RDWR );
	if ( fd < 0 ) { return EXIT_FAILURE; }
	ret = write( fd , sChanel , sizeof(sChanel) );
	if ( ret != sizeof(sChanel) ) { close( fd ); return EXIT_FAILURE; }
	ret = close( fd );
	if ( ret != EXIT_SUCCESS ) { return EXIT_FAILURE; }
 
	return EXIT_SUCCESS;
}
 
int getADC( void ) {
	int fd;
	int ret;
	int val;
	char sVal[7]; /* { '0' , 'x' , '3 ' , 'F' , 'F' , '\n' , '\0' } */
 
 
	fd = open( "/dev/lpc313x_adc" , O_RDONLY );
	if ( fd < 0 ) { return -1; }
	ret = read( fd , sVal , sizeof(sVal) );
	if ( ret != sizeof(sVal) ) { close( fd ); return -2; }
	ret = close( fd );
	if ( ret != EXIT_SUCCESS ) { return -3; }
 
	ret = sscanf( sVal , "0x%x" , &val );
	if ( ret != 1 ) { return -4; }
 
	return val;
}

Ein weiterer Code-Auszug aus den Beispielen:

Das Programm ist im Ordner /root/examples/misc/adc zu finden und nennt sich adc.c.

#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;
}

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.

In anderen Sprachen