Navigation |
A/D-Wandler:
Da der Sourcecode wieder ein bisschen länger ist, öffnet er sich in einem neuen Fenster. Das Programm beginnt wie die anderen bisher auch, nämlich mit dem Einbinden der Header-Datei, der Definition der Prototypen und dem Abschalten des Watchdogs. Port 1 benutzen wir wieder als Output (wegen der LEDs), Port 6 wieder als Input. P6SEL weist nun die erste Neuerung auf: Bisher haben wir alle Ports immer als "normale" I/O-Ports benutzt. Wie ich im Kapitel LEDs blinken! aber ja schon erwähnte, besitzen alle Pins eine Doppelbelegung. So kann man die Pins an Port 6 als GPIO (general purpose input/output) und eben auch als Eingänge für die A/D-Wandler benutzen. Da der MSP430 aber schlecht raten kann, was wir vorhaben, müssen wir die Verwendung als ADC12-Eingänge explizit festlegen, und zwar mit P6SEL=0x01. Man errät also unschwer, dass eine logische "0" den jeweiligen Pin als GPIO benutzt, wohingegen eine "1" ihm seine Zweitfunktion zuweist. Es sind hier natürlich beliebige Kombinationen möglich, so würde z.B. ein P6SEL=0xD2 P6.1, P6.4, P6.6 und P6.7 als A/D-Wandler-Eingänge benutzen und den Rest (P6.0, P6.2, P6.3 und P6.5) als I/O-Leitungen. Nach zwei weiteren schon bekannten Zuweisungen geht es mit der Konfiguration des ADC12 los. Hierzu ist ein Blick in den User's Guide des MSP430 (zu finden auf der TI-Website) und das Datenblatt (zu finden z.B. auf Elektronikladen.de) sehr empfehlenswert. OK, ich gebe es zu, der User's Guide und das Datenblatt sind wirklich alles andere als leichte Lektüre und die Funktionsweise des ADC12 dürfte dadurch wirklich nur erfahrenen Embedded-Programmierern auf Anhbieb klar werden. Deswegen werde ich versuchen, die wesentlichen Fakten ein wenig näher zu beleuchten und die weniger wichtigen erst mal zu vernachlässigen. Die erste wichtige Frage ist: Wie steuere ich den A/D-Wandler eigentlich? Nun, hierzu gibt es zwei Register, ADC12CTL0 und ADC12CTL1. Durch Zuweisungen an diese beiden Register kann man alle möglichen Parameter zum Betrieb des ADC12 einstellen. Mit der Anweisung ADC12CTL0=ADC12ON+SHT0_0+REFON+REF2_5V; geschehen mehrere Sachen. ADC12ON schaltet den ADC12 erst einmal ein. Wäre ADC12ON nicht gesetzt, wäre der A/D-Wandler komplett ausgeschaltet, was eine etwas geringere Leistungsaufnahme des MSP430 zur Folge hätte. So könnte man z.B. im Batteriebetrieb Strom sparen. SHT0_0 bewirkt, dass die Sample-and-hold-Zeit auf n=1 eingestellt wird (siehe Datenblatt Seite 31). Da wir ja nur eine Spannung an einem Poti abtasten und keine komplizierten, schnell veränderlichen analogen Signale, ist die Abtastdauer eigentlich völlig egal. Es würde jetzt zu sehr in die elektrotechnischen Details gehen, das Nyquist-Theorem zu erklären, aber wer unbedingt mal ein 120kHz-Signal an den A/D-Wandler legen will, wird schon wissen, was das für seine Abtastrate bedeutet. :-) Hier nehmen wir einfach n=1 (und damit also t_sample=4*ADC12CLK*1), das funktioniert wunderbar. Etwas weiter ausholen muss man bei REFON. Der ADC12 benötigt für die Konvertierung eine interne Ober- und Untergrenze (die sogenannte Referenz-Spannung), damit er weiss, wie er die Werte dazwischen umwandeln soll. Wählt man z.B. die Untergrenze (sinnigerweise) als 0 Volt und die Obergrenze als 2,5 Volt, so entspricht eine Spannung von 0 Volt an unserem P6.0 einer digitalen 000000000000 und 2,5 Volt entprechen 111111111111. Man zählt nach, dass dies jweils 12 Bit sind, was auch logisch ist, da wir ja einen 12-Bit-A/D-Wandler haben. ;-) Nun, und für diese Referenz-Spannung gibt es prinzipiell zwei verschiedene Wege der Realisierung: Durch Anlegen einer externen Spannung an einen bestimmten Pin (Nummer 10) oder durch interne Erzeugung im MSP430. Die interne Erzeugung bietet Referenz-Spannungsquellen von 1,5 und 2,5 Volt. Der Vorteil dieser Quellen ist, dass man sich jegliche externe Lötarbeiten spart, der Nachteil ist meiner Meinung nach die etwas ungeschickte Wahl der Spannungen -- ich hätte mir eigentlich noch 3,3 Volt gewünscht, um direkt auf VCC-Level arbeiten zu können. Dies kann man aber halt durch Anlegen einer eigenen beliebigen Referenz-Spannung an Pin 10 erreichen. Jetzt ist es unschwer zu erraten, was REFON bewirkt: Es schaltet die interne Referenz-Spannungsquelle ein und schaltet sie mit REF2_5V auf 2,5 Volt. Wäre REF2_5V nicht gesetzt, würde die Spannungsquelle 1,5 Volt erzeugen. Da unser Poti aber an VCC (also 3,3 Volt) hängt, ist 2,5 Volt die bessere Wahl. Unser "Arbeitsbereich" wird zwar ein wenig eingeschränkt sein, da alle Spannungen zwischen 2,5 und 3,3 Volt am Poti gleichermaßen zu 111111111111 konvertiert werden, aber das soll uns erst mal nicht weiter stören. Durch die nächste Anweisung (ADC12CTL1=SHP;) legen wir fest, dass wir jegliche Einstellungen zum Sample-and-hold-Vorgang erst mal möglichst automatisch machen lassen wollen. Man kann bei der Sample-Rate die abenteuerlichsten Konfigurationen auswählen (das Datenblatt hilft weiter ...), wir wollen uns jedoch erst mal keine Gedanken machen und wählen den SHP-Modus. Das besagt grob vereinfacht, dass wir über einen einzigen Software-Befehl eine A/D-Wandlung starten können. Daraufhin wird ein Abtastvorgang ausgelöst (wenige µs lang), gefolgt von der völlig automatisch eingeleiteten Konvertierung der analogen Spannung in einen digitalen Wert. Das Ergebnis der Konvertierung wird in ein Register geschrieben, von wo aus es bequem weiter verarbeitet werden kann. Hierzu kommen wir aber später noch detaillierter. Der nächste Befehl (ADC12MCTL0=SREF_1+INCH_0;) bearbeitet das Kontrollregister des ersten Ausgaberegisters. Hierzu muss man wissen, dass der ADC12 16 Register (ADC12MEM0 bis ADC12MEM15) besitzt, in denen die Ergebnisse der A/D-Wandlung gespeichert werden. Benutzt man z.B. alle 8 Kanäle gleichzeitig, ist es klar, dass man schon 8 Register braucht, um wirklich parallel arbeiten zu können. Möchte man nun mehrere Messungen hintereinander durchführen, ohne jedes Mal direkt einen Auslesevorgang starten zu müssen, sieht man, dass diese 16 Register schon ziemlich sinnvoll sein können. SREF_1 bewirkt, dass die intern erzeugten 2,5 Volt als Referenz-Spannungsquelle gewählt werden. "Moment!" werden nun einige rufen. "Das haben wir doch eben schon gemacht?!". Das stimmt teils. Durch REFON und REF2_5V haben wir die interne Referenz-Spannungsquelle lediglich eingeschaltet. Wie und wo und ob wir sie überhaupt benutzen, ist dort aber noch keinesfalls festgelegt. Das Praktische am ADC12 ist nämlich, dass man für jedes der 16 Register eine eigene Referenz-Spannungsquelle festlegen kann. So kann man ganz leicht z.B. vier Kanäle mit 2,5 Volt Referenz-Spannung betreiben, zwei mit 1,5 Volt und zwei mit extern angelegten 3,3 Volt. Die den Registern zugeordneten Kanäle kann man auch frei wählen. Wie man an INCH_0 schon erkennt, ordnen wir ADC12MEM0 den Kanal 0 (also P6.0, d.h. Pin 59) zu. Zusammengefasst bedeutet das: Die Spannung an P6.0 wird in einen digitalen Wert mit der Obergrenze 2,5 Volt konvertiert und anschließend in ADC12MEM0 geschrieben, von wo aus sie weiter verarbeitet werden kann. Der letzte Initialisierungs-Befehl (ADC12CTL0 |= ENC;) ist eine Art zweiter Schalter. Über ADC12ON haben wir den ADC12 ja schon generell eingeschaltet, über ENC schalten wir ihn jetzt zum Konvertieren frei. Das hat den Sinn, dass fast alle bisher vorgenommenen Einstellungen nur bei ENC=0 vorgenommen werden können. Durch setzen von ENC nageln wir quasi unsere Konfiguration fest und geben den ADC12 zum Konvertieren frei. Das eigentliche Hauptprogramm läuft nun wieder in einer Endlosschleife, welche als erstes eine A/D-Wandlung einleitet. Dies geschieht über den Befehl ADC12CTL0|=ADC12SC;. Nun wird P6.0 für einige Mikrosekunden abgetastet; anschließend wird der ermittelte Spannungswert konvertiert und das Ergebnis in ADC12MEM0 geschrieben. Da das Ganze ein bisschen dauert, machen wir sicherheitshalber mit wait(10000); eine kurze Pause. Dann prüfen wir, ob der ermittelte Spannungswert kleiner ist als 0x7FF. Das entspricht 11111111111 (11 Einsen) in binärer Schreibweise, was fast genau die Hälfte von 111111111111 (12 Einsen), dem Maximum des Wertebereiches, ist. Ist der Wert kleiner als 0x7FF (also kleiner als 1,25 Volt), so wird die LED gelöscht, andernfalls wird sie angeschaltet. Anschließend ist die Schleife beendet und es wird direkt die nächste Konvertierung mittels ADC12CTL0|=ADC12SC; eingeleitet -- das Spiel beginnt von vorne ... Naja, und weil ein einfaches LED-an-aus nicht so wirklich prickelnd ist, kann man den Wert aus dem ADC12MEM0 natürlich auch auslesen, mit DecToString() oder IntToString() in einen String umwandeln und auf dem LCD ausgeben. Je nachdem, wie viel Mühe man sich mit der Formatierung der Ausgabe macht, kann das ganze dann z.B. so aussehen: Nun denn, das soll für's Erste reichen. :-) Wenn ihr dieses relativ einfache Beispiel verstanden habt, sollte es kein allzu großes Problem sein, das Datenblatt mal genauer durchzulesen um herauszufinden, was es mit den zu Beginn erwähnten Kanälen 8,9 und 10 auf sich hat. Tipp: Kanal 10 ist der interne Temperatursensor, er kann ganz normal über INCH_10 gewählt und mit 2,5 Volt verglichen werden. Mit etwas Nachlesen schafft ihr es bestimmt, das einfache Thermometer, das ich hier aufgebaut habe, zu programmieren. :-) |