前言
在学完第七讲《串口通信》之后,做练习题用到了前面所学的A/D转换器的知识,将模数转换器与串口通信结合起来实现题目功能。
难度:中等偏上。
一、A/D是什么?
首先A(anolog)是模拟量,D(digital)是数字量。对于TTL电平的取值范围来说,模拟量的取值范围在0~5V之间,是连续的,而对于数字量,则使用二进制数表示的。
二、A/D之间是如何转换的呢?
模拟量通常是在自然界中表示的电信号,可以在一定范围内连续变化。数字量则用1、0来表示电平的两个值,且只能表示两个值,通常应用于数字电路当中,例如在TTL标准中0代表低电平0V,1代表高电平+5V。
那么如何将连续的量转换成离散的量呢?
这就需要用到微分的思想将一段连续的量,切分成离散的数据。例如,0~5V如果用二位二进制数表示,则00代表0V,01代表1.67V,10代表3.34V,11代表5.01V。同理可以用8位的二进制数进行表示,二进制数加1,十进制电压则加5/255≈0.0196V。
三、A/D转换的原理
四、时序图
五、代码实现
1.引入库,初始化全局变量以及引脚
代码如下(示例):
#include<reg52.h>
#include<intrins.h>
#define AD_CH2 0xa4 //电位器端口,从此口可以获取电位器的阻值
typedef unsigned int uint;
typedef unsigned char uchar;
sbit CS=P3^7; //片选信号
sbit DCLK=P2^1; //时钟信号
sbit DIN=P2^0; //输入控制信号
sbit DOUT=P2^5; //输出数字量
sbit dula=P2^6; //段选信号
sbit wela=P2^7; //位选信号
//sbit beef=P2^3; //蜂鸣器
uchar temp1,temp2;
uint voltage; //电压值
uchar code du[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
uchar code we[] = {0xfe, 0xfd, 0xfb, 0xf7};
void delay(uchar z); //延时函数
void init(); //初始化函数
void display(uint); //位数计算函数
void show_number(uchar,uchar); //数码管显示函数
uint read_ad(uchar); //读取二进制电压值
2.延时函数(传入参数为1则延时1毫秒)
void delay(uchar z)
{
uchar x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--)
;
}
3.初始化函数
void init()
{
//将数码管全部初始化为0
dula=1;
P0=du[0];
dula=0;
P0=0xff;
wela=1;
P0=0x00;
wela=0;
//初始化串口中断
TMOD=0x20; //设置定时器1的中断模式为串口中断
TH1=0xe8; //1200bps对应的高八位为0xe8
TL1=0xe8;
SM0=0; //10位异步收发器,可收发八位数据
SM1=1;
REN=1; //允许串口接收数据
EA=1; //开启中断开关
ES=1; //开启终端开关
TR1=1; //启动定时器1
}
4.数码管显示函数,位数计算函数
void display(uint number)
{
uchar qian,bai,shi,ge;
qian=number/1000;
bai=number/100%10;
shi=number/10%10;
ge=number%10;
show_number(qian,0);
show_number(bai,1);
show_number(shi,2);
show_number(ge,3);
}
void show_number(uchar x,uchar y)
{
dula=1;
P0=du[x];
dula=0;
P0=0xff;
wela=1;
P0=we[y];
wela=0;
delay(5);
}
5.A/D转换函数(传入参数为)
uint read_ad(uchar channel)
{
uint date=0;
uchar i;
CS=0;
for(i=0;i<8;i++) //分别写8次,每次写1位
{
DCLK=0; //拉低时钟总线,允许DIN变化
if(channel&0x80) //先写数据的最高位
DIN=1; //写1
else
DIN=0; //写0
DCLK=1; //拉高时钟,让从机读取DIN
channel<<=1; //将次高位移动到最高位
}
DCLK=0; //拉低时钟总线
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
for(i=0;i<12;i++) //分别读12次,每次读1位
{
date<<=1; //数据左移1位,准备接收一位
DCLK=1; //拉高时钟总线,读取SDA上的数据
DCLK=0; //拉低时钟总线,允许从机控制SDA变化
if(DOUT)
date|=0x01; //为1则写1,否则不执行写1,通过下一次循环的左移补0
}
CS=1; //拉高片选,为下一次拉低片选做准备
temp2=date; //获取低八位
temp1=date>>8; //获取高八位
return date;
}
6、主函数
代码如下(示例):
void main()
{
init();
while(1)
{
voltage=read_ad(AD_CH2);
ES=0; //关闭定时器
SBUF=temp1; //获取高八位
while(!TI);
SBUF=temp2; //获取低八位
while(!TI);
ES=1; //开启中断定时器
voltage*=1.2207; // (5/4096)
display(voltage);
}
}
单片机不断的给计算机发送数字量电压
串口数据
当转动电位器时,数码管上显示电位器的电压
数码管显示