单片机论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 3305|回复: 17
打印 上一主题 下一主题
收起左侧

51单片机入门经验分享7-更加实用的ADC转换程序

  [复制链接]
跳转到指定楼层
楼主
dabing89 发表于 2018-10-13 09:22 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
                              更加实用的ADC转换程序


    来接着继续分享单片机的一些学习经历,上一次,我们写了一个测试电压的程序,但是这样的程序是不实用的,所以我们这一节,来介绍个比较实用的程序,还是测电池电压,然后我们解释一下这个代码,这一节狠狠狠重要。。。
     先把代码献上,然后我们再来分析
  1. /*******************************************************************************
  2. * 文件名: ADC转换器使用
  3. * 描  述: 电池电压
  4. * 功  能:中断方式
  5. * 作  者:大核桃
  6. * 版本号:1.0.1(2017.05.23)
  7. *******************************************************************************/
  8. #include "stc15w.h"//头文件
  9. #include "intrins.h"



  10. /*******************************************************************************
  11. * 文件名: 重定义
  12. * 描  述:   
  13. * 功  能:
  14. * 作  者:大核桃
  15. * 版本号:1.0.1(2017.05.23)
  16. *******************************************************************************/
  17. typedef unsigned char uint8;
  18. typedef unsigned int  uint16;
  19. typedef unsigned long uint32;

  20. #define ADC_POWER   0x80            //ADC电源控制位
  21. #define ADC_FLAG    0x10            //ADC完成标志
  22. #define ADC_START   0x08            //ADC起始控制位
  23. #define ADC_SPEEDLL 0x00            //540个时钟
  24. //#define ADC_SPEEDL  0x20            //360个时钟
  25. //#define ADC_SPEEDH  0x40            //180个时钟
  26. //#define ADC_SPEEDHH 0x60            //90个时钟

  27. /*******************************************************************************
  28. * 文件名:全局变量定义区域
  29. * 描  述:
  30. * 功  能:
  31. * 作  者:大核桃
  32. * 版本号:1.0.1(2015.03.03)
  33. *******************************************************************************/
  34. uint8 val,ch;
  35. uint16 temp;
  36. bit flag_ad2 = 0;//电压采集完成标志
  37. bit flag_coll1 = 0;//数据采集间隔
  38. uint16 Adresult_val = 0;//采集的AD数值xx
  39. uint8 ad_count = 0; //采集AD的次数计数器

  40. /*******************************************************************************
  41. * 文件名:共阳数码管真值表
  42. * 描  述:
  43. * 功  能:
  44. * 作  者:大核桃
  45. * 版本号:1.0.1(2015.03.03)
  46. *******************************************************************************/
  47. code uint8 LedChar[] = {
  48.         0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
  49.         0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff,0xc1
  50. };

  51. /*******************************************************************************
  52. * 文件名:单独位定义
  53. * 描  述:   
  54. * 功  能:
  55. * 作  者:大核桃
  56. * 版本号:1.0.1(2017.05.23)
  57. *******************************************************************************/
  58. sbit LED0 = P1^0;//第1组LED
  59. sbit LED1 = P1^1;//第2组LED
  60. sbit LED2 = P1^2;//第3组LED                                 
  61. sbit LED3 = P1^3;//第4组LED
  62. sbit LED4 = P1^4;//第5组LED
  63. sbit LED5 = P3^2;//第6组LED
  64. sbit LED6 = P0^0;//第7组LED
  65. sbit LED7 = P0^1;//第8组LED

  66. sbit LEDS1 = P3^3;//数码管1
  67. sbit LEDS2 = P3^4;//数码管2
  68. sbit LEDS3 = P3^6;//数码管3
  69. sbit LEDS4 = P3^7;//数码管4

  70. /*******************************************************************************
  71. * 文件名:函数前置声明
  72. * 描  述:   
  73. * 功  能:
  74. * 作  者:大核桃
  75. * 版本号:1.0.1(2017.05.23)
  76. *******************************************************************************/
  77. void Mcu_Port_Init();
  78. void LedScan();
  79. void Time0_Init();//定时器0
  80. void InitADC(void);
  81. uint16 VolTage_Monitor(uint8 times); //AD转换与查表处理程序

  82. /*******************************************************************************
  83. * 文件名
  84. * 描  述: 主函数  
  85. * 功  能:入口
  86. * 作  者:大核桃
  87. * 版本号:1.0.1(2017.05.23)
  88. *******************************************************************************/
  89. void main(void)
  90. {                                       
  91.         Mcu_Port_Init();//IO上电初始化
  92.         Time0_Init();
  93.         InitADC();

  94.         while(1)
  95.         {
  96.                 VolTage_Monitor(16);//采集16次数据
  97.         }
  98. }

  99. /*******************************************************************************
  100. * 文件名:void LedScan()
  101. * 描  述: LED刷新
  102. * 功  能:
  103. * 作  者:大核桃
  104. * 版本号:1.0.1(2017.05.23)
  105. *******************************************************************************/
  106. void LedScan()
  107. {
  108.         static uint8 i = 0;

  109.         P2 = 0Xff;
  110.         switch(i)
  111.         {
  112.                 case 0: LEDS4 = 0;LEDS1 = 1;P2 = 0x7f & LedChar[16];i++;break;
  113.                 case 1: LEDS1 = 0;LEDS2 = 1;P2 = LedChar[val / 10 % 10];i++;break;
  114.                 case 2: LEDS2 = 0;LEDS3 = 1;P2 = LedChar[val % 10];i++;break;
  115.                 case 3: LEDS3 = 0;LEDS4 = 1;P2 = LedChar[17];i = 0;break;

  116.                 default:break;
  117.         }
  118. }

  119. /*******************************************************************************
  120. * 文件名:void InitADC(void)
  121. * 描  述: //初始化 AD 转换
  122. * 功  能:
  123. * 作  者:大核桃
  124. * 版本号:1.0.1(2015.03.03)
  125. *******************************************************************************/
  126. void InitADC(void)
  127. {
  128.         P1ASF = 0xE0; //设置 P1 口为模拟口
  129.         ADC_RES = 0; //清除结果寄存器
  130.         CLK_DIV |= 0x20; //ADRJ 为 1,ADC_RES 存放高两位结果,ADC_RESL 存放低 8 位结果
  131.     //ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START;
  132.         ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START;                    //ADC上电并延时
  133. }

  134. /*******************************************************************************
  135. * 文件名:void adc_isr() interrupt 5 using 1
  136. * 描  述: 中断服务程序
  137. * 功  能:
  138. * 作  者:大核桃
  139. * 版本号:1.0.1(2015.03.03)
  140. *******************************************************************************/
  141. void adc_isr() interrupt 5 using 1
  142. {
  143.         EADC = 1;//开ADC中断

  144.         ADC_CONTR &= !ADC_FLAG; //清除ADC中断标志
  145.         temp = ADC_RES;
  146.         temp <<= 8;
  147.         temp |= ADC_RESL;
  148.         flag_ad2 = 1; //电压采集完成标志
  149.         ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START | 5;

  150.         EADC = 0;//关闭中断               

  151. }

  152. /*******************************************************************************
  153. * 文件名:VolTage_Monitor(void);
  154. * 描  述: 电压结果计算
  155. * 功  能:模编程块化
  156. * 作  者:大核桃
  157. * 版本号:1.0.1(2015.03.03)
  158. *******************************************************************************/
  159. uint16 VolTage_Monitor(uint8 times) //AD转换与查表处理程序
  160. {
  161.    if(flag_coll1) //每次采集AD的时间间隔标志位
  162.    {
  163.                 if(ad_count < times)//连续采集16次后再把求总数据的平均值
  164.                 {
  165.                         if(flag_ad2 == 1)   //完成一次AD采样
  166.                         {
  167.                                 flag_ad2 = 0;  //清除完成一次采样的标志位

  168.                                 Adresult_val = Adresult_val + temp;
  169.                                 ad_count++;
  170.                                 ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START | 5;
  171.                         }
  172.                 } //右移动一位数据就相当于整除以2
  173.                 else  //已经采集完16次数据,这个时候把总累加数据除以16就可以求得平均值了
  174.                 {
  175.                         Adresult_val >>= 4;
  176.                         val = ((Adresult_val) * 2 * (3.3 / 1023) * 10);//放大10
  177.                         Adresult_val = 0; //AD暂存清零
  178.                         temp = 0;        //把采集AD的结果清零
  179.                         ad_count = 0;     //把采集次数重新清零
  180.                 }
  181.    }

  182.    return  val;//返回采集的电压数值
  183. }

  184. /*******************************************************************************
  185. * 文件名:void Time0_Init()
  186. * 描  述: 定时器0初始化
  187. * 功  能:1毫秒@11.0592MHz
  188. * 作  者:大核桃
  189. * 版本号:1.0.1(2017.05.23)
  190. *******************************************************************************/
  191. void Time0_Init(void)
  192. {
  193.         AUXR |= 0x80;   //定时器时钟1T模式
  194.         TMOD &= 0xF0;        //设置定时器模式
  195.         TL0 = 0xCD;                //设置定时初值
  196.         TH0 = 0xD4;                //设置定时初值
  197.         ET0 = 1;
  198.         TR0 = 1;                //定时器0开始计时
  199.         EA = 1;               
  200. }
  201. /*******************************************************************************
  202. * 文件名:
  203. * 描  述: 中断函数
  204. * 功  能:1毫秒@11.0592MHz
  205. * 作  者:大核桃
  206. * 版本号:1.0.1(2017.05.23)
  207. *******************************************************************************/
  208. void ET0_IRQHandler() interrupt 1
  209. {
  210.         static uint8 tmrcoll1 = 0;//数据采集间隔

  211.         EADC = 0;  //在定时中断中禁止AD中断

  212.     TL0 = 0xCD;                //设置定时初值
  213.         TH0 = 0xD4;                //设置定时初值

  214.         tmrcoll1++;//数据采集时间累加
  215.         if(tmrcoll1 >= 2) // 2 = 2ms                                                                                                                                                                           //1
  216.         {
  217.             tmrcoll1 = 0;
  218.                 flag_coll1 = 1;//数据采集间隔标志位 2ms读取一次数据
  219.         }

  220.         LedScan();

  221.         EADC = 1;  //在定时中断中打开AD中断
  222.                         
  223. }

  224. /*******************************************************************************
  225. * 文件名:void Mcu_Port_Init()
  226. * 描  述: io初始化
  227. * 功  能:
  228. * 作  者:大核桃
  229. * 版本号:1.0.1(2017.05.23)
  230. *******************************************************************************/
  231. void Mcu_Port_Init()
  232. {
  233.         IE = 0xa8;//允许AD转换
  234.         //将P0口低二位配置为推挽输出
  235.         //234567位配置位高阻输入
  236.         P0M1 = 0xFC;//1111 1100
  237.         P0M0 = 0X03;//0000 0011
  238.         //P0 = 0X01;//第6个
  239.         //P0 = 0X02;//第7个
  240.         //高3位配置高阻输入,用作模拟口
  241.         //其他配置推挽输出,驱动LED
  242.         P1M1 = 0xE0;//1110 0000
  243.         P1M0 = 0X1F;//0001 1111
  244.         //P2口配置准双向口
  245.         P2M1 = 0X00;
  246.         P2M0 = 0X00;
  247.         P2 = 0Xff; //上电为1111 1111


  248. //        //P54,P55口为推挽输出
  249.         P5M1 = 0X00;
  250.         P5M0 = 0X00;
  251.         P5 = 0xFF;

  252.         //P37,P36,3.2,P3.3 P3.4口为推挽输出
  253.         P3M1 = 0X00;
  254.         P3M0 = 0XFC;
  255.         P3 = 0X23; //0010 0111//第5个LED端口
  256.         
  257.         LED0 = 0;//第1组LED,如果使能请置为1
  258.         LED1 = 0;
  259.         LED2 = 0;
  260.         LED3 = 0;
  261.         LED4 = 0;
  262.         LED5 = 0;
  263.         LED6 = 0;
  264.         LED7 = 0;        
  265. }
复制代码


先来介绍一些基本的理论知识,不然的话,可能有些东西无法搞懂。
     关于ADC的参考电压
因为我们的电子时钟是锂电池供电的,电压是3.7V的,我们这里用了一个3.3V的稳压芯片662K,输出3.3V直接作为单片机的电源,也作为ADC的参考电压,这里我们简化了设计,没有用外部的参考电压源,对于一个简单的来说,这样也是可以的。
     关于ADC的位数和分辨率
在这里,我们选择ADC工作在10位方式,10位的ADC,是从0-1023,那么分辨率也就是3.3/1023 = 0.0032258064516129V,大概一个分辨率3mv左右。
     关于转换时间和转换速率
转换时间和转换速率是倒数的关系,所谓的转换时间,指的是ADC从开始启动,到ADC转换完成出结果,这个时间该怎么去计算呢?我们在程序中选择了时钟频率是11.0592MHZ,那么我们ADC的时钟频率也就是11.0592MHZ了,在程序中,我们选择了540个时钟周期完成一个ADC转换,转换速率也就是20KHZ左右,转换时间大约是48US左右
    关于采样频率和采样周期
采样频率和采样周期也是互为倒数的关系,这个和上面的转换时间,转换速率非常容易让人搞迷糊,关于采样频率,有一个采样定理,叫奈奎斯特采样定律,这个定律说的是,采样频率不能低于输入ADC的信号的最高频率的2倍,举个例子,比如上面这样的情况,我们选择540个时钟周期完成一次ADC转换,那么转换速率是20KHZ,那么也就是说,如果我们要保证信号采集的是完整的波形,那么这个输入的信号不能超过10KHZ,你想想看,如果输入的信号大于10KHZ,而你转换速率是20KHZ,如果采样频率小于20KHZ,那么可能你还没有完成一个完整的ADC转换过程,或者采集的波形不是完整的,那么这样的ADC的结果跟实际值比较会存在严重的失真,这样是不被允许的。

   好了,经过以上知识的铺垫,再来看程序代码就应该比较容易懂了,在程序中,我们选择了在ADC进中断前打开EADC,处理完数据后,要关闭EADC这个ADC转换中断使能标志位,防止其他中断或者任务打断ADC的采集。我们在任务中选择的采样频率是500HZ,也就是2MS启动ADC采集依次数据,连续采集16次,因为我们所采集的电压信号的变化频率没有那么快速,所以,我们这里是可以这样用的,当启动了一次AD转换之后,进行计数,如果小于16次,那么一直在IF里面执行,当系统检测到完成一个AD转换,将AD转换完成标志位清零,将读取的相关通道的ADC数值累加,继续采集,如果采集完成了,那么进行取平均运算,然后将结果计算出来,赋值给相关的变量就可以了,然后对相关的变量或者缓冲区清零,这样,显示在数码管上的电压结果是比较稳定的。和前面那个例子不一样,我们这个例子用的是STC15W单片机定时器0的1T模式,也就是说比原来快了12倍,这个移植的时候一定要注意。
   好了,今天就到这里吧,源码奉献上。
007 实用的AD转换器的使用.rar (50.8 KB, 下载次数: 44)



     



评分

参与人数 1黑币 +3 收起 理由
tab001 + 3

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏3 转播转播 分享分享 分享淘帖 顶2 踩
回复

使用道具 举报

无效楼层,该帖已经被删除
板凳
iousky 发表于 2018-10-22 14:14 | 只看该作者
感谢分享
回复

使用道具 举报

无效楼层,该帖已经被删除
5#
baxk1986 发表于 2018-10-28 09:27 | 只看该作者
感谢分享............
回复

使用道具 举报

6#
TRAVISLYH 发表于 2018-11-3 10:00 | 只看该作者
感谢分享
回复

使用道具 举报

7#
topsjb 发表于 2018-11-10 23:55 | 只看该作者
感谢分享............
回复

使用道具 举报

8#
abingma 发表于 2018-11-16 10:19 | 只看该作者
谢谢大佬
回复

使用道具 举报

9#
hjh2621845983 发表于 2018-11-20 15:30 来自手机 | 只看该作者
感谢,能派上用场了
回复

使用道具 举报

10#
chliang 发表于 2018-11-28 16:49 | 只看该作者
感谢分享
回复

使用道具 举报

11#
双赢电子 发表于 2018-11-28 22:06 | 只看该作者
多谢师傅知识分享,公开者大公无私,赞一个
回复

使用道具 举报

12#
无效楼层,该帖已经被删除
13#
LowHuang 发表于 2018-11-29 17:17 | 只看该作者
楼主,val = ((Adresult_val) * 2 * (3.3 / 1023) * 10);//放大10 这里为什么要*2呢
回复

使用道具 举报

14#
无效楼层,该帖已经被删除
15#
仗义 发表于 2018-12-10 08:10 | 只看该作者
厉害了额 小学生这么难的
回复

使用道具 举报

16#
841812960 发表于 2018-12-24 16:01 | 只看该作者
感谢分享
回复

使用道具 举报

17#
se苦涩 发表于 2018-12-28 09:46 | 只看该作者
仗义 发表于 2018-12-10 08:10
厉害了额 小学生这么难的

谁让我们是从幼儿园毕业200多月的宝宝呢
回复

使用道具 举报

18#
个地调 发表于 2019-1-3 20:10 | 只看该作者
厉害厉害
回复

使用道具 举报

19#
2019keil 发表于 2019-1-23 10:13 | 只看该作者

感谢分享
回复

使用道具 举报

20#
struct00 发表于 2019-2-20 17:25 | 只看该作者
有一点用,先下了
回复

使用道具 举报

21#
84399253 发表于 2019-3-6 13:46 | 只看该作者
MARK,多谢楼主分享
回复

使用道具 举报

22#
PO999999 发表于 2019-5-10 15:39 | 只看该作者
感谢,谢谢分享
回复

使用道具 举报

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

本版积分规则

QQ|手机版|小黑屋|单片机论坛 |51Hei单片机16群 联系QQ:125739409;技术交流QQ群7344883

Powered by 单片机教程网

快速回复 返回顶部 返回列表