模吧

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

29109查看 | 101回复

[单片机课堂] 第十三课:扩展课!电压变成数字

  [复制链接]
发表于 2016-11-30 02:19:24 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
单片机中运行的是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口模拟功能寄存器
第十三课:扩展课!电压变成数字 电压230v正常吗 作者:我爱萝丽爱萝丽 7786
设置P1.7引脚对应的程序是: P1ASF=0x80;


本节的难点在于最后这个寄存器,稍微复杂:

知识点(49) sfr ADC_CONTR=0xBC  声明 模数转换寄存器
第十三课:扩展课!电压变成数字 电压230v正常吗 作者:我爱萝丽爱萝丽 5028 .
这个寄存器里面参数较多,不过我们只需要用该用到的就行了,别的暂时不用理会。
寄存器的后三位是引脚选择,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即可保证转换完成。




回复 支持 0 反对 1

使用道具 举报

 楼主| 发表于 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结果换算成电压?
  1. #include<reg51.h>

  2. sfr P2M0=0x96;     //声明 P2引脚模式寄存器
  3. sfr P3M0=0xb2;     //声明 P3引脚模式寄存器
  4. sfr P1ASF=0x9D;    //声明 P1口模拟功能寄存器
  5. sfr ADC_RES=0xBD;  //声明 ADC转换结果寄存器
  6. sfr ADC_CONTR=0xBC;//声明 ADC控制寄存器


  7. sbit DA=P2^4;                        //数码管每个引脚
  8. sbit DB=P3^2;
  9. sbit DC=P3^6;
  10. sbit DD=P2^0;
  11. sbit DE=P2^1;
  12. sbit DF=P2^3;
  13. sbit DG=P3^5;
  14. sbit DP=P3^7;

  15. sbit B1=P2^5;                        //四个数码管的引脚定义
  16. sbit B2=P2^2;
  17. sbit B3=P3^3;
  18. sbit B4=P3^4;


  19. unsigned char display_time;        //定义变量用于显示次数

  20. unsigned char Data1;        //第一个数码管要显示的数据
  21. unsigned char Data2;        //第二个数码管要显示的数据
  22. unsigned char Data3;        //第三个数码管要显示的数据
  23. unsigned char Data4;        //第四个数码管要显示的数据

  24. unsigned char adc;          //保存AD转换结果

  25. void Delay1ms()                //@12.000MHz
  26. {
  27.         unsigned char i, j;

  28.         i = 12;
  29.         j = 169;
  30.         do
  31.         {
  32.                 while (--j);
  33.         } while (--i);
  34. }

  35. void delay_n_ms(unsigned int n) //延时n毫秒函数
  36. {
  37.         while(n)         
  38.         {
  39.                 Delay1ms();
  40.                 n=n-1;//每循环一次n减小1
  41.         }   
  42. }

  43. void Delay50us()                //@12.000MHz
  44. {
  45.         unsigned char i, j;

  46.         i = 1;
  47.         j = 146;
  48.         do
  49.         {
  50.                 while (--j);
  51.         } while (--i);
  52. }

  53. void display(unsigned char x)//控制数码管显示内容的函数
  54. {
  55.         //判断x的值来决定显示什么
  56.         if(x==0){DA=1;DB=1;DC=1;DD=1;DE=1;DF=1;DG=0;DP=0;} //显示“0”
  57.        
  58.         if(x==1){DA=0;DB=1;DC=1;DD=0;DE=0;DF=0;DG=0;DP=0;} //显示“1”
  59.        
  60.         if(x==2){DA=1;DB=1;DC=0;DD=1;DE=1;DF=0;DG=1;DP=0;} //显示“2”
  61.        
  62.         if(x==3){DA=1;DB=1;DC=1;DD=1;DE=0;DF=0;DG=1;DP=0;} //显示“3”

  63.         if(x==4){DA=0;DB=1;DC=1;DD=0;DE=0;DF=1;DG=1;DP=0;} //显示“4”
  64.        
  65.         if(x==5){DA=1;DB=0;DC=1;DD=1;DE=0;DF=1;DG=1;DP=0;} //显示“5”
  66.        
  67.         if(x==6){DA=1;DB=0;DC=1;DD=1;DE=1;DF=1;DG=1;DP=0;} //显示“6”
  68.        
  69.         if(x==7){DA=1;DB=1;DC=1;DD=0;DE=0;DF=0;DG=0;DP=0;} //显示“7”
  70.        
  71.         if(x==8){DA=1;DB=1;DC=1;DD=1;DE=1;DF=1;DG=1;DP=0;} //显示“8”
  72.        
  73.         if(x==9){DA=1;DB=1;DC=1;DD=1;DE=0;DF=1;DG=1;DP=0;} //显示“9”
  74.        
  75. }


  76. main()
  77. {
  78.         P2M0=0x1B;                //需要高电平大电流的引脚 2.4、2.3、2.1、2.0,填入数值0001 1011
  79.         P3M0=0xE4;                //需要高电平大电流的引脚 3.7、3.6、3.5、3.2,填入数值1110 0100
  80.        
  81.        
  82.         while(1)
  83.         {
  84.                 delay_n_ms(4);//系统周期4ms
  85.                
  86.                 P1ASF=0x80;  //设置P1.7引脚
  87.                 ADC_CONTR=0x88+7;  //开始P1.7引脚转换
  88.                 Delay50us();   //等待AD转换完成
  89.                 adc=ADC_RES;  //读取转换结果
  90.                
  91.                 Data1=0;
  92.                 Data2=adc/100;          //百位数据
  93.                 Data3=adc%100/10; //十位数据
  94.                 Data4=adc%10;     //个位数据
  95.                
  96.                 ///////////////////////////////////////////////////////
  97.                 //显示程序模块化,只需要改变Data1/2/3/4的值就可以控制显示内容
  98.                
  99.                 display_time=display_time+1;
  100.                 if(display_time>3)
  101.                 {
  102.                         display_time=0;  //0、1、2、3 四个循环
  103.                 }
  104.                
  105.                 if(display_time==0)//第0次显示第一个数码管
  106.                 {
  107.                         B1=0;B2=1;B3=1;B4=1;       
  108.                         display(Data1);                //显示第1位数据
  109.                 }
  110.                
  111.                 if(display_time==1)//第1次显示第二个数码管
  112.                 {
  113.                         B1=1;B2=0;B3=1;B4=1;       
  114.                         display(Data2);                //显示第2位数据
  115.                 }
  116.                
  117.                 if(display_time==2)//第2次显示第三个数码管
  118.                 {
  119.                         B1=1;B2=1;B3=0;B4=1;       
  120.                         display(Data3);                //显示第3位数据
  121.                 }
  122.                
  123.                 if(display_time==3)//第3次显示第四个数码管
  124.                 {
  125.                         B1=1;B2=1;B3=1;B4=0;       
  126.                         display(Data4);                //显示第4位数据
  127.                 }
  128.                        
  129.                
  130.         }
  131. }
复制代码


回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 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或多次测量求平均值等方法提高精度……





一个实现中等要求的小程序:
  1. #include<reg51.h>

  2. sfr P2M0=0x96;     //声明 P2引脚模式寄存器
  3. sfr P3M0=0xb2;     //声明 P3引脚模式寄存器
  4. sfr P1ASF=0x9D;    //声明 P1口模拟功能寄存器
  5. sfr ADC_RES=0xBD;  //声明 ADC转换结果寄存器
  6. sfr ADC_CONTR=0xBC;//声明 ADC控制寄存器


  7. sbit DA=P2^4;                        //数码管每个引脚
  8. sbit DB=P3^2;
  9. sbit DC=P3^6;
  10. sbit DD=P2^0;
  11. sbit DE=P2^1;
  12. sbit DF=P2^3;
  13. sbit DG=P3^5;
  14. sbit DP=P3^7;

  15. sbit B1=P2^5;                        //四个数码管的引脚定义
  16. sbit B2=P2^2;
  17. sbit B3=P3^3;
  18. sbit B4=P3^4;


  19. unsigned char display_time;        //定义变量用于显示次数

  20. unsigned char Data1;        //第一个数码管要显示的数据
  21. unsigned char Data2;        //第二个数码管要显示的数据
  22. unsigned char Data3;        //第三个数码管要显示的数据
  23. unsigned char Data4;        //第四个数码管要显示的数据

  24. unsigned char count;        //计时
  25. unsigned int adc;          //AD转换计算


  26. void Delay1ms()                //@12.000MHz
  27. {
  28.         unsigned char i, j;

  29.         i = 12;
  30.         j = 169;
  31.         do
  32.         {
  33.                 while (--j);
  34.         } while (--i);
  35. }

  36. void delay_n_ms(unsigned int n) //延时n毫秒函数
  37. {
  38.         while(n)         
  39.         {
  40.                 Delay1ms();
  41.                 n=n-1;//每循环一次n减小1
  42.         }   
  43. }

  44. void Delay50us()                //@12.000MHz
  45. {
  46.         unsigned char i, j;

  47.         i = 1;
  48.         j = 146;
  49.         do
  50.         {
  51.                 while (--j);
  52.         } while (--i);
  53. }

  54. void display(unsigned char x)//控制数码管显示内容的函数
  55. {
  56.         //判断x的值来决定显示什么
  57.         if(x==0){DA=1;DB=1;DC=1;DD=1;DE=1;DF=1;DG=0;DP=0;} //显示“0”        
  58.         if(x==1){DA=0;DB=1;DC=1;DD=0;DE=0;DF=0;DG=0;DP=0;} //显示“1”        
  59.         if(x==2){DA=1;DB=1;DC=0;DD=1;DE=1;DF=0;DG=1;DP=0;} //显示“2”        
  60.         if(x==3){DA=1;DB=1;DC=1;DD=1;DE=0;DF=0;DG=1;DP=0;} //显示“3”
  61.         if(x==4){DA=0;DB=1;DC=1;DD=0;DE=0;DF=1;DG=1;DP=0;} //显示“4”        
  62.         if(x==5){DA=1;DB=0;DC=1;DD=1;DE=0;DF=1;DG=1;DP=0;} //显示“5”        
  63.         if(x==6){DA=1;DB=0;DC=1;DD=1;DE=1;DF=1;DG=1;DP=0;} //显示“6”        
  64.         if(x==7){DA=1;DB=1;DC=1;DD=0;DE=0;DF=0;DG=0;DP=0;} //显示“7”        
  65.         if(x==8){DA=1;DB=1;DC=1;DD=1;DE=1;DF=1;DG=1;DP=0;} //显示“8”        
  66.         if(x==9){DA=1;DB=1;DC=1;DD=1;DE=0;DF=1;DG=1;DP=0;} //显示“9”
  67.                                                         
  68.         if(x==10){DA=1;DB=1;DC=1;DD=1;DE=1;DF=1;DG=0;DP=1;} //显示“0”带小数点        
  69.         if(x==11){DA=0;DB=1;DC=1;DD=0;DE=0;DF=0;DG=0;DP=1;} //显示“1”带小数点        
  70.         if(x==12){DA=1;DB=1;DC=0;DD=1;DE=1;DF=0;DG=1;DP=1;} //显示“2”带小数点        
  71.         if(x==13){DA=1;DB=1;DC=1;DD=1;DE=0;DF=0;DG=1;DP=1;} //显示“3”带小数点
  72.         if(x==14){DA=0;DB=1;DC=1;DD=0;DE=0;DF=1;DG=1;DP=1;} //显示“4”带小数点        
  73.         if(x==15){DA=1;DB=0;DC=1;DD=1;DE=0;DF=1;DG=1;DP=1;} //显示“5”带小数点        
  74.         if(x==16){DA=1;DB=0;DC=1;DD=1;DE=1;DF=1;DG=1;DP=1;} //显示“6”带小数点
  75.         if(x==17){DA=1;DB=1;DC=1;DD=0;DE=0;DF=0;DG=0;DP=1;} //显示“7”带小数点        
  76.         if(x==18){DA=1;DB=1;DC=1;DD=1;DE=1;DF=1;DG=1;DP=1;} //显示“8”带小数点
  77.         if(x==19){DA=1;DB=1;DC=1;DD=1;DE=0;DF=1;DG=1;DP=1;} //显示“9”带小数点
  78. }


  79. main()
  80. {
  81.         P2M0=0x1B;                //需要高电平大电流的引脚 2.4、2.3、2.1、2.0,填入数值0001 1011
  82.         P3M0=0xE4;                //需要高电平大电流的引脚 3.7、3.6、3.5、3.2,填入数值1110 0100
  83.         
  84.         P1ASF=0x80;  //设置P1.7引脚为模拟功能
  85.         
  86.         while(1)
  87.         {
  88.                 delay_n_ms(4);//系统周期4ms
  89.                
  90.                 count=count+1;
  91.                 if(count>125)  //0.5秒测量一次电压
  92.                 {
  93.                         count=0;
  94.                         
  95.                         ADC_CONTR=0x88+7;  //开始P1.7引脚转换
  96.                         Delay50us();   //等待AD转换完成
  97.                         adc=ADC_RES;  //读取转换结果
  98.                         
  99.                         adc=adc*2;      //量程增大倍数
  100.                         adc=adc*10/51;  //计算电压*10倍,消除小数点                        
  101.                         
  102.                         Data1=0;
  103.                         Data2=adc/100;          //百位数据
  104.                         Data3=adc%100/10; //十位数据
  105.                         Data3=Data3+10;   //加10,display函数显示的数字会带小数点
  106.                         
  107.                         Data4=adc%10;     //个位数据        
  108.                 }
  109.                
  110.                
  111.                 ///////////////////////////////////////////////////////
  112.                 //显示程序模块化,只需要改变Data1/2/3/4的值就可以控制显示内容
  113.                
  114.                 display_time=display_time+1;
  115.                 if(display_time>3)
  116.                 {
  117.                         display_time=0;  //0、1、2、3 四个循环
  118.                 }
  119.                
  120.                 if(display_time==0)//第0次显示第一个数码管
  121.                 {
  122.                         B1=0;B2=1;B3=1;B4=1;        
  123.                         display(Data1);                //显示第1位数据
  124.                 }
  125.                
  126.                 if(display_time==1)//第1次显示第二个数码管
  127.                 {
  128.                         B1=1;B2=0;B3=1;B4=1;        
  129.                         display(Data2);                //显示第2位数据
  130.                 }
  131.                
  132.                 if(display_time==2)//第2次显示第三个数码管
  133.                 {
  134.                         B1=1;B2=1;B3=0;B4=1;        
  135.                         display(Data3);                //显示第3位数据
  136.                 }
  137.                
  138.                 if(display_time==3)//第3次显示第四个数码管
  139.                 {
  140.                         B1=1;B2=1;B3=1;B4=0;        
  141.                         display(Data4);                //显示第4位数据
  142.                 }
  143.                         
  144.                
  145.         }
  146. }
复制代码




回复 支持 1 反对 0

使用道具 举报

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

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

使用道具 举报

发表于 2020-7-23 17:26:46 | 显示全部楼层
好帖好帖好帖子顶顶顶顶顶顶顶顶
回复 支持 0 反对 1

使用道具 举报

发表于 2019-3-27 23:17:43 | 显示全部楼层

期待详细内容,继续加油
回复 支持 1 反对 0

使用道具 举报

发表于 2019-4-29 09:49:22 | 显示全部楼层
厉害了,买了所有配件,来学习,嘿嘿第十三课:扩展课!电压变成数字 电压230v正常吗 作者:咖啡不苦1 2594
回复 支持 0 反对 1

使用道具 举报

发表于 2018-12-22 23:09:37 | 显示全部楼层
学习中学习中学习中
回复 支持 1 反对 0

使用道具 举报

发表于 2018-11-6 07:32:18 | 显示全部楼层
萝丽大法好
回复 支持 0 反对 1

使用道具 举报

发表于 2018-10-14 21:47:34 | 显示全部楼层
没有原理图。。。。。。。。。。。。。。。
回复 支持 1 反对 0

使用道具 举报

发表于 2018-8-2 15:56:38 | 显示全部楼层
看看  学习嘻嘻
回复 支持 0 反对 1

使用道具 举报

发表于 2018-7-16 23:13:59 | 显示全部楼层
本帖最后由 coolbgo 于 2018-7-17 10:08 编辑

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

数码管.zip

3.48 KB, 下载次数: 5, 下载积分: 牛币 -10

回复 支持 1 反对 0

使用道具 举报

发表于 2018-7-16 16:24:15 | 显示全部楼层
分享一下我的代码,前面的看不到注释  有兴趣的可以下载看看 包含c hex  接线脚位和萝莉大神的都一样

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

数码管.zip

2.38 KB, 下载次数: 2, 下载积分: 牛币 -10

回复 支持 1 反对 0

使用道具 举报

发表于 2016-11-30 05:10:31 | 显示全部楼层
顶顶顶,发发发
发表于 2016-11-30 06:33:47 | 显示全部楼层
顶顶顶顶顶
发表于 2016-11-30 07:05:33 | 显示全部楼层
路过,6666666
发表于 2016-11-30 07:26:02 | 显示全部楼层
学习一下
发表于 2016-11-30 07:48:50 | 显示全部楼层
帮顶……
发表于 2016-11-30 08:12:42 | 显示全部楼层
7楼了,走起!
发表于 2016-11-30 08:21:31 | 显示全部楼层
期待期待,这期学会就能做红外线车模控了。
回复 支持 1 反对 0

使用道具 举报

发表于 2016-11-30 08:40:43 | 显示全部楼层

期待AD转换,做一个小电压表~谢谢!
回复 支持 1 反对 0

使用道具 举报

发表于 2016-11-30 09:08:23 | 显示全部楼层
6666666666666666666666666666
发表于 2016-11-30 09:19:12 | 显示全部楼层
高级灰
发表于 2016-11-30 09:22:49 | 显示全部楼层
好东西啊
发表于 2016-11-30 10:35:05 | 显示全部楼层
大师 你课程很容易懂谢谢 ,, 别的教程简直是天书啊 我看了只想睡觉啊
回复 支持 1 反对 0

使用道具 举报

发表于 2016-11-30 10:53:09 | 显示全部楼层
,好教程。顶
发表于 2016-11-30 11:38:53 | 显示全部楼层
关键字:数模转换  顶萝丽大神
发表于 2016-11-30 13:00:15 | 显示全部楼层
学习了,。好帖。。
发表于 2016-11-30 14:13:55 | 显示全部楼层
顶一顶
发表于 2016-11-30 18:09:58 | 显示全部楼层
顶,顶,顶
发表于 2016-11-30 22:35:24 | 显示全部楼层
期待详细内容,继续加油
回复 支持 0 反对 1

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

QQ|关于模吧|APP下载|广告报价|手机版|企业会员|商城入驻|联系我们|模吧 ( 黔ICP备2022002348号-1 )

© 2013-2020 Moz8.com 模吧,玩出精彩!