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