MSP430 Segmentanzeige über ein Potentiometer steuern (10bit ADC)

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

adc_poti

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

Leave a Reply

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload CAPTCHA.