S3F9454/94C4/94C8通用ADC检测电压转换显示例程
文章来源: 本站原创   更新时间: 2014-06-23
;--------------------------------------
;程序中所有分号后面的都是注释语句
;#号表示一个常量名字或数据
;-----------------------------------------------------
.include "s3c9454.reg" ;必须加入寄存器定义文件,这个文件是OPENICE自带的
.include "\myfile\sam\mysam.inc"
;-----------------------------------------------------
;以下为程序运行所用变量定义
DIGIT1 EQU 00H ;LED-A位数据定义
DIGIT2 EQU 01H ;LED-B位数据定义
DIGIT3 EQU 02H ;LED-C位数据定义
DIGIT4 EQU 03H ;LED-D位数据定义
SEG1 EQU 04H ;SEG-A位显示码
SEG2 EQU 05H ;SEG-B位显示码
SEG3 EQU 06H ;SEG-C位显示码
SEG4 EQU 07H ;SEG-D位显示码

TMRFLAG EQU 08H ;定时标志寄存器
LED_LOOP EQU 09H ;显示循环计数器
TMR_05S EQU 0AH ;0.5S计时器

PV_H EQU 0BH ;电压值高位
PV_L EQU 0CH ;电压值低位
ADC_H EQU 0DH ;ADC数据高位
ADC_L EQU 0EH ;ADC数据低位
;--------------------------------------------
;位操作定义
CK_164 BIT P0.0 ;P0.0是164时钟驱动线
DT_164 BIT P0.1 ;P0.1是164数据驱动线

SMG_D1 BIT P0.5 ;P0.5是数码管驱动位线1
SMG_D2 BIT P0.4 ;P0.4是数码管驱动位线2
SMG_D3 BIT P0.3 ;P0.3是数码管驱动位线3
SMG_D4 BIT P0.2 ;P0.2是数码管驱动位线4

PV_IN BIT P2.6 ;输入电压接在P26

FLAG_05S BIT TMRFLAG.0 ;0.5S定时标志

ADC_PV .EQU 10000001B ;PV ADC转换启动设定值

;--------------------------------------------------------
         ;9454只有一个中断向量地址,在0000H,这里设置中断服务程序INT_9454
        ORG 0000H ;ORG表示定义一个绝对的ROM地址,0000H地址
        .VECTOR $,INT_9454 ;一个向量地址占用2个字节,$表示是在当前地址处定义,也可以采用直接的绝地址定义,
                                ;如:.VECTOR 0000H,INT_9454,两者效果相同
;------------------------------------------------------------
;显示编码予定义
DIGIT_CODE: ;表格开始地址是0000H
       DB 03fh ;0
       DB 006h ;1
       DB 05bh ;2
       DB 04fh ;3
       DB 066h ;4
       DB 06dh ;5
       DB 07dh ;6
       DB 007h ;7
       DB 07fh ;8
       DB 06fh ;9
       DB 077h ;A
       DB 07Ch ;B
       DB 039h ;C
       DB 05Eh ;D
       DB 079h ;E
       DB 071h ;F
       DB 040H ;-号
       DB 0FFH ;全显示
       DB 00H ;不显示(黑屏)
       
_CODE .EQU 10H
LED_OFF .EQU 12H
LED_ON .EQU 11H
;-----------------------------------------------------------
;SMART OPTION
;有关这几个数据设置,请详细参阅9454手册的ADDRESS SPACE部分的SMART OPTION说明
;SMART OPTION定义4个字节数据在003CH--003FH这4个ROM地址
        ORG 003CH
        DB 00H
        DB 00H
        DB 0E7H ;设置内部LVR使能,用内部复位,LVR电平为2.3V
        DB 03H ;00H定义外部晶振,03H定义内部OSC
;-----------------------------------------------------------
;-------<>
        ORG 0100H

RESET: ;-------------------------
        ;follow must set
        DI ;disable interrupt
        LD BTCON,#10100011B ;Watch-dog disable
        LD CLKCON,#00011000B ;selet non-divided CPU clock
        LD SP,#0C0H ;Stack pointer must be set as 0C0H

        LD P0CONH,#00001010B
        LD P0CONL,#10101010B ;设置P00--P05作为输出
        
        LD P2CONH,#00110000B ;设置P26作为ADC输入
      
        LD T0CON,#01001010B ;TMR0输入时钟=FXX/8, 开启T0中断
        LD T0DATA,#50 ;若OSC=3.2MHZ,则T0每2MS产生一次中断,TIME= (3.2MHZ/256)*50= 4MS

        LD LED_LOOP,#00H
        EI
;----------------------------------------
MAIN:
        ;每0.5S检测一次温度
        TMBIT FLAG_05S
        JR Z,MAIN
        CLRBIT FLAG_05S
         
        CALL GET_PV ;检测得到输入电压值
        
        CALL DISPLAY_CONVERT ;转换温度数据到ABCD四位显示数据
        
        CALL DIGIT_CONVERT ;转换显示数据到显示码
        JR MAIN
 
;--------------------------------------------------------
;该进程检测ADC端口NTC热敏电阻状态并查表得到温度值
GET_PV:
        LD R0,#ADC_PV
        CALL TEST_ADC
        
        ;LD ADC_H,#00H
        ;LD ADC_L,#0F0H
        ;转换得到的AD数据在ADC_H:ADC_L中存放
        ;再通过乘法计算得到实际的电压值
        ;ADC分辨率为1023,则每份电压为0.0049V,则设乘数为49
        LD R3,ADC_H
        LD R4,ADC_L
        LD R9,#49
        CALL UMUL1608 ;16位ADC数据乘以分辨率49
        ;R13:R14:R15是返回的乘积值,由于最大乘积不会超过16位,则只取R14:R15即可
        ;将乘积再除以100,得到3位的电压值
        LD R3,R14
        LD R4,R15
        LD R9,#100
        CALL UDIV1608
        ;返回的商在R14:R15中存放
        LD PV_H,R14
        LD PV_L,R15
        RET
;--------------------------------------------------------
;该模块把电压值转换为ABCD四位显示数据
DISPLAY_CONVERT:
              
        ;得到整数位值(百位)
        LD R0,PV_H
        LD R1,PV_L
        LD R2,#00H
CVT_100:
        SUB R1,#100
        SBC R0,#0
        JR C,CVT_DATA
        INC R2
        JR CVT_100
CVT_DATA:
        LD DIGIT2,R2
        ADD R1,#100 ;由于前面对R1进行了予减100,则这里必须先行加上100
        CALL CONVERT_DATA
        LD DIGIT3,R0
        LD DIGIT4,R1
        LD DIGIT1,#LED_OFF
        RET
;--------------------------------------------------------
;该进程把一个两位数据转换为十位和个位数据
;R1--要转换的两位数据,转换后为个位数据
;R0--转换后的十位数据
CONVERT_DATA:
        CP R1,#99
        JR ULE,CVT_ST
        LD R1,#99 ;最大限99
CVT_ST:
        LD R0,#00H
CVT_LOOP:
        CP R1,#10
        JR ULT,CVT_BACK
        SUB R1,#10
        INC R0
        JR CVT_LOOP
CVT_BACK:
        RET
;---------------------------------------------------------
;该进程把要数码管各位要显示的数据转换为显示码
DIGIT_CONVERT:
        LD R10,#00H ;调入显示码定义高位地址
        LD R11,#DIGIT_CODE ;调入显示码定义起始地址
        
        ;调入A位显示码
        LD R11,#DIGIT_CODE ;调入显示码定义起始地址
        ADD R11,DIGIT1 ;LED-A位数据定义
        LDC R0,@RR10 ;把RR10寄存器对中的地址所指向的ROM地址字节中的数据调入到R0
        LD SEG1,R0 ;把R0寄存器中的地址所指向的ROM地址字节中的数据调入到
                                        ;SEG-A位显示码
        
        ;调入B位显示码
        LD R11,#DIGIT_CODE ;调入显示码定义起始地址
        ADD R11,DIGIT2
        LDC R0,@RR10
        OR R0,#80H ;点亮中间点指示
        LD SEG2,R0
        
        ;调入C位显示码
        LD R11,#DIGIT_CODE ;调入显示码定义起始地址
        ADD R11,DIGIT3
        LDC R0,@RR10
        LD SEG3,R0
        
        ;调入D位显示码
        LD R11,#DIGIT_CODE ;调入显示码定义起始地址
        ADD R11,DIGIT4
        LDC R0,@RR10
        LD SEG4,R0
     
        RET
;--------------------------------------------------------
;该进程根据输入的ADC设置数据进行AD检测得到ADH,ADL数据
;连续测64次AD输入求平均值
;R0---输入的ADCON设置数据
;返回:ADC_H:ADC_L是平均值的高位和低位
TEST_ADC:
        PUSH R15 ;首先将用到的暂存器入栈
        PUSH R14
        PUSH R13
        PUSH R12
        PUSH R11 ;但如果主程序中未用到这些工作寄存器,也可以不用管这些
        PUSH R10
        PUSH R9
        
        
        LD R9,#64 ;16次计数
        CLR R10
        CLR R11
        CLR R12
        ;R10:R11:R12是用来存储16次检测值加法和
        ;R13:R14:R15用来暂存ADDATAH:ADDATAL的移位转换
ADC_LOOP:
        LD ADCON,R0
ADC_WAIT:
        TM ADCON,#08H ;ADCON.3是AD转换完成位标志
        JR Z,ADC_WAIT
        ;转换完成
        LD R15,ADDATAL
        LD R14,ADDATAH
        LD R13,#00H
           
        ;将AD数据加到加法和
        RCF
        RLC R14 ;通过左移2位方式将ADDATAH的高2位移入到R12
        RLC R13 ;并腾出低2位以便加入ADDATAL的低2位
        RLC R14
        RLC R13
        AND R14,#11111100B ;低2位必须先行清零,以消除C标志移入的影响
        ADD R14,R15 ;加上低2位值
        ADC R13,#00H
        
        ADD R12,R14
        ADC R11,R13
        ADC R10,#00H
        
        DEC R9
        JR NZ,ADC_LOOP
        ;64次检测完毕,求平均值
        ;采用简单移位除法,将整个32位加法和右移4位即可得到
        ;除2
        RRC R10
        RRC R11
        RRC R12
        ;除4
        RRC R10
        RRC R11
        RRC R12
        ;除8
        RRC R10
        RRC R11
        RRC R12
        ;除16
        RRC R10
        RRC R11
        RRC R12
        ;除32
        RRC R10
        RRC R11
        RRC R12
        ;除64
        RRC R10
        RRC R11
        RRC R12
        
        ;低位R11是64次平均值
        LD ADC_H,R11
        LD ADC_L,R12
        ;如果只处理8位AD数据,则需要忽略低2位,则将数据右移2位即可
        ;RRC ADC_H
        ;RRC ADC_L
        ;RRC ADC_H
        ;RRC ADC_L
        ;ADTL是实际得到的AD测试平均值数据
        
        POP R9
        POP R10 ;将暂存器出栈,注意先进先出原则
        POP R11
        POP R12
        POP R13
        POP R14
        POP R15
        RET

        RET
;---------------------------------------------------------
;R0---要发送到164输出的数据
SENDTO164:
        PUSH R1
        CLRBIT CK_164
        CLRBIT DT_164
        LD R1,#08H
;----------
LOOP_164:
        ;必须先根据数据状态予置好DATA线状态
        CLRBIT DT_164
        RRC R0
        JR C,SET_1
SET_0:
        CLRBIT DT_164
        JR SEND_CLK
SET_1:
        SETBIT DT_164
;---------
;发送一个时钟信号
SEND_CLK:
        SETBIT CK_164
        NOP
        NOP
        NOP
        CLRBIT CK_164
        
        DEC R1
        JR NZ,LOOP_164
;---------
SEND_BACK:
        POP R1
        RET
;---------------------------------------------------------
;该进程通过轮询方式来分时扫描数码管的A,B,C,D各位显示
LED_SCAN:
        INC LED_LOOP
        ;注意在每次送数据到LED显示前,必须先关闭全部位线
        CLRBIT SMG_D1
        CLRBIT SMG_D2
        CLRBIT SMG_D3
        CLRBIT SMG_D4
         
        CP LED_LOOP,#01H
        JR EQ,SCAN_1
        CP LED_LOOP,#02H
        JR EQ,SCAN_2
        CP LED_LOOP,#03H
        JR EQ,SCAN_3
        CP LED_LOOP,#04H
        JR EQ,SCAN_4
        
        LD LED_LOOP,#00H
SCAN_1:
        LD R0,SEG1 ;调入A位显示码
        CALL SENDTO164
        SETBIT SMG_D1 ;打开1位线显示
        JR SCAN_BACK
SCAN_2:
        LD R0,SEG2 ;调入B位显示码
        CALL SENDTO164
        SETBIT SMG_D2 ;打开2位线显示
        JR SCAN_BACK
SCAN_3:
        LD R0,SEG3 ;调入C位显示码
        CALL SENDTO164
        SETBIT SMG_D3 ;打开3位线显示
        JR SCAN_BACK
SCAN_4:
        LD R0,SEG4 ;调入D位显示码
        CALL SENDTO164
        SETBIT SMG_D4 ;打开4位线显示
SCAN_BACK:
        RET
;---------------------------------------------------------
;TMR0-2MS中断一次
INT_9454:
        AND T0CON,#0FEH ;清T0中断标志位
        OR T0CON,#08H ;清T0CNT,这个是芯片手册规定的
        
        CALL LED_SCAN ;扫描LED显示
        
        INC TMR_05S
        CP TMR_05S,#50
        JR ULT,INT_BACK
        ;定时1S时间到
        SETBIT FLAG_05S
        CLR TMR_05S
INT_BACK:
        IRET
;---------------------------------------------------------------
UMUL1608:
        UMUL1608_MACRO
        RET
UDIV1608:
        UDIV1608_MACRO
        RET
;---------------------------------------------------

.END ;Programer is end

相关备注:
 
三星单片机开发网首页