min 发表于 2012-8-22 12:53:59

献给初学AD的朋友,精确到小数点后二位的电压采样程序

献给初学AD的朋友,精确到小数点后二位的电压采样程序

;**************************************************************************************
;**文件名: ADnew.asm
;**说明: 这是初学PIC写的一个AD采样的程序,基于1CD的PIC16F877A芯片,通道RA0;利用
;**定时器0进行动态扫描,主程序循环采样.这是一个完整的AD采样例子,精确到小
;**点后两位,学AD时发了好多精力,看了好多的例子,都没有搞懂,所以,现在想把这个例
;**给各位初学者看一下,在网上看到例子里如没有乘法,除法的,那就是没有真正的进行电压转换...
;**采样电压最高值*实际采样值/1024(10位AD),这才是关键所在....................
;**此程序可以在ICD上直接编译运行...不要作任何更改.
;**其实,不要浮点运算子程序也可以进行AD转换,这是我以前的误区,程序里也没有BCD转换..

;***************************************************************************************
#include "p16f877.inc"
disbufequ 20h ;显示缓冲区20-25,如果显示小数点后两位就用20,21,22
bakwequ 26h ;W备份
bakstaequ 27h ;STATUS备份
timecount equ 28h
ledtempequ 29h
vrevhequ 2Ah
vrevlequ 2Bh
v_1equ 2ch ;电压值第一位
v_2equ 2dh ;电压值小数点后一位
v_3equ 2eh ;电压值小数点后两位
SOUHequ 40h ;子程序入口高位
SOUequ 41h ;子程序入口低位
RLTHequ 42h ;子程序入口高位
RLTequ 43h ;子程序入口低位
CNTequ 44h ;子程序用寄存器
TEMP1equ 45h ;子程序用
TEMP2equ 46h ;同上
TEMP3equ 47h ;同上
TEMP4equ 48h ;同上

org 0000h
goto start
org 0004h
goto init_in
;*******宏定义,便于操作*********************************************
Bank0 macro   
bcf STATUS,RP0
bcf STATUS,RP1
endm
Bank1 macro
bsf STATUS,RP0
bcf STATUS,RP1
endm
Bank2 macro
bcf STATUS,RP0
bsf STATUS,RP1
endm
Bank3 macro
bsf STATUS,RP0
bsf STATUS,RP1
endm
;*****************************************
start:
Bank1
movlw B'00000001';AN0>>>>DC input DC通道上输入,注意,这里是打开RA0,但是在ICD上RA0 控制第二个LED.RA1
movwf TRISA                     ;对应第一个LED,这一点在显示结果时请自已区分
movlw B'00000000'
movwf TRISC
Bank0
;***** ***********************定时器初始化
movlw B'10100000'
movwf INTCON
Bank1
movlw B'11000111'
movwf OPTION_REG
Bank0
movlw 0x0F0
movwf TMR0
;***** ***************ADC初始化
;***** *****************
Bank1
movlw B'10001110'
movwf ADCON1
Bank0
movlw B'01000001'
movwf ADCON0
;*****************************
movlw 0x00
movwf disbuf
movlw 0x01
movwf disbuf+1
movlw 0x02
movwf disbuf+2
movlw 0x03
movwf disbuf+3
movlw 0x04
movwf disbuf+4
movlw 0x05
movwf disbuf+5 ;显示的内容,共六个缓冲区(disbuf,disbuf+1,disbuf+2...,disbuf+5),里面可以放0-9的数据,
ATOD:
; clrf disbuf+1 ;不要,会闪烁
; clrf disbuf+2
; clrf disbuf+3
nop
nop
nop
nop
nop
bsf ADCON0,GO ;开始采样
ADWAIT:
btfsc ADCON0,GO
goto ADWAIT;等待
movf ADRESH,w;放电压值高位
movwf vrevh
movf ADRESL,w;放电压低位
movwf vrevl;装值放入接收寄存器VERVH,VERVL,为节省时间
    ;采样值可以直接放入SOUH,SOU,但运算不方便
;*******测试用B'1100001111'**********************
movlw 0x03;这里可以手动往VREVH,VrevL两个寄存器输入10位AD值,以便用来测试是否能
                     ;在LED上显示正确的电压值,如:30F=B'1100001111'(10位采样AD值);
                     ;30F的实际值是3.823V,那么在LED上将显示3.82,寄存器21,22,23的值分别为3,8,2
movwf vrevh;程序正常采样时这四句话要屏蔽;
movlw 0x0f
movwf vrevl
;************************************************
movf vrevh,w
movwf SOUH ;将被乘数放入SOUH,SOU
movf vrevl,w
movwf SOU
movlw 0x00 ;乘数放入RLTH,RLT
movwf RLTH
movlw 0x05 ;
movwf RLT ;这里表示:30F*5,结果放入RLTH,RLT,SOUH,SOU;
call DUMUL;>>>>>>5*V_gather,result>>>RLTH,RLT SOUH,SOU

movlw 0x04 ;准备除1024(400),放数入RLTH,RLT!!!!关键所以,要理解为重.....以下三步都是这样的操作
movwf RLTH ;除法子程序用SOUH,SOU除以RLTH,RLT,因为上面的乘法程序不会超过两个字节
movlw 0x00 ;5V*3FF(10位满值)=13FB,所以在调用除法程序前不用考虑RLTH,RLT是否有其他值而被值   
movwf RLT ;0X0400冲掉
call DUDIV ;调用除法程序,商在SOUH,SOU,余数在RLTH,RLT,对于余数再*0A处理.然后再除 0x0400
movf SOU,w ;这样的话除两次就是小数点后两位精度
movwf disbuf+1;//V_1,这里得到电压整数值
movf RLTH,w
movwf SOUH ;送余数到SOUH,SOU,然后*0A,为小数点后一位的运算作准备
movf RLT,w
movwf SOU
clrf RLTH ;清除RLTH,RLT,因为里面要放乘数0X0a
clrf RLT
movlw 0x00
movwf RLTH
movlw 0x0A
movwf RLT
call DUMUL;>>>余数*10>>>RLTH,RLT SOUH,SOU,这里一般在souh,sou两个字节,为除法作准备

movlw 0x04 ;放除数0X0400
movwf RLTH
movlw 0x00
movwf RLT
call DUDIV ;原来的余数再除以0X400
movf SOU,w ;
movwf disbuf+2;//V_2取商到第二位电压值,这里是小数点的后一位
movf RLTH,w ;然后将余数放到SOUH,SOU,为下一次乘法作准备
movwf SOUH
movf RLT,w
movwf SOU
clrf RLTH
clrf RLT
movlw 0x00
movwf RLTH
movlw 0x0A ;SOUH,SOU,RLTH,RLT为乘法入口
movwf RLT
call DUMUL;>>>*10>>>RLTH,RLT SOUH,SOU,再乘以0A,出口在RLTH,RLT,SOUH,SOU
movlw 0x04
movwf RLTH
movlw 0x00
movwf RLT
call DUDIV ;再除以0X0400,除完这一次后就不要再除了,因为是保留小数点后两位
movf SOU,w
movwf disbuf+3;//V_3,取电压值,这里是小数点后两位值
;call Led_scan
call delay_same1
goto ATOD          ;循环采样

init_in:                   ;中断入口;定时器0
movwf bakw
swapf STATUS,W
movwf baksta
bcf INTCON,T0IF
movlw 0xF0
movwf TMR0
call Led_scan
swapf baksta,w
movwf STATUS
movf bakw,w
retfie
;*******led scan***********************************************
;LED扫描程序,对应于ICD,下面程序可以优化,请自已进行优化
Led_scan:
movlw ledtable
movwf ledtemp
movf disbuf+5,w
addwf ledtemp,w
call ledconvert
movwf PORTC
movlw B'11101111'
movwf PORTA
call delay_same

movlw ledtable
movwf ledtemp
movf disbuf+4,w
addwf ledtemp,w
call ledconvert
movwf PORTC
movlw B'11011111'
movwf PORTA
call delay_same

movlw ledtable
movwf ledtemp
movf disbuf+3,w
addwf ledtemp,w
call ledconvert
movwf PORTC
movlw B'11111011'
movwf PORTA
call delay_same
movlw ledtable
movwf ledtemp
movf disbuf+2,w
addwf ledtemp,w
call ledconvert
movwf PORTC
movlw B'11110111'
movwf PORTA
call delay_same

movlw ledtable
movwf ledtemp
movf disbuf+1,w
addwf ledtemp,w
call ledconvert
movwf ledtemp
bcf ledtemp,7
movf ledtemp,w
movwf PORTC
movlw B'11111110'
movwf PORTA
call delay_same

;movlw ledtable ;因为RA0要被AD占用,所以不要运行扫描
;movwf ledtemp
;movf disbuf,w
;addwf ledtemp,w
;call ledconvert
;movwf ledtemp
;bcf ledtemp,7
;movf ledtemp,w
;movwf PORTC
;movlw B'11111101'
;movwf PORTA
;call delay_same
movlw B'11111111' ;加此语句让RA1的亮度与其他LED平衡.
movwf PORTA
return
;*******end for led send***************************************
;;----------------数码管查表程序-------------------------------
ledconvert
movwf 2
ledtable
RETLW 0c0h;0
RETLW 0f9h;1
RETLW 0a4h;2
RETLW 0b0h;3
RETLW 099h;4
RETLW 092h;5
RETLW 082h;6
RETLW 0F8h;7
RETLW 080h;8
RETLW 090h;9
return
delay_same;延时
movlw 0F0h
movwf 70h
lop0 decfsz 70h,1
goto lop0
return
delay_same1
movlw 0F0h
movwf 71h
lop1 decfsz 71h,1
goto lop1
return

;********************************************************************************
;//是16*16进制,如果要十进制,则要进行BCD转换
;********************DUMUL test Date:0808,ok********************
;最大实现FFFF*FFFF=FFFE0001的算法 比如:0X08 0X43 * 0X00 0X10>>>0X84 0X30
;本程序实现双字节无符号数乘法。
;入口参数:被乘数在SOUH:SOU中,乘数在RLTH:RLT中。
;出口参数:结果在RLTH:RLT:SOUH:SOU中。
         IFNDEF       DUMUL1
            #DEFINE   DUMUL1               
DUMUL       MOVLW       .16
            MOVWF       CNT
            MOVF      SOU,W
            MOVWF       TEMP3
            MOVF      SOUH,W
            MOVWF       TEMP4
            CLRF      SOU         ;用于暂
            CLRF      SOUH      ;存
            CLRF      TEMP1       ;结
            CLRF      TEMP2       ;果
            BCF         STATUS,C
LOOP3       RRF         TEMP4,F
            RRF         TEMP3,F   ;将被乘数的某一位送到C中
            BTFSC       STATUS,C
            CALL      DUADD       ;将RLTH:RLT中的被乘数加上
            RRF         SOUH,F
            RRF         SOU,F
            RRF         TEMP2,F
            RRF         TEMP1,F   ;被乘数右移
            DECFSZ      CNT,F
            GOTO      LOOP3
            MOVF      SOUH,W      ;保存结果
            MOVWF       RLTH
            MOVF      SOU,W
            MOVWF       RLT
            MOVF      TEMP2,W      
            MOVWF       SOUH
            MOVF TEMP1,W
            MOVWF       SOU
            RETURN
            ;INCLUDE   "DUADD.ASM"
         ENDIF

;********************DUADD*
;本程序实现双字节无符号数加法。
;入口参数:被加数在SOUH:SOU中,加数在RLTH:RLT中。
;出口参数:结果在SOUH:SOU中,进位位在STATUS:C中。
;占用资源:W,024H,025H,026H,027H,一重堆栈。
         IFNDEF       DUADD1
            #DEFINE   DUADD1
DUADD       MOVF      RLT,W
            ADDWF       SOU,F
            MOVF      RLTH,W
            BTFSC       STATUS,C
            INCFSZ      RLTH,W
            ADDWF       SOUH,F
            RETURN
         ENDIF
;********************************************************************************
;********************************************************************************
;********************DUDIV*
;本程序实现双字节无符号数除法。
;入口参数:被除数在SOUH:SOU中,除数在RLTH:RLT中。
;出口参数:商在SOUH:SOU中,余数在RLTH:RLT中.
;占用资源:W,STATUS,023H,024H,025H,026H,027H,028H,029H,一重堆栈。
;说    明: 用户在调用该子程序之前必须确定除数不为零,否则得不到正确结果.
         IFNDEF       DUDIV1
            #DEFINE   DUDIV1
                        
DUDIV       MOVLW       .16         ;循环16次
            MOVWF       CNT
            CLRF      TEMP2
            CLRF      TEMP1       ;TEMP2:TEMP1得到余数
            BCF         STATUS,C
            RLF         SOU,F
            RLF         SOUH,F
            RLF         TEMP1,F
            RLF         TEMP2,F
LOOP79      MOVF      RLTH,W
            SUBWF       TEMP2,W   ;检测是否余数大于除数
            BTFSS       STATUS,Z
            GOTO      NOCHK
            MOVF      RLT,W
            SUBWF       TEMP1,W   ;如果高位相等则检测低位
NOCHK       BTFSS       STATUS,C   
            GOTO      NOGO
            MOVF      RLT,W       ;余数减除数
            SUBWF       TEMP1,F
            BTFSS       STATUS,C
            DECF      TEMP2,F
            MOVF      RLTH,W
            SUBWF       TEMP2,F
            BSF         STATUS,C    ;结果中移入1
NOGO      RLF         SOU,F
            RLF         SOUH,F
            RLF         TEMP1,F
            RLF         TEMP2,F
            DECFSZ      CNT,F      
            GOTO      LOOP79
            BCF         STATUS,C
            RRF         TEMP2,W
            MOVWF       RLTH
            RRF         TEMP1,W   ;恢复余数
            MOVWF       RLT
            RETLW       0
         ENDIF
;********************************************************************************
end

页: [1]
查看完整版本: 献给初学AD的朋友,精确到小数点后二位的电压采样程序