求一个单片机程序

利用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,22
ledtemp equ 29h
vrevh equ 2Ah
vrevl equ 2Bh
SOUH 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 0005H
start:
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 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个回答  2008-04-21
//实验: 四位数码管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 interrupts

MCUCR = 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-9
uchar 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; /*启动转换*/
}
}本回答被提问者采纳
相似回答
大家正在搜