AD-Referenz in Bascom umschalten
Hexfile disassemblieren, eigene AD-Funktion
Im Mega32 ist jede Menge Platz
für neue Ideen.
So könnte man z.B. ganz spezielle Messprogramme in Bascom
entwickeln. In diesem Zusammenhang habe ich nach
einer Möglichkeit gesucht, die Referenz des AD-Wandlers im
laufenden Betrieb umzuschalten. Dazu muss der Ref-Anschluss des
Controllers frei bleiben. Nur ein 100-nF-Kondensator gegen Masse ist zu
empfehlen. Das Umschalten per Software ist anscheinend nicht so einfach
wie man denken könnte. Der erste Versuch ging etwa so, womit auf
die interne Referenz von 2,56 V umgeschaltet werden sollte:
If Einstellung2 then Config Adc = Single , Prescaler = 32 , Reference = Internal
Keine
Reaktion. Anscheinend ist die ursprüngliche Config-Anweisung
für den Compiler bindend und kann zur Laufzeit nicht mehr
korrigiert werden. Wie ging das noch mit dem Umschalten? Das Datenblatt
zum Mega32 sagt: Alles steht im Register ADMUX, der Kanal, und auch die
verwendete Referenz. Die Einstellungen zur Referenz belegen die oberen
beiden Bits: 11 bedeutet interne Referenz, 01 bedeutet AVCC. Also
sollte man doch einfach dieses Register verändern können:
If Einstellung2 then Admux = 192
Die
Hoffnung war, dass Bascom Admux liest, den gewünschten
AD-Kanal in den unteren Bits einfügt und wieder
zurückschreibt. Ging aber leider auch nicht. Mit konstanter
Boshaftigkeit wurde immer nur die Einstellung aus der
ursprünglichen Config-Anweisung genommen. Wie das überhaupt
geht, mit dem AD-Wandler, das zeigt ein Assemblerprogramm aus dem
Lernpaket Mikrocontroller für den Tiny13:
;ADC3.asm, AD-Wandler mit Referenz 1,1 V
.include "tn13def.inc"
.def A = r16
.def B = r17
.def Delay = r18
.def Count = r19
;Port B
.equ TXD = 1
.equ RXD = 2
rjmp Anfang
Anfang:
sbi ddrb,TXD ;Datenrichtung TXD
rcall AdcInit
Schleife:
rcall RdCOM
rcall RdADC
rcall WrCOM
rjmp Schleife
AdcInit:
ldi A,3 ;Clock / 4
out ADCSRA,A
sbi ADCSRA,ADEN ;AD einschalten
ret
RdADC:
out ADMUX,A
sbi ADMUX,ADLAR ;Left adjust
sbi ADMUX,REFS0 ;1,1 V Referenz
sbi ADCSRA,ADSC ;Wandlung starten
ADrdy:
sbic ADCSRA,ADSC
rjmp ADrdy
sbi ADCSRA,ADSC
ADrdyb:
sbic ADCSRA,ADSC
rjmp ADrdyb
in A,ADCH
ret
Die
spannende Frage ist nun, was läuft da in Bascom anders, und was
kann man dagegen tun? Man muss dem Compiler einmal genauer auf die
Finger schauen, indem das Ergebnis in Assembler analysiert wird. Das
geht mithilfe des AVR Studio 4. Beim Kompilieren soll Bascom ein
OBJ-File erzeugen.
Hier ein extra kurzes Basic-Programm, das helfen soll, der Sache auf den Grund zu gehen:
$regfile = "m32def.dat"
Dim Dd As Word
Config Adc = Single , Prescaler = 32 , Reference = Internal
Start Adc
Do
Dd = Getadc(3)
Loop
Beim Kompilieren erzeugt Bascom neben dem Hexfile und anderen Dateien auch
das OBJ-File. Dieses kann man nun in das AVR Studio laden. Es wird
automatisch ein AVR-Projekt erstellt und der Simulator gestartet.
Vorher wird man noch gefragt, welcher Chip gemeint ist.

Im
Simulator kann man dem Programm mit allen möglichen Mitteln auf
die Finger schauen. Zum Beispiel mit dem Disassembler.

Hier ist das Ergebnis des Disassemblers. Die entscheidende Stelle ist markiert und zusätzlich kommentiert. Die
für die Referenz zuständigen Bits im ADMUX-Register (Adresse 0x07)
werden als Konstanten eingefügt, so wie der Compiler sie aus der
Config-Anweisung gelesen hat. Zur Laufzeit ist da leider nichts mehr zu
ändern.
--- Test2.bas ------------------------------------------------------------------------------------
5: Config Adc = Single , Prescaler = 32 , Reference = Internal
+0000003D: E085 LDI R24,0x05 Load immediate
+0000003E: B986 OUT 0x06,R24 Out to I/O location
5: Config Adc = Single , Prescaler = 32 , Reference = Internal
+0000003F: EC80 LDI R24,0xC0 Load immediate
+00000040: B987 OUT 0x07,R24 Out to I/O location
6: Start Adc
+00000041: 9A37 SBI 0x06,7 Set bit in I/O register
10: Dd = Getadc(3)
+00000042: EC83 LDI R24,0xC3 Load immediate
+00000043: 6C80 ORI R24,0xC0 Logical OR with immediate , 1100 000 fest vorgegeben
10: Dd = Getadc(3)
+00000044: B987 OUT 0x07,R24 Out to I/O location , ins ADMUX-Register
+00000045: 940E0056 CALL 0x00000056 Call subroutine
+00000047: E6A0 LDI R26,0x60 Load immediate
10: Dd = Getadc(3)
+00000048: E0B0 LDI R27,0x00 Load immediate
+00000049: 938D ST X+,R24 Store indirect and postincrement
10: Dd = Getadc(3)
+0000004A: 939C ST X,R25 Store indirect
12: Loop
+0000004B: 940C0042 JMP 0x00000042 Jump
12: Loop
+0000004D: 9731 SBIW R30,0x01 Subtract immediate from word
+0000004E: F7F1 BRNE PC-0x01 Branch if not equal
12: Loop
+0000004F: 9508 RET Subroutine return
+00000050: 9468 SET Set T in SREG
12: Loop
+00000051: F862 BLD R6,2 Bit load from T to register
+00000052: 9508 RET Subroutine return
12: Loop
+00000053: 94E8 CLT Clear T in SREG
+00000054: F862 BLD R6,2 Bit load from T to register
12: Loop
+00000055: 9508 RET Subroutine return
+00000056: 9A36 SBI 0x06,6 Set bit in I/O register
12: Loop
+00000057: 9936 SBIC 0x06,6 Skip if bit in I/O register cleared
+00000058: CFFE RJMP PC-0x0001 Relative jump
12: Loop
+00000059: 9A36 SBI 0x06,6 Set bit in I/O register
+0000005A: 9936 SBIC 0x06,6 Skip if bit in I/O register cleared
12: Loop
+0000005B: CFFE RJMP PC-0x0001 Relative jump
+0000005C: B184 IN R24,0x04 In from I/O location
12: Loop
+0000005D: B195 IN R25,0x05 In from I/O location
+0000005E: 9508 RET Subroutine return
Das
Ergebnis ist klar: Mit den Standard-Befehlen kann man die Referenz
nicht umschalten! Also muss eine eigene Funktion her. Das ist gar nicht
so schwierig, denn man kann ja einiges abschreiben, z.B. dass Bascom
den AD-Wandler gleich zweimal hintereinander startet. Hier ist eine
mögliche Lösung, bei der die Referenzeinstellung zusammen mit
dem Kanal übergeben wird:
$regfile = "m32def.dat"
$hwstack = 32
$swstack = 64
$framesize = 64
$baud = 19200
$crystal = 11059200
Dim Dd As Word
Dim Ref As Byte
Declare Function Ad(byval C As Byte) As Word
Config Adc = Single , Prescaler = 32 , Reference = Avcc
Start Adc
Ref = 192 'Ref 2,56 V
Do
Dd = Ad(7)
Print Dd
Waitms 500
Loop
Function Ad(byval C As Byte) As Word
Local Dl As Byte
Local Dh As Byte
Local Dd3 As Word
C = C + Ref
Admux = C
Adcsr.6 = 1
Do
Loop Until Adcsr.6 = 0
Adcsr.6 = 1
Do
Loop Until Adcsr.6 = 0
Dl = Adcl
Dh = Adch
Dd3 = 256 * Dh
Dd3 = Dd3 + Dl
Ad = Dd3
End Function
Elektronik-Labor Projekte AVR