利用AVR单片机片内的ADC对模拟输入进行转换,并在LED数码管上显示结果。 PS:实验条件⑴ SL-AVRAD编程开发实验板 (2) AVR系列单片机ATMega16(L) (3) AVR单片机的C语言编译器WinAVR (4) AVR单片机下载软件SLISP对不起 没有说清楚吧 在WinAVR下用GCC编写单片机程序 用汇编太辛苦了 老师要求GCC 不过还是多谢了~~
#include "p16f877A.inc" errorlevel -302;***********************************__CONFIG _DEBUG_OFF&_CP_ALL&_WRT_HALF&_CPD_ON&_LVP_OFF&_BODEN_OFF&_PWRTE_ON&_WDT_OFF&_HS_OSC;;************************************disbuf equ 20h ;显示缓冲区20,21,22ledtemp equ 29hvrevh equ 2Ahvrevl equ 2BhSOUH equ 40h ;子程序入口高位SOU equ 41h ;子程序入口低位RLTH equ 42h ;子程序入口高位RLT equ 43h ;子程序入口低位CNT equ 44h ;子程序用寄存器TEMP1 equ 45h ;子程序用TEMP2 equ 46h ;同上TEMP3 equ 47h ;同上TEMP4 equ 48h ;同上;***************************************** org 0000h NOP goto start org 0005Hstart: banksel TRISA movlw B'00000001' ;AN0>>>>DC input DC通道上输入,注意,这里是打开RA0,但是在ICD上RA0 控制第二个LED.RA1 movwf TRISA ;对应第一个LED,这一点在显示结果时请自已区分 movlw B'00000000' movwf TRISC movlw B'10000111' ;预分频器给TMRO,且分频比为1:256 movwf OPTION_REG clrf STATUS movlw 0xa0 ;TMRO初值 movwf TMR0 ;***** ***************ADC初始化 ;***** *****************ATOD: banksel ADCON1 movlw B'10001110' ;转换结果右对齐,除RA0为模拟输入口外,其他RA口跟RE口均为普通数字口 movwf ADCON1 CLRF STATUS movlw B'01000001' ;转换时钟频率为内部时钟的1/8,AN0通道,允许ADC工作,暂时不开启AD转换 movwf ADCON0 ;***** ************************ movlw 0x00 movwf disbuf movwf disbuf+1 movwf disbuf+2 CLRF STATUS BTFSS INTCON,T0IF ;等待和循环检测TMR0溢出中断标志位 GOTO $-1 ;如果没有发生TMR0溢出中断则返回循环检测 BCF INTCON,T0IF ;保证足够的采样时间 movlw 0xa0 ;TMRO初值 movwf TMR0 bsf ADCON0,GO ;开始转换ADWAIT: btfsc ADCON0,GO goto ADWAIT ;等待转换完成 banksel ADRESH movf ADRESH,w ;读电压值高2位 CLRF STATUS movwf vrevh BANKSEL ADRESL movf ADRESL,w ;读电压低8位 CLRF STATUS 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 ;这里得到电压整数值 movf RLTH,w movwf SOUH ;送余数到SOUH,SOU,然后*0A,为小数点后一位的运算作准备 movf RLT,w movwf SOU 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+1 ;//取商到第二位电压值,这里是小数点的后一位 movf RLTH,w ;然后将余数放到SOUH,SOU,为下一次乘法作准备 movwf SOUH movf RLT,w movwf SOU 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+2 ;取电压值,这里是小数点后两位值 call Led_scan call delay_same1 goto ATOD ;循环转换;*********************led scan*************************;LED扫描程序,对应于ICD,下面程序可以优化,请自已进行优化Led_scan: movlw ledtable ;取得表头地址 movwf ledtemp movf disbuf+2,w ;取得偏移量 addwf ledtemp,w ;表头地址加上偏移量做为跳转地址 call ledconvert ;查表 movwf PORTC ;送数码管显示 movlw B'11101111' movwf PORTA ;点亮相应的数码管 call delay_same ;延时一段时间,保证显示足够亮度 movlw 0ffh movwf PORTC ;清除显示,防止干扰其他位显示 movlw ledtable movwf ledtemp movf disbuf+1,w addwf ledtemp,w call ledconvert movwf PORTC movlw B'11011111' movwf PORTA call delay_same movlw 0ffh movwf PORTC movlw ledtable movwf ledtemp movf disbuf,w addwf ledtemp,w call ledconvert andlw b'01111111' ;加上小数点 movwf PORTC movlw B'11111011' movwf PORTA call delay_same movlw 0ffh movwf PORTC movlw 0ffh ;关闭所有显示 movwf PORTA return;*******end for led send***************************************;;----------------数码管查表程序-------------------------------ledconvert movwf 2ledtable 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 returndelay_same ;延时 movlw 0F0h movwf 70hlop0 decfsz 70h,1 goto lop0 return delay_same1 movlw 0F0h movwf 71hlop1 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,CLOOP3 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 DUADD1DUADD 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,FLOOP79 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 ;结果中移入1NOGO 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
//实验: 四位数码管8通道循环显示AD转换电压//单片机:ATMEGA16L//作者: jia//日期: 2008//在硬件上已调试通过#include<iom16v.h>#define uchar unsigned char#define uint unsigned int//#define vref 2048 //转换值uchar num[4];//共阳数码管位码0~9//uchar Table[10]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};//uchar Data[4]={0xC0,0xC0,0xC0,0xC0};//AD转换程序uint ADC_Convert(void){uint temp1,temp2; temp1=(uint)ADCL; temp2=(uint)ADCH; temp2=(temp2<<8)+temp1; return(temp2);}//数据处理和显示void Process(uint i,uchar *p){uint temp;//i=i/204.8;//temp=(unsigned int)(((unsigned long)((unsigned long)i*vref))/1024);temp=i;p[0]=temp/1000; temp=temp%1000; p[1]=temp/100; temp=temp%100; p[2]=temp/10; temp=temp%10; p[3]=temp;}//i/o口初始化void port_init(void){ DDRA=0x00; //设置A口为不带上拉输入; PORTA=0x00; PORTB=0xff; DDRB=0xff; DDRC=0XFF; PORTC=0X00; DDRD=0xff; //设置C口为推挽1输出; PORTD=0xff; ADMUX=0x00; //选择第1通道ADC0;AVCC, AREF 引脚外加滤波电容电压 ADCSR=0xe6; //125K转换速率,自由转换模式;启动AD转换器;}void init_devices(void){ port_init(); //stop errant interrupts until set up// CLI(); //disable all interruptsMCUCR = 0x00; GICR = 0x00; TIMSK = 0x00; //timer interrupt sources //SEI(); //re-enable interrupts //all peripherals are now initialized}void Delay(int del){//unsigned char i,j;for(del;del>=0;del--); // { // for(j=10;j>0;j--); // ; //}}/************显示程序*************/void disp(void){uchar LEDValue[11]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0xff}; //0-9uchar i,k,senddat;//num[5]=10;num[4]=10;for(k=0;k<=5;k++)//发送6个字节 { senddat=LEDValue[num[5-k]]; for(i=0;i<=7;i++)//发送8位 { if(senddat&0x01!=0x00)//取最低位数据送给IO口 PORTB |= 0x01; else PORTB &= 0xfe; /*送一个脉冲发送一位数据 */ PORTB &= 0xfd;//PA2为零 Delay(3); PORTB |= 0x02; //PA2为1 Delay(3); senddat>>=1; } }}//主程序void main(void){uint i; init_devices(); Delay(30); //for(i=0;i<5;i++) //延时3s待系统稳定? num[5]=0; disp(); //Delay(30000); //Delay(30000); //Delay(30000); //Delay(30000); /* ADCSR=0xe6;i=ADC_Convert();ADMUX=7;*/while(1) { while(!(ADCSRA & (1 << ADIF))); /*等待*/ i=ADC_Convert(); ADCSRA &= ~(1 << ADIF); /*清标志*/ ADCSRA &= ~(1 << ADEN); /*关闭转换*/ Process(i,num); num[5]=ADMUX; disp(); Delay(30000); Delay(30000); Delay(30000); Delay(30000); Delay(30000); Delay(30000); Delay(30000); Delay(30000); Delay(30000); ADMUX++; //选择0~7道ADC0;AVCC, AREF 引脚外加滤波电容电压 ADMUX%=8; ADCSR=0xe6; /*启动转换*/ }}