第十三课:扩展课!电压变成数字
单片机中运行的是0、1、0、1的数字信号,只能知道引脚是高电平还是低电平,怎么能测量电压呢?在15W408AS单片机中,集成了一个神奇的东西:模拟/数字转换器,简称AD转换,能够将引脚上的电压换算成数字。
有了它,你不仅能测电压,还能测电流,加个热敏电阻单片机就能感知到温度,加个光敏电阻单片机就能知道是白天还是黑夜……学会AD转换,你的单片机将大有可为。
本课的主要内容:学会单片机测量电压,做一个小电压表
本帖最后由 我爱萝丽爱萝丽 于 2016-12-2 04:18 编辑
1.1 模数转换原理
首先要知道的一点是:模数转换并不能直接告诉你电压是多少,只会告诉你一个分数。简单的模数转换,精度是255个等级,如果单片机电源电压是5V,相当于5V是满分255。如果你的引脚上电压是1V,你只能得51分。
当然,相反的,如果你得了51分,说明引脚上是1V。
AD转换的流程很简单,三步走:
1、设置相应的引脚为模拟输入功能 ;
2、启动一次转换;
3、等待转换完成,读取转换结果。
学习新的功能必然会学到新的寄存器。先说一个最简单的:
知识点(47) sfr ADC_RES=0xBD;声明 转换结果寄存器
转换出来的分数保存在这个寄存器里,范围0~255。
假如写程序 a=ADC_RES; a就获得转换结果了。
如果你已经熟练掌握了之前学的引脚模式寄存器设置,那么设置引脚的模拟功能也是非常轻松的了:
STC单片机的P1口引脚有模拟功能,本次实验用的电位器接在了P1.7引脚上。因此需先设置P1.7引脚为模拟输入:
知识点(48) sfr P1ASF=0x9D 声明 P1口模拟功能寄存器
设置P1.7引脚对应的程序是: P1ASF=0x80;
本节的难点在于最后这个寄存器,稍微复杂:
知识点(49) sfr ADC_CONTR=0xBC声明 模数转换寄存器
.
这个寄存器里面参数较多,不过我们只需要用该用到的就行了,别的暂时不用理会。
寄存器的后三位是引脚选择,000~111,0到7代表P1.0~P1.7引脚。
开始P1.7引脚AD转换,你可以直接设置为 1000 1111,也可以分开再相加 1000 1000+111
对应程序,可以写作 ADC_CONTR=0x8F; 也可以写作 ADC_CONTR=0x88+7;
知识点(50) AD转换例程
P1ASF=0x80;//设置P1.7引脚
ADC_CONTR=0x88+7;//开始P1.7引脚转换
Delay50us(); //等待AD转换完成
a=ADC_RES;//读取转换结果
单片机时钟为12M时,一次AD转换最慢也是45us,所以等待50us即可保证转换完成。
1.2 显示测量结果
上一课学了数码管的显示,加上这节课的AD转换,你就能测量出电位器上的电压啦。
不过每个数码管都只能独立的显示0~9,假如你有一个数据是123,你需要写个小程序把它拆开成百位、十位、个位才行。
用我们小学数学的知识来解答:
123/100=1余数23,百位显示1;23/10=2余数3,十位显示2,个位显示3。
单片机由于没有小数点,它的除法跟小学生的水平是差不多的,你可千万别嫌它笨哈哈:
知识点(51) 单片机的除法和余数
/:整除符号。单片机里面,29除以10结果不是2.9,真正的结果没有小数也没有四舍五入,是2;
%:求余数符号。29除10的余数自然是9。
求123的十位的例程:a=123%100/10; //先求100的余数是23,再除以10得2。
显示电位器ADC结果的程序,试着思考下怎么把AD结果换算成电压?
#include<reg51.h>
sfr P2M0=0x96; //声明 P2引脚模式寄存器
sfr P3M0=0xb2; //声明 P3引脚模式寄存器
sfr P1ASF=0x9D; //声明 P1口模拟功能寄存器
sfr ADC_RES=0xBD;//声明 ADC转换结果寄存器
sfr ADC_CONTR=0xBC;//声明 ADC控制寄存器
sbit DA=P2^4; //数码管每个引脚
sbit DB=P3^2;
sbit DC=P3^6;
sbit DD=P2^0;
sbit DE=P2^1;
sbit DF=P2^3;
sbit DG=P3^5;
sbit DP=P3^7;
sbit B1=P2^5; //四个数码管的引脚定义
sbit B2=P2^2;
sbit B3=P3^3;
sbit B4=P3^4;
unsigned char display_time; //定义变量用于显示次数
unsigned char Data1; //第一个数码管要显示的数据
unsigned char Data2; //第二个数码管要显示的数据
unsigned char Data3; //第三个数码管要显示的数据
unsigned char Data4; //第四个数码管要显示的数据
unsigned char adc; //保存AD转换结果
void Delay1ms() //@12.000MHz
{
unsigned char i, j;
i = 12;
j = 169;
do
{
while (--j);
} while (--i);
}
void delay_n_ms(unsigned int n) //延时n毫秒函数
{
while(n)
{
Delay1ms();
n=n-1;//每循环一次n减小1
}
}
void Delay50us() //@12.000MHz
{
unsigned char i, j;
i = 1;
j = 146;
do
{
while (--j);
} while (--i);
}
void display(unsigned char x)//控制数码管显示内容的函数
{
//判断x的值来决定显示什么
if(x==0){DA=1;DB=1;DC=1;DD=1;DE=1;DF=1;DG=0;DP=0;} //显示“0”
if(x==1){DA=0;DB=1;DC=1;DD=0;DE=0;DF=0;DG=0;DP=0;} //显示“1”
if(x==2){DA=1;DB=1;DC=0;DD=1;DE=1;DF=0;DG=1;DP=0;} //显示“2”
if(x==3){DA=1;DB=1;DC=1;DD=1;DE=0;DF=0;DG=1;DP=0;} //显示“3”
if(x==4){DA=0;DB=1;DC=1;DD=0;DE=0;DF=1;DG=1;DP=0;} //显示“4”
if(x==5){DA=1;DB=0;DC=1;DD=1;DE=0;DF=1;DG=1;DP=0;} //显示“5”
if(x==6){DA=1;DB=0;DC=1;DD=1;DE=1;DF=1;DG=1;DP=0;} //显示“6”
if(x==7){DA=1;DB=1;DC=1;DD=0;DE=0;DF=0;DG=0;DP=0;} //显示“7”
if(x==8){DA=1;DB=1;DC=1;DD=1;DE=1;DF=1;DG=1;DP=0;} //显示“8”
if(x==9){DA=1;DB=1;DC=1;DD=1;DE=0;DF=1;DG=1;DP=0;} //显示“9”
}
main()
{
P2M0=0x1B; //需要高电平大电流的引脚 2.4、2.3、2.1、2.0,填入数值0001 1011
P3M0=0xE4; //需要高电平大电流的引脚 3.7、3.6、3.5、3.2,填入数值1110 0100
while(1)
{
delay_n_ms(4);//系统周期4ms
P1ASF=0x80;//设置P1.7引脚
ADC_CONTR=0x88+7;//开始P1.7引脚转换
Delay50us(); //等待AD转换完成
adc=ADC_RES;//读取转换结果
Data1=0;
Data2=adc/100; //百位数据
Data3=adc%100/10; //十位数据
Data4=adc%10; //个位数据
///////////////////////////////////////////////////////
//显示程序模块化,只需要改变Data1/2/3/4的值就可以控制显示内容
display_time=display_time+1;
if(display_time>3)
{
display_time=0;//0、1、2、3 四个循环
}
if(display_time==0)//第0次显示第一个数码管
{
B1=0;B2=1;B3=1;B4=1;
display(Data1); //显示第1位数据
}
if(display_time==1)//第1次显示第二个数码管
{
B1=1;B2=0;B3=1;B4=1;
display(Data2); //显示第2位数据
}
if(display_time==2)//第2次显示第三个数码管
{
B1=1;B2=1;B3=0;B4=1;
display(Data3); //显示第3位数据
}
if(display_time==3)//第3次显示第四个数码管
{
B1=1;B2=1;B3=1;B4=0;
display(Data4); //显示第4位数据
}
}
}
本帖最后由 我爱萝丽爱萝丽 于 2016-12-27 23:58 编辑
1.3 项目规划
上次说到如何把ADC转换结果转换为电压,这个算法应该中学生都会:(前提是单片机5V要准)
电压x adc
————= ———
5V 255
所以电压x=adc/255*5=adc/51。假设adc=128,x=2.5。
不过要注意,由于单片机没有小数点,你要是想精确到小数点后,需要把adc扩大10倍,x=25,显示时再补上小数点。
思考题:a=29*10/10; 与 b=29/10*10; 有什么区别?
表面上好像一样,实际上大不相同!
a=29*10/10 =290/10 =29;
b=29/10*10 =2*10 =20;
由于单片机的除法会损失精度,所以必须先乘后除。
学了新知识,就要学以致用。本期的项目就是要做一个实用的小电压表。
根据已学到的知识,你起码可以完成的最低要求:
单片机使用稳压5V或3.3V供电,测量一路0~5V或0~3.3V电压。
中等要求:
单片机稳压供电,用10k、10k电阻分压,量程增加2倍。
增加数码管显示小数点功能。
扩展要求:
多路不同量程电压测量,自动切换显示或按键切换……
使用10位ADC或多次测量求平均值等方法提高精度……
一个实现中等要求的小程序:#include<reg51.h>
sfr P2M0=0x96; //声明 P2引脚模式寄存器
sfr P3M0=0xb2; //声明 P3引脚模式寄存器
sfr P1ASF=0x9D; //声明 P1口模拟功能寄存器
sfr ADC_RES=0xBD;//声明 ADC转换结果寄存器
sfr ADC_CONTR=0xBC;//声明 ADC控制寄存器
sbit DA=P2^4; //数码管每个引脚
sbit DB=P3^2;
sbit DC=P3^6;
sbit DD=P2^0;
sbit DE=P2^1;
sbit DF=P2^3;
sbit DG=P3^5;
sbit DP=P3^7;
sbit B1=P2^5; //四个数码管的引脚定义
sbit B2=P2^2;
sbit B3=P3^3;
sbit B4=P3^4;
unsigned char display_time; //定义变量用于显示次数
unsigned char Data1; //第一个数码管要显示的数据
unsigned char Data2; //第二个数码管要显示的数据
unsigned char Data3; //第三个数码管要显示的数据
unsigned char Data4; //第四个数码管要显示的数据
unsigned char count; //计时
unsigned int adc; //AD转换计算
void Delay1ms() //@12.000MHz
{
unsigned char i, j;
i = 12;
j = 169;
do
{
while (--j);
} while (--i);
}
void delay_n_ms(unsigned int n) //延时n毫秒函数
{
while(n)
{
Delay1ms();
n=n-1;//每循环一次n减小1
}
}
void Delay50us() //@12.000MHz
{
unsigned char i, j;
i = 1;
j = 146;
do
{
while (--j);
} while (--i);
}
void display(unsigned char x)//控制数码管显示内容的函数
{
//判断x的值来决定显示什么
if(x==0){DA=1;DB=1;DC=1;DD=1;DE=1;DF=1;DG=0;DP=0;} //显示“0”
if(x==1){DA=0;DB=1;DC=1;DD=0;DE=0;DF=0;DG=0;DP=0;} //显示“1”
if(x==2){DA=1;DB=1;DC=0;DD=1;DE=1;DF=0;DG=1;DP=0;} //显示“2”
if(x==3){DA=1;DB=1;DC=1;DD=1;DE=0;DF=0;DG=1;DP=0;} //显示“3”
if(x==4){DA=0;DB=1;DC=1;DD=0;DE=0;DF=1;DG=1;DP=0;} //显示“4”
if(x==5){DA=1;DB=0;DC=1;DD=1;DE=0;DF=1;DG=1;DP=0;} //显示“5”
if(x==6){DA=1;DB=0;DC=1;DD=1;DE=1;DF=1;DG=1;DP=0;} //显示“6”
if(x==7){DA=1;DB=1;DC=1;DD=0;DE=0;DF=0;DG=0;DP=0;} //显示“7”
if(x==8){DA=1;DB=1;DC=1;DD=1;DE=1;DF=1;DG=1;DP=0;} //显示“8”
if(x==9){DA=1;DB=1;DC=1;DD=1;DE=0;DF=1;DG=1;DP=0;} //显示“9”
if(x==10){DA=1;DB=1;DC=1;DD=1;DE=1;DF=1;DG=0;DP=1;} //显示“0”带小数点
if(x==11){DA=0;DB=1;DC=1;DD=0;DE=0;DF=0;DG=0;DP=1;} //显示“1”带小数点
if(x==12){DA=1;DB=1;DC=0;DD=1;DE=1;DF=0;DG=1;DP=1;} //显示“2”带小数点
if(x==13){DA=1;DB=1;DC=1;DD=1;DE=0;DF=0;DG=1;DP=1;} //显示“3”带小数点
if(x==14){DA=0;DB=1;DC=1;DD=0;DE=0;DF=1;DG=1;DP=1;} //显示“4”带小数点
if(x==15){DA=1;DB=0;DC=1;DD=1;DE=0;DF=1;DG=1;DP=1;} //显示“5”带小数点
if(x==16){DA=1;DB=0;DC=1;DD=1;DE=1;DF=1;DG=1;DP=1;} //显示“6”带小数点
if(x==17){DA=1;DB=1;DC=1;DD=0;DE=0;DF=0;DG=0;DP=1;} //显示“7”带小数点
if(x==18){DA=1;DB=1;DC=1;DD=1;DE=1;DF=1;DG=1;DP=1;} //显示“8”带小数点
if(x==19){DA=1;DB=1;DC=1;DD=1;DE=0;DF=1;DG=1;DP=1;} //显示“9”带小数点
}
main()
{
P2M0=0x1B; //需要高电平大电流的引脚 2.4、2.3、2.1、2.0,填入数值0001 1011
P3M0=0xE4; //需要高电平大电流的引脚 3.7、3.6、3.5、3.2,填入数值1110 0100
P1ASF=0x80;//设置P1.7引脚为模拟功能
while(1)
{
delay_n_ms(4);//系统周期4ms
count=count+1;
if(count>125)//0.5秒测量一次电压
{
count=0;
ADC_CONTR=0x88+7;//开始P1.7引脚转换
Delay50us(); //等待AD转换完成
adc=ADC_RES;//读取转换结果
adc=adc*2; //量程增大倍数
adc=adc*10/51;//计算电压*10倍,消除小数点
Data1=0;
Data2=adc/100; //百位数据
Data3=adc%100/10; //十位数据
Data3=Data3+10; //加10,display函数显示的数字会带小数点
Data4=adc%10; //个位数据
}
///////////////////////////////////////////////////////
//显示程序模块化,只需要改变Data1/2/3/4的值就可以控制显示内容
display_time=display_time+1;
if(display_time>3)
{
display_time=0;//0、1、2、3 四个循环
}
if(display_time==0)//第0次显示第一个数码管
{
B1=0;B2=1;B3=1;B4=1;
display(Data1); //显示第1位数据
}
if(display_time==1)//第1次显示第二个数码管
{
B1=1;B2=0;B3=1;B4=1;
display(Data2); //显示第2位数据
}
if(display_time==2)//第2次显示第三个数码管
{
B1=1;B2=1;B3=0;B4=1;
display(Data3); //显示第3位数据
}
if(display_time==3)//第3次显示第四个数码管
{
B1=1;B2=1;B3=1;B4=0;
display(Data4); //显示第4位数据
}
}
}
长寿烙铁 发表于 2016-12-3 20:57
你好,我想自制一个简易微型万用表,具有常用的简单功能,可以挂在钥匙上,偶尔用一下。
我的单片机是20脚 ...
这么复杂,哪里还叫简易……测个电压电流还算简单,电阻二极管就不推荐搞了 好帖好帖好帖子顶顶顶顶顶顶顶顶
期待详细内容,继续加油 厉害了,买了所有配件,来学习,嘿嘿{:1_12:} 学习中学习中学习中 萝丽大法好
没有原理图。。。。。。。。。。。。。。。 看看学习嘻嘻 本帖最后由 coolbgo 于 2018-7-17 10:08 编辑
增加了温度探测器功能某宝购买MAX6675 K型热电偶模块 说是可以测1000度用打火机烧到230多度没继续试 分享一下我的代码,前面的看不到注释有兴趣的可以下载看看 包含c hex接线脚位和萝莉大神的都一样
另外请问大神,怎么样可以把数码管脚位定义到一个变量里,想了好久没搞明白 顶顶顶,发发发 顶顶顶顶顶
路过,6666666
学习一下
帮顶……
7楼了,走起!
期待期待,这期学会就能做红外线车模控了。
期待AD转换,做一个小电压表~谢谢! 6666666666666666666666666666 高级灰
好东西啊
大师 你课程很容易懂谢谢 ,, 别的教程简直是天书啊 我看了只想睡觉啊 ,好教程。顶
关键字:数模转换顶萝丽大神 学习了,。好帖。。
顶一顶
顶,顶,顶
顶起 期待详细内容,继续加油