In diesem ASM-Programm soll über ein lineares Potentiometer eine Segmentanzeige gesteuert werden. Je nach Poti-Stellung wird eine Zahl ausgegeben. In diesem Fall die Zahlen “0” bis “9”.
Bevor ein Programm geschrieben werden kann, müssen vorher ein paar Vorüberlegungen getroffen werden. Der MSP430G2231 besitzt ein 10Bit-ADC, der Wertebereich beträgt somit 0-1023 (0 bis (2^10)-1). Eine Spannung von 0V am Analog-Eingang ergibt einen digitalen Wert von “0”, die maximal zulässige Spannung ergibt einen Wert von “1023”.
In diesem Beispiel-Programm sollen die Ziffern “0”-“9” auf einer 7-Segmentanzeige ausgegeben werden, also insgesamt 10 Werte. Das bedeutet: Der maximale ADC-Wert von 1023 wird durch 10 geteilt. Somit erhählt man die geeigneten ADC-Bereiche für die weitere Berechnung und Ausgabe.
Berechnungstabelle für lineare Interpolation
U_adc = ADC-Wert
U_adc/64 = ADC-Wert geteilt durch eine 2er-Potenz (64) und Feldindex
delta U_adc = Abstand zwischen n+1-ten und n-ten Wert (benötigt für Interpolation)
Seg_Output | U_adc | U_adc/64 | delta U_adc |
0 | 0 | 0 | 1 |
1 | 102,300 | 1 | 2 |
2 | 204,600 | 3 | 1 |
3 | 306,900 | 4 | 2 |
4 | 409,200 | 6 | 1 |
5 | 511,500 | 7 | 2 |
6 | 613,800 | 9 | 2 |
7 | 716,100 | 11 | 1 |
8 | 818,400 | 12 | 2 |
9 | 920,700 | 14 | 1 |
1023 | 15 | 0 |
Aus dieser Tabelle wird ein linear interpoliertes Feld erzeugt um den Wert für die Segmentanzeige zu berechnen. Der U_adc-Wert wurde durch die maximal größte 2er-Potenz geteilt um die Feldelemente, und somit den Speicherverbauch, niedrig zu halten. Wie man in der Tabelle sehen kann, gibt es immer für einen Segment-Wert genau einen ADC-Wert, aber leider mit verschiedenen Abständen. Zwischen U_adc=12 und U_adc=14 herrscht ein Abstand von 2 (siehe delta U_adc). U_adc=13 ist somit erstmal undefiniert, es muss also ein Zwischenwert erstellt werden. Also wird dem Wert von z.B. U_adc=13 die Segment-Zahl “8” zugewiesen (linear Interpoliert).
Somit sieht das Feld für die Segmentanzeige-Werte folgendermaßen aus:
0,1,1,2,3,3,4,5,5,6,6,7,8,8,9,9
Um nun in Abhängigkeit der Poti-Stellung den richtigen Wert aus dem Feld auszulesen, muss das Feldelement berechnet werden. U_adc/64 dient somit als Berechnung für das Feld (Feldindex)
Beispiel:
1) Eingelesener Wert ADC=850
2) ADC-Wert durch 64 teilen -> U_adc/64=13 (Index)
3) 13. Element aus Feld wählen -> 0,1,1,2,3,3,4,5,5,6,6,7,8,8,9,9 -> “8”
4) Ausgabe
Natürlich kann aus den ADC-Bereichen ein if-else-Konstrukt erstellt werden, bedeutet aber einiges mehr an Schreibarbeit und Speicherverbrauch.
Andere Möglichkeit: Geradengleichung aufstellen (später mehr).
Video
Schaltplan
ASM-Programm
FILE: main.asm
;***********************************************
; ___ _ _
; | |_ _ _| |___ ___ _| |___
; | | |_'_| . | -_| _|_| . | -_|
; |___|_,_|___|___|___|_|___|___|
;
; FILE: main.asm
; Author: declis (xdec.de)
;***********************************************
.cdecls "msp430g2231.h"
.ref init_ADC,get_ADC_result,time_wait,ADC_result_division64,output
.sect .const
seg_value: .byte 0,1,1,2,3,3,4,5,5,6,6,7,8,8,9,9
.global main
.text
main:
mov.w #0x280,SP ;initialize stack pointer
mov.w #WDTPW+WDTHOLD,&WDTCTL ;stop watchdog timer
bis.b #0x0F,&P1DIR ;P1.0,P1.1,P1.2,P1.3 (7seg output)
call #init_ADC ;initialize ADC
mov.w #seg_value,R10 ;start address of array in R14
loop:
mov.w #50,R4 ;wait 50ms
call #time_wait
call #get_ADC_result ;get ADC value
call #ADC_result_division64 ;normalize ADC value
add.w R14,R10 ;calculate address of value in array
mov.b @R10,R5 ;get 7seg-value for output
sub.w R14,R10 ;reset start address
call #output
jmp loop
.end
FILE: lib_func.asm
;*********************************************** ; ___ _ _ ; | |_ _ _| |___ ___ _| |___ ; | | |_'_| . | -_| _|_| . | -_| ; |___|_,_|___|___|___|_|___|___| ; ; FILE: lib_func.asm ; Author: declis (xdec.de) ;*********************************************** .cdecls "msp430g2231.h" .def init_ADC,get_ADC_result,time_wait,ADC_result_division64,output .text ;------------------------------------------ ; Name: init_ADC ; Description: initialize 10bit ADC ; ADC internal clock = 5MHz ; Input: nothing ; Returns: nothing ; Destroys: nothing ;------------------------------------------ init_ADC: mov.w #SREF_0+ADC10ON+ADC10SHT_3,&ADC10CTL0 ;Uref=Vcc=3.56V / ADC on / S&H=16xCLKs mov.w #ADC10DIV_3+ADC10SSEL_0+INCH_4,&ADC10CTL1 ;ADC clock divider=1, clk-src=ADC10OSC bis.b #BIT4,&ADC10AE0 ;P1.x configured as analag input ret ;------------------------------------------ ; Name: get_ADC_result ; Description: copy the conversion result ; from ADC10MEM into register ; Input: nothing ; Returns: ADC value in R14 ; Destroys: R14 ;------------------------------------------ get_ADC_result: bis.b #ENC+ADC10SC,&ADC10CTL0 ;sample and conversion start ;ENC must be toggled between each conversion ;ADC10SC is reset automatically mov.w &ADC10MEM,R14 ;copy 10bit conversion result in R14 ret ;------------------------------------------ ; Name: ADC_result_division64 ; Description: divide ADC result by 64 ; Input: nothing ; Returns: R14 ; Destroys: R14 ;------------------------------------------ ADC_result_division64: rra.w R14 rra.w R14 rra.w R14 rra.w R14 rra.w R14 rra.w R14 ret ;------------------------------------------ ; Name: output ; Description: output routine for 7-segment ; display ; Input: R4 ; Returns: nothing ; Destroys: R4 ;------------------------------------------ output: mov.w R5,R4 ;copy intern value to R4 mov.b R4,&P1OUT ;move R4 to Port1 ret ;------------------------------------------ ; Name: time_wait ; Description: wait "x" ms ; (the inner loop consume 1ms, ; outer loop is multiplication ; factor "x". ; (1ms*200=200ms for example) ; SMCLK=DC0=~1MHz ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; time in s = (5*195+24)*x/(1*10^6Hz) ; example: x=500 (500ms) ; => 0,4995s ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; Input: R4 - "x" ms ; Returns: nothing ; Destroys: nothing ;------------------------------------------ time_wait: push.w SR ;3 cycles push.w R4 ;3 cycles push.w R5 ;3 cycles outer_loop: mov.w #195,R5 ;2 cycles inner_loop: sub.w #1,R5 ;2 cycles nop ;1 cycle jnz inner_loop ;2 cycles sub.w #1,R4 ;2 cycles jnz outer_loop ;2 cycles pop.w R5 ;3 cycles pop.w R4 ;3 cycles pop.w SR ;3 cycles ret .end