我爱萝丽爱萝丽 发表于 2016-11-30 02:19:24

第十三课:扩展课!电压变成数字

单片机中运行的是0、1、0、1的数字信号,只能知道引脚是高电平还是低电平,怎么能测量电压呢?
在15W408AS单片机中,集成了一个神奇的东西:模拟/数字转换器,简称AD转换,能够将引脚上的电压换算成数字。
有了它,你不仅能测电压,还能测电流,加个热敏电阻单片机就能感知到温度,加个光敏电阻单片机就能知道是白天还是黑夜……学会AD转换,你的单片机将大有可为。


本课的主要内容:学会单片机测量电压,做一个小电压表

我爱萝丽爱萝丽 发表于 2016-12-2 04:12:20

本帖最后由 我爱萝丽爱萝丽 于 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即可保证转换完成。



我爱萝丽爱萝丽 发表于 2016-12-3 04:59:43

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-5 03:37:29

本帖最后由 我爱萝丽爱萝丽 于 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 23:05:50

长寿烙铁 发表于 2016-12-3 20:57
你好,我想自制一个简易微型万用表,具有常用的简单功能,可以挂在钥匙上,偶尔用一下。
我的单片机是20脚 ...

这么复杂,哪里还叫简易……测个电压电流还算简单,电阻二极管就不推荐搞了

大林子 发表于 2020-7-23 17:26:46

好帖好帖好帖子顶顶顶顶顶顶顶顶

大饼302 发表于 2019-3-27 23:17:43


期待详细内容,继续加油

咖啡不苦1 发表于 2019-4-29 09:49:22

厉害了,买了所有配件,来学习,嘿嘿{:1_12:}

闪闪的红星 发表于 2018-12-22 23:09:37

学习中学习中学习中

shspdxl 发表于 2018-11-6 07:32:18

萝丽大法好

CPU 发表于 2018-10-14 21:47:34

没有原理图。。。。。。。。。。。。。。。

yht0816zdl 发表于 2018-8-2 15:56:38

看看学习嘻嘻

coolbgo 发表于 2018-7-16 23:13:59

本帖最后由 coolbgo 于 2018-7-17 10:08 编辑

增加了温度探测器功能某宝购买MAX6675 K型热电偶模块 说是可以测1000度用打火机烧到230多度没继续试

coolbgo 发表于 2018-7-16 16:24:15

分享一下我的代码,前面的看不到注释有兴趣的可以下载看看 包含c hex接线脚位和萝莉大神的都一样

另外请问大神,怎么样可以把数码管脚位定义到一个变量里,想了好久没搞明白

菜鸟也会变老鸟 发表于 2016-11-30 05:10:31

顶顶顶,发发发

7861685 发表于 2016-11-30 06:33:47

顶顶顶顶顶

zjy7870477 发表于 2016-11-30 07:05:33

路过,6666666

武杰杰 发表于 2016-11-30 07:26:02

学习一下

鹰隼001 发表于 2016-11-30 07:48:50

帮顶……

机甲老顽童 发表于 2016-11-30 08:12:42

7楼了,走起!

田不辣 发表于 2016-11-30 08:21:31

期待期待,这期学会就能做红外线车模控了。

独行者 发表于 2016-11-30 08:40:43


期待AD转换,做一个小电压表~谢谢!

hhy19791010 发表于 2016-11-30 09:08:23

6666666666666666666666666666

我是大白 发表于 2016-11-30 09:19:12

高级灰

该用户已下架 发表于 2016-11-30 09:22:49

好东西啊

haiyanglin280 发表于 2016-11-30 10:35:05

大师 你课程很容易懂谢谢 ,, 别的教程简直是天书啊 我看了只想睡觉啊

麒麟科技 发表于 2016-11-30 10:53:09

,好教程。顶

LegendFly 发表于 2016-11-30 11:38:53

关键字:数模转换顶萝丽大神

sjzzhsx 发表于 2016-11-30 13:00:15

学习了,。好帖。。

Mr-iuy 发表于 2016-11-30 14:13:55

顶一顶

nb12444 发表于 2016-11-30 18:09:58

顶,顶,顶

狗狐狸桑 发表于 2016-11-30 19:33:41

顶起

海浪淘沙 发表于 2016-11-30 22:35:24

期待详细内容,继续加油
页: [1] 2 3 4 5 6
查看完整版本: 第十三课:扩展课!电压变成数字