利用10位AD转换器 及 LED 数码管,实现由AD转换器采集温度,并用数码管显示。温度超出一定范围,LED 指示灯闪烁报警。(主要分析进制16进制转换10进制,并用数码管显示问题)

**

题目:

**

利用原有的电路 AD 及 LED 数码管显示等例子,进行整合。实现由 AD转换器采集温度(用可调电阻模拟),温度在数码管上显示。当温度超出一定范围,用 LED 指示灯进行闪烁报警。(后边附有源码,这里主要讲解一下进制转换问题要使用进制转换的原因是这里采用的是10位AD转换器,即转换结果为10位,且结果为16进制数,例如全一情况:0x000003FF,如果直接将这个结果拿来当4个8位的数码管的输入的话,会发现,显示的也是16进制。所以需要进行进制转换,详细进制转换过程本文中间部分有介绍。

下边附上原电路图与修改后的电路图:

这是原有的含有AD转换电路的电路图 。

**

题目分析:

**

**这题主要是读取AD转换结果,然后将结果,用十进制并在数码管上显示出来。根据转换结果数值大小确定那种指示灯的亮灭,由于多个AD转换器不能同时使用,这里数码管显示内容仅为通过AD0,并进行一系列转换得来的。AD1实际上并未用到
**于是改进电路:
改进后的电路图。

**

进制转换分析 :

**

是这里采用的是10位AD转换器,即转换结果为10位,且结果为16进制数,例如全一情况:0x000003FF,需要先转换成10进制,在把十进制的每一位,送到16进制中去,(即4位2进制代表一位16进制数,这四位的范围就是0-F)我们要做的就是只让这四位只显示0-9的数,然后4位代表一位10进制数,送到数码管就可以了,这里是用的是4个8位的数码管。

举一个通俗易懂的例子:原AD转换结果为:0x000002A8,需要转换成10进制680,即让数码管显示0680,所以需要把0680分解开,主要分解680,为6和8和0 ,这里需要注意的是,6代表的是百位,8代表的是10位。所以,再将10进制数转换成4位显示一位10进制数的16进制,需要注意的是每一位的位置不一样。即转换成0x00000680。这时再送入数码管输入接口就可以了。)
代码如下(不是用的680,是采集的数据)

//只是部分代码,解说进制转换
//16进制每位数值数组
const uint32 a_[] = {0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF};
//10进制相对16进制每位的数值对照数组
const uint32 b_[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
//16进制每位的权重,因为AD转换结果只有二进制的10位,所以16进制只考虑到第三位即可,即到256
const uint32 c_[] = {0,16,256,4096,65536};

int i=0;
int j=0;
int k=0;
int l=0;





		AD0CR = (AD0CR&0x00FFFF00)|0x01|(1 << 24);	// 设置AD0.0,并进行第一次转换
        while( (AD0DR&0x80000000)==0 );	            // 等待转换结束
        AD0CR = AD0CR | (1 << 24);					// 再次启运转换
        while( (AD0DR&0x80000000)==0 );             // 等待转换结束       
		ADC_Data = AD0DR;							// 读取ADC结果       		
		ADC_Data = (ADC_Data>>6) & 0x3FF;           // 提取AD转换值 共10位       
		ADC_Data = ADC_Data * 3300/1024;            // 数值转换,转换成温度数值,但是16位的


		//16进制转10进制
		ADC_Data2= ADC_Data;//ADC_Data为经过AD转换后的16进制数据
		ADC_Data3= ADC_Data;//ADC_Data2和ADC_Data3都是用作中间变量		

		//循环次数,其实第四次就直接跳出循环了,因为AD转换结果只有10位
		for(i=0;i<4;i++){
			 j=i*4;
			 ADC_Data3 = ADC_Data2 &(0x0000000F << j);//0x0000000F依次左移4位然后和ADC_Data2求与,结果为除了要分离的4位,其余位都为零。
			 if(ADC_Data3 ==0)//判断是否分离完
			 	break;
			 else{
			 	//这里有移的目的是将0x00000F00,0x0000F000,0x000000F0等格式都转换成0x0000000F形式
			 	ADC_Data3=ADC_Data3>>j;	


				//得出16进制数组对应10进制数组的数组编号
				for(k=0;k<16;k++){
					if(ADC_Data3 == b_[k]){
						 l=k;
					}	   						
				}
			 }			
			 ADC_Data4= ADC_Data4 +b_[l]*c_[i] +b_[l];//得出的16进制对应的10进制数
		}		
		
		
		ADC_Data2 = 0x0;
		//10进制转换成用1位16进制数代表1个10进制数的16进制,例如:9->0x09
		for(i=0;i<4;i++)
		{	j=i*4;
			if(ADC_Data4 !=0)//直到10进制的每一位都转换结束
			{
				ADC_Data5=ADC_Data4%10;
				ADC_Data4=ADC_Data4/10;
				
				//依次累加,例如第一步0x000+0x005;第二步0x005+0x020;最后0x025+0x100;等
				ADC_Data2= ADC_Data2 + (ADC_Data5 << j);
			}
		}

需要注意的是,本题目用的是ARM的P1口,且为P1.16-P1.31,所以要将上述转换得到的结果进行左移16位,即变为0x06800000。并对P1口进行输入高电平操作,代码如下:

/*
		对P1接口输出清零,若相应位没能清零,可能会出现累加现象,
		例如使用IO1CLR =  0x03FF0000;,由于用到的P1为高16位,就会出现累加现象
		*/
		IO1CLR =  0xFFFF0000;
		IO1SET = (ADC_Data2<<16) & 0xFFFF0000 ;

详细源代码:

#include  "config.h"
//#define VREF  3300

/****************************************************************************
* 函数名称:DelayNS()
* 函数功能:长软件延时
* 入口参数:dly	延时参数,值越大,延时越久
****************************************************************************/
void  DelayNS(uint32  dly)
{  
    uint32  i;
    for(; dly>0; dly--) 
        for(i=0; i<5000; i++);
}

/****************************************************************************
* 函数名称:UART0Init()
* 函数功能:初始化串口0。设置为8位数据位,1位停止位,无奇偶校验
* 入口参数:bps 通讯波特率 
****************************************************************************/
void  UART0Init(uint32 bps)
{  
    uint16 Fdiv;
    PINSEL0 = (PINSEL0 & (~0x0F)) | 0x05;	// 不影响其它管脚连接,设置I/O连接到UART0
	IO0DIR = 0x0000003B;
	U0LCR = 0x83;							// DLAB = 1,可设置波特率
    Fdiv = (Fpclk / 16) / bps;				// 设置波特率
    U0DLM = Fdiv / 256;							
    U0DLL = Fdiv % 256;						
    U0LCR = 0x03;
}

//16进制每位数值数组
const uint32 a_[] = {0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF};
//10进制相对16进制每位的数值对照数组
const uint32 b_[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
//16进制每位的权重,因为AD转换结果只有二进制的10位,所以16进制只考虑到第三位即可,即到256
const uint32 c_[] = {0,16,256,4096,65536};

int i=0;
int j=0;
int k=0;
int l=0;
void display_yellow()
{
	
		IO0CLR = 0x38;
		DelayNS(30);
		IO0SET = 0x10;
		DelayNS(30);
}
void display_red()
{
	IO0CLR = 0x38;
	DelayNS(10);
	IO0SET = 0x20;
	DelayNS(10);
	

}
void display_green()
{	 

	IO0CLR = 0x38;
	//DelayNS(50);
	IO0SET = 0x08;
	//DelayNS(50);
	
}
/****************************************************************************
* 函数名称:main()
* 函数功能:进行通道0、1电压ADC转换,并把结果转换成电压值,然后发送到串口。
* 说    明:在CONFIG.H文件中包含stdio.h。
****************************************************************************/
int  main(void)
{  
    uint32  ADC_Data;
	
	//进制转换时的中间变量
	uint32  ADC_Data2;
	uint32  ADC_Data3;
	uint32  ADC_Data4;
	uint32  ADC_Data5;

    UART0Init(9600);							// 初始化UART0	
    PINSEL1 = 0x01400000;								// 设置P0.27、P0.28连接到AIN0、AIN1
	IO1DIR = 0xFFFFFFFF;						
    /* 进行ADC模块设置,其中x<<n表示第n位设置为x(若x超过一位,则向高位顺延) */
    AD0CR = (1 << 0)                     |		// SEL = 1 ,选择AD0.0
           ((Fpclk / 1000000 - 1) << 8) | 		// CLKDIV = Fpclk / 1000000 - 1 ,即转换时钟为1MHz
           (0 << 16)                    |		// BURST = 0 ,软件控制转换操作
           (0 << 17)                    | 		// CLKS = 0 ,使用11clock转换
           (1 << 21)                    | 		// PDN = 1 , 正常工作模式(非掉电转换模式)
           (0 << 22)                    | 		// TEST1:0 = 00 ,正常工作模式(非测试模式)
           (1 << 24)                    | 		// START = 1 ,直接启动ADC转换
           (0 << 27);							// EDGE = 0 (CAP/MAT引脚下降沿触发ADC转换)
    DelayNS(10);								
    ADC_Data = AD0DR;								// 读取ADC结果,并清除DONE标志位
   
    while(1)
    {  
	
        AD0CR = (AD0CR&0x00FFFF00)|0x01|(1 << 24);	// 设置AD0.0,并进行第一次转换
        while( (AD0DR&0x80000000)==0 );	            // 等待转换结束
        AD0CR = AD0CR | (1 << 24);					// 再次启运转换
        while( (AD0DR&0x80000000)==0 );             // 等待转换结束       
		ADC_Data = AD0DR;							// 读取ADC结果       		
		ADC_Data = (ADC_Data>>6) & 0x3FF;           // 提取AD转换值 共10位       
		ADC_Data = ADC_Data * 3300/1024;            // 数值转换

		
		//16进制转10进制
		ADC_Data2= ADC_Data;
		ADC_Data3= ADC_Data;
		//j=0;

		//循环次数,其实第四次就直接跳出循环了,因为AD转换结果只有10位
		for(i=0;i<4;i++){
			 j=i*4;
			 ADC_Data3 = ADC_Data2 &(0x0000000F << j);//依次左移4位
			 if(ADC_Data3 ==0)
			 	break;
			 else{
			 	//这里有移的目的是将0x0F00,0xF000,0x000F0等格式都转换成0x0000000F形式
			 	ADC_Data3=ADC_Data3>>j;	
				
				//得出16进制数组对应10进制数组的数组编号
				for(k=0;k<16;k++){
					if(ADC_Data3 == b_[k]){
						 l=k;
					}	   						
				}
			 }			
			 ADC_Data4= ADC_Data4 +b_[l]*c_[i] +b_[l];//得出的16进制对应的10进制数
		}		
		
		
		ADC_Data2 = 0x0;
		//10进制转换成用1位16进制数代表1个10进制数的16进制,例如:9->0x09
		for(i=0;i<4;i++)
		{	j=i*4;
			if(ADC_Data4 !=0)//直到10进制的每一位都转换结束
			{
				ADC_Data5=ADC_Data4%10;
				ADC_Data4=ADC_Data4/10;
				
				//依次累加,例如第一步0x000+0x005;第二步0x005+0x020;最后0x025+0x100;等
				ADC_Data2= ADC_Data2 + (ADC_Data5 << j);
			}
		}
		
		
		//对于不同数值温度,显示不同颜色灯,且闪烁速度不同,红底闪烁最快,黄灯稍微慢,绿灯不闪
		if(ADC_Data>1200)
		{
			if(ADC_Data<1800)
				display_yellow();
			else
				display_red();
		}
		else
			display_green();
		
		
		/*
		对P1接口输出清零,若相应位没能清零,可能会出现累加现象,
		例如使用IO1CLR =  0x03FF0000;,由于用到的P1为高16位,就会出现累加现象
		*/
		IO1CLR =  0xFFFF0000;
		IO1SET = (ADC_Data2<<16) & 0xFFFF0000 ;
       	
        printf("AD0.0=%d mV \r\n", ADC_Data);        // 数据串行口输出    
        
		//变量清零
		ADC_Data2 =0x00000000;
		ADC_Data2 =0x00000000;
		ADC_Data3 =0x00000000;
		ADC_Data4 =0x00000000;
		ADC_Data5 =0x00000000;
		
		//AD0.1
        /* 
        AD0CR = (AD0CR&0x00FFFF00)|0x02|(1 << 24);	// 设置AD0.1,并进行第一次转换
        while( (AD0DR&0x80000000)==0 );			    // 等待转换结束
        AD0CR = AD0CR | (1 << 24);					// 再次启运转换
        while( (AD0DR&0x80000000)==0 );             // 等待转换结束
        ADC_Data = AD0DR;							// 读取ADC结果
        ADC_Data = (ADC_Data>>6) & 0x3FF;           // 提取AD转换值
		
        ADC_Data = ADC_Data * 3300/1024;            // 数值转换
       
        printf("AD0.1=%d mV \r\r\n", ADC_Data);      // 数据通过串行口输出       
    	//DelayNS(100);	   */
		
    }

}

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的C语言程序实现以AT89C51单片机为核心,ADC0808为AD转换器,采用中断方式,对0~5V的模拟电压行循环采集采集的数据送LED数码显示,并存入内存,超过5V界限指示灯闪烁。 ``` #include<reg52.h> #include<intrins.h> #define uchar unsigned char #define uint unsigned int sbit CS=P1^4; //ADC芯片选择信号 sbit OE=P1^5; //ADC芯片输出使能信号 sbit CLK=P1^6; //ADC芯片时钟信号 sbit EOC=P1^7; //ADC芯片转换完成信号 uchar AD_Value; //存储ADC转换结果的变量 uchar LED_Buffer[8]; //存储LED数码显示的缓冲区 uchar Flag; //标志位,用来表示是否超过5V界限 void delay(uint n) //延时函数 { uint i,j; for(i=n;i>0;i--) for(j=110;j>0;j--); } void ADC_Read() interrupt 5 //中断服务程序 { uchar i; CS=0; //ADC芯片选择 OE=0; //使能输出 _nop_(); //空指令 _nop_(); _nop_(); CLK=1; //时钟信号上升沿 _nop_(); _nop_(); _nop_(); CLK=0; //时钟信号下降沿 AD_Value=P1; //读取ADC转换结果 CS=1; //ADC芯片取消选择 OE=1; //禁止输出 EOC=0; //清除转换完成标志 if(AD_Value>=0xC8) //超过5V界限 Flag=1; //设置标志位 else Flag=0; //清除标志位 for(i=0;i<8;i++) //将AD_Value转换成8位二数 { LED_Buffer[i]=AD_Value%2; AD_Value/=2; } } void main() { uchar i,j,k; TMOD=0x10; //定时器1工作在模式1,16位自动重装载计数器 TH1=(65535-50000)/256; //定时器初值 TL1=(65535-50000)%256; ET1=1; //开启定时器1中断 EA=1; //开启总中断 TR1=1; //启动定时器1 while(1) { for(i=0;i<8;i++) //循环显示AD_Value的二数 { P0=LED_Buffer[i]<<i; //点亮LED数码管 delay(2); //延时一段时间 P0=0; //熄灭LED数码管 delay(2); //延时一段时间 } if(Flag) //如果超过5V界限 { for(j=0;j<8;j++) //指示灯闪烁 { P2=0x00; //灭 delay(10); //延时 P2=0xFF; //亮 delay(10); //延时 } } for(k=0;k<8;k++) //将AD_Value存入内存 Memory[k]=LED_Buffer[k]; } } ``` 需要注意的是,此程序仅供参考,具体实现需要根据实际情况行调整。同时也需要注意单片机与ADC芯片的连接方式和电气特性,以确保程序正常运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值