【STC8A8K64D4开发板】第2-11讲:模数转换ADC

第2-11讲:模数转换ADC

    1. 学习目的

1. 了解ADC的基本概念:分辨率、精度等。

2. 掌握STC8A8K64D4单片机ADC的配置、采样数据计算为实际电压值的方法。

3. 掌握ADC多通道采样。

    1. ADC基本概念

实际应用中,我们经常需要将模拟量转换为数字量供CPU处理,如电池电压检测、温度检测等等。对于CPU来说,他能处理的是数字量,所以,需要通过A/D转换(模数转换)将时间连续、幅值也连续的模拟量转换为时间离散、幅值也离散的数字信号,从而实现CPU对模拟信号的处理,这种能够实现A/D转换功能的电路称之为模数转换器(ADC:Analog-to-digital converter)。

ADC的结构和实现原理有多种方式,常见的ADC的类型有积分型、逐次逼近型、并行比较型/串并行型、Σ-Δ调制型等。STC8A8K64D4集成的是逐次逼近型ADC,他由一个比较器和 D/A 转换器通过逐次比较逻辑构成,从 MSB 开始,顺序地对每一位将输入电压与内置 D/A 转换器输出进行比较,经 n 次比较而输出数字值。其优点是速度较高、功耗低。

ADC常用的技术参数有以下几点,这是学习ADC必须要掌握的。

  1. 分辨率

ADC分辨率是指输出数字量变化一个最低有效位(LSB)所需的输入模拟电压的变化量。ADC的分辨率用位数表示,如10位的ADC,分辨率为210=1024。如果ADC的量程为(0~5)V,那么ADC即可“分辨”出(5/1024)V的电压变化。

  1. ADC 精度

ADC 的精度取决于量化误差及系统内其他误差的总和。这里要特别注意ADC分辨率和ADC精度的区别,“精度”是用来描述物理量的准确程度的,而“分辨率”是用来描述刻度划分的。对于一个给定的器件,他的分辨率是固定的,如STC8A8K64D4,他的分辨率固定为12位的,但是他的精度不仅仅受器件本身的影响,还可能会受PCB布线、外界环境(温度、湿度、干扰等)的影响而变化。

  1. 转换速度

转换速度是指完成一次从模拟转换到数字的AD转换所需的时间的倒数,STC8A8K64D4单片机最快的速度可以达到800K(每秒转换80万次)。

    1. ADC的应用流程

STC8A8K64D4单片机内部集成了一个12位高速A/D转换器,共有16个通道,ADC最快的转换速度可以达到800K,并且ADC 转换结果的数据格式可以配置为左对齐或右对齐格式,以方便用户程序进行读取和引用。ADC应用步骤如下图所示。

【STC8A8K64D4开发板】第2-11讲:模数转换ADC_第1张图片

图1:ADC应用步骤

      1. 配置ADC通道

使用STC8A8K64D4单片机ADC时,需要选择使用的ADC通道,STC8A8K64D4单片机ADC的16个通道如下表所示。这里要注意一下,ADC的通道15是没有输入引脚的,他只能用于检测内部参考信号源。

表1:STC8A8K64D4单片机ADC引脚分配

ADC通道

对应IO口

功能描述

ADC0

P1.0

ADC模拟通道输入0

ADC1

P1.1

ADC模拟通道输入1

ADC2

P1.2

ADC模拟通道输入2

ADC3

P1.3

ADC模拟通道输入3

ADC4

P1.4

ADC模拟通道输入4

ADC5

P1.5

ADC模拟通道输入5

ADC6

P1.6

ADC模拟通道输入6

ADC7

P1.7

ADC模拟通道输入7

ADC8

P0.0

ADC模拟通道输入8

ADC9

P0.1

ADC模拟通道输入9

ADC10

P0.2

ADC模拟通道输入10

ADC11

P0.3

ADC模拟通道输入11

ADC12

P0.4

ADC模拟通道输入12

ADC13

P0.5

ADC模拟通道输入13

ADC14

P0.6

ADC模拟通道输入14

ADC15

ADC 的第 15 通道只能用于检测内部参考信号源, 参考信号源值出厂时校准为 1.19V,由于制造误差以及测量误差,导致实际的内部参考信号源相比 1.19V,大约有± 1%的误差。如果用户需要知道每一颗芯片的准确内部参考信号源值,可外接精准参考信号源,然后利用 ADC 的第 15 通道进行测量标定。

ADC输入通道由“ADC 控制寄存器(ADC_CONTR)”中的ADC_CHS[3:0]位确定,如下所示。另外需要注意,当有I/O口被选择为ADC输入通道时,需要通过PxM0/PxM1寄存器将I/O口模式设置为高阻输入模式。

ADC 控制寄存器(ADC_CONTR)

  1. ADC_CHS[3:0] ADC 通道
  1. 0000:P1.0/ADC0。
  2. 0001:P1.1/ADC1。
  3. 0010:P1.2/ADC2。
  4. 0011:P1.3/ADC3。
  5. 0100:P1.4/ADC4。
  6. 0101:P1.5/ADC5。
  7. 0110:P1.6/ADC6。
  8. 0111:P1.7/ADC7。
  9. 1000:P0.0/ADC8。
  10. 1001:P0.1/ADC9。
  11. 1010:P0.2/ADC10。
  12. 1011:P0.3/ADC11。
  13. 1100:P0.4/ADC12。
  14. 1101:P0.5/ADC13。
  15. 1110:P0.6/ADC14。
  16. 1111:测试内部 1.19V。
      1. 配置ADC时钟频率和内部时序

ADC时钟频率和内部时序决定了ADC的转换速度,STC8A8K64D4的ADC转换速度计算公式如下。可以看到ADC转换速度和时钟频率“SPEED”以及ADC内部时序控制寄存器中的“CSSETUP”、“CSHOLD”和“SMPDUTY”相关。

对于ADC转换速度,要注意以下几点:

  1. 12位ADC的速度不能高于800KHz。
  2. SMPDUTY 的值不能小于10,建议设置为15。
  3. CSSETUP可使用上电默认值0。
  4. CHOLD 可使用上电默认值1(ADCTIM 建议设置为3FH)。
  1. ADC时钟频率设置

ADC时钟频率由“ADC 配置寄存器(ADCCFG)”中的SPEED[3:0]位确定,如下所示。

ADC 配置寄存器(ADCCFG)

  1. SPEED[3:0]:设置ADC工作时钟频率FADC= SYSclk/2/(SPEED+1)。

  1. ADC内部时序设置

ADC时序由“ADC时序控制寄存器(ADCTIM)”中的“CSSETUP”、“CSHOLD”和“SMPDUTY”确定,如下所示。

ADC 时序控制寄存器(ADCTIM)

【STC8A8K64D4开发板】第2-11讲:模数转换ADC_第2张图片

  1. CSSETUP:ADC 通道选择时间控制 Tsetup
  1. 0:1(默认值),即占用1个ADC工作时钟。
  2. 1:2,即占用2个ADC工作时钟。

  1. CSHOLD[1:0]:ADC 通道选择保持时间控制Thold
  1. 00:1,即占用1个ADC工作时钟。
  2. 01:2(默认值),即占用2个ADC工作时钟。
  3. 10:3,即占用3个ADC工作时钟。
  4. 11:4,即占用4个ADC工作时钟。
  1. SMPDUTY[4:0]:ADC 模拟信号采样时间控制Tduty(注意:SMPDUTY的值必须≥01010B),SMPDUTY的值对应的占用ADC工作时钟数如下表所示。

【STC8A8K64D4开发板】第2-11讲:模数转换ADC_第3张图片

      1. 配置ADC数据格式

为了方便用户在程序里面处理ADC采样数据,STC8A8K64D4的ADC提供了两种数据格式:左对齐和右对齐,数据格式由“ADC 配置寄存器(ADCCFG)”中的“RESFMT”位设置,如下所示。

ADC 配置寄存器(ADCCFG)

  1. RESFMT:ADC 转换结果格式控制位
  1. 0:转换结果左对齐。ADC_RES 保存结果的高8位,ADC_RESL保存结果的低4位,格式如下:

【STC8A8K64D4开发板】第2-11讲:模数转换ADC_第4张图片

  1. 1:转换结果右对齐。ADC_RES保存结果的高4位,ADC_RESL保存结果的低8位,格式如下:

【STC8A8K64D4开发板】第2-11讲:模数转换ADC_第5张图片

      1. 配置ADC中断

ADC中断的开启和关闭由中断使能寄存器IE的位5(EADC)控制,如下图所示。另外注意:开启ADC中断的情况下,还需要开启总中断“EA=1”,ADC中断才能起作用。

  1. EADC:A/D 转换中断允许位。
  1. 0:禁止A/D 转换中断。
  2. 1:允许A/D 转换中断。
      1. ADC上电和启动

ADC上电和启动由“ADC 控制寄存器(ADC_CONTR)”中“ADC_POWER”和“ADC_START”位控制,如下图所示。

ADC 控制寄存器(ADC_CONTR)

  1. ADC_POWER:ADC电源控制位
  1. 0:关闭 ADC 电源。
  2. 1:打开 ADC 电源。

注意事项:

  1. ADC在启动之前需要先上电,上电后需等待约1ms的时间,让MCU内部的ADC电源趋于稳定后再启动ADC工作。
  2. 适当加长对外部信号的采样时间,就是对 ADC 内部采样保持电容的充电或放电时间,时间够,内部才能和外部电势相等。
  3. 通过ADC电源控制位,我们可以在单片机进入空闲模式和掉电模式前将 ADC 电源关闭,当需要ADC工作的时候再打开电源,以降低功耗。
  1. ADC_START: ADC 转换启动控制位。写入 1 后开始 ADC 转换,转换完成后硬件自动将此位清零。
  1. 0:无影响。即使 ADC 已经开始转换工作,写 0 也不会停止 A/D 转换。
  2. 1:开始 ADC 转换,转换完成后硬件自动将此位清零。
      1. 读取ADC采样结果

当A/D 转换完成后,转换结果会自动保存到ADC 转换结果寄存器ADC_RES 和 ADC_RESL中。保存时会按照“ADC 配置寄存器(ADCCFG)”中的“RESFMT”位设置的数据格式保存,因此,我们在读取数据时,也要按照设置的格式获取数据。下面的代码是ADC数据格式为右对齐时的读取示例。

代码清单:读取ADC采样结果

  1. u8 adc_H,adc_L;   //分别保存读取的ADC结果的高字节和低字节数据
  2. u16 adc_value;    //保存ADC结果转换为16位的数据
  3.   
  4. adc_H = ADC_RES & 0x0F;   //ADC_RES寄存器中读取ADC结果的高字节数据,只有低4位有效
  5. adc_L = ADC_RESL;         //从ADC_RESL寄存器中读取ADC结果的低字节数据
  6.   
  7. adc_value = (u16)((adc_H<<8)+adc_L);  //将ADC结果转换位16位数据,以便于计算电压

读取ADC采样值后,即可计算电压值。开发板使用的是外部2.5V参考电压,采样值转换为电压的公式如下。

    1. 软件设计
      1. ADC采样电位器电压(查询方式)
  • 注:本节的实验是在“实验2-6-1:串口1数据收发实验”的基础上修改,本节对应的实验源码是:“实验2-11-1:ADC采样电位器电压(查询方式)”。
        1. 实验内容

使用ADC模拟通道输入14(即引脚P0.6)采样电位器抽头电压,程序中每500毫秒执行一次电压采样,采样结果计算为电压值后通过串口输出。

本例使用查询方式执行ADC采样,所谓查询方式即不开启中断,当ADC启动采样后,程序中反复查询ADC采样完成标志位,若该位置位,表示当前ADC采样完成,此时,可以读取ADC采样结果。

        1. 代码编写
  1. 新建一个名称为“adc.c”的文件及其头文件“adc.h”并保存到工程的“Source”文件夹,并将“adc.c”加入到Keil工程中的“SOURCE”组。
  2. 引用头文件

因为在“main.c”文件中使用了“adc.c”文件中的函数,所以需要引用下面的头文件“adc.h”。

代码清单:引用头文件

  1. //引用ADC的头文件  
  2. #include    " adc.h"  
  1. ADC初始化

ADC初始化通常包含以下部分内容:

  1. 通道选择:本例中使用ADC模拟通道输入14采样电位器抽头电压,ADC模拟通道输入14对应的I/O是P0.6,因此,需要选择P0.6作为模拟功能ADC使用。
  2. 时钟频率和内部时序:
  1. ADC通道选择时间设置为:占用 1个ADC 工作时钟。
  2. ADC通道选择保持时间设置为:占用 2个ADC 工作时钟。
  3. ADC模拟信号采样时间设置为:占用 32个ADC 工作时钟。
  4. ADC工作时钟频率设置为:SYSclk/2/16。
  1. ADC采样结果数据格式设置为:右对齐,即ADC_RES存高4位,ADC_RESL存低8位。
  2. ADC中断:本例使用查询方式,因此不开启ADC中断。
  3. ADC上电:ADC上电可以在ADC初始化中完成,也可以在需要的时候再执行,执行ADC上电操作后,切记,需要延时不小于1ms的时间以保证ADC电源稳定。

为了方便程序调用,我们将ADC初始化封装为名称为“adc_config()”的函数,该函数代码如下:

代码清单:ADC初始化函数

  1. /********************************************************************************** 
  2. 功能描述:初始化ADC 
  3. 参    数:无 
  4. 返 回 值:无 
  5. ***********************************************************************************/  
  6. void adc_config(void)         
  7. {  
  8.    P0M1 |= 0x40;P0M0 &= ~0x40; //设置P0.6为输入  
  9.    ADC_CONTR |= 0x0E;          //选择P0.6作为模拟功能ADC使用  
  10.    ADC_CONTR &= 0xFE;          //选择P0.6作为模拟功能ADC使用  
  11.       
  12.    //内部时序配置  
  13.    P_SW2 |= 0x80;              //将EAXFR位置1,允许访问扩展RAM区特殊功能寄存器(XFR) 
  14.    ADCTIM = 0x3f;              //设置ADC内部时序  
  15.    P_SW2 &= 0x7f;              //将EAXFR位置0,禁止访问XFR
  16.       
  17.    ADCCFG |= 0x0F;             //ADC工作时钟频率设置为 SYSclk/2/16  
  18.    ADC_CONTR &= 0xDF;          //清AD转换完成标志  
  19.       
  20.    EADC = 0;          //关闭 ADC 中断  
  21.    ADCCFG|=0x20;      //ADC转换结果格式设置为右对齐,即ADC_RES存高4位,ADC_RESL存低8位    
  22.    ADC_CONTR|=0x80;   //ADC上电  
  23.    delay_ms(2);       //ADC上电后,延时不小于1ms以保证ADC供电稳定  
  24. }  

  1. 启动ADC转换 

ADC初始化并上电完成后,即可启动ADC转换。ADC每次转换都需要启动一次,也就是每启动一次ADC转换,ADC就会执行一次转换,下次转换前需要再次启动。我们编写的启动ADC转换的函数代码如下。

代码清单:ADC启动函数

  1. /********************************************************************************** 
  2.  * 描  述 : 启动AD转换 
  3.  * 入  参 : 无 
  4.  * 返回值 : 无 
  5.  **********************************************************************************/  
  6. void adc_start(void)      
  7. {  
  8.    ADC_CONTR|=0x40;            //启动AD转换  
  9. }  
  1. 查询ADC转换是否完成

本例中,我们使用的查询的方式,因此在启动ADC采样之后,需要查询ADC_CONTR寄存器中的ADC_FLAG位(ADC 转换结束标志位),以此判断ADC是否转换完成,代码清单如下。

代码清单:查询ADC转换是否完成

  1. /********************************************************************************** 
  2.  * 描  述 : 查询ADC转换是否完成 
  3.  * 入  参 : 无 
  4.  * 返回值 : ADC转换完成-返回true,否则,返回false 
  5.  **********************************************************************************/  
  6. bool adc_completed(void)  
  7. {  
  8.    if((ADC_CONTR & 0x20) == 0x20)return true;  
  9.    else return false;  
  10. }  
  1. 读取ADC采样值

查询到ADC转换完成后,即可读取ADC转换结果,这里需要注意读取结果时需要结合ADC初始化时配置的采样结果数据格式(左对齐或右对齐),从采样结果中读出正确的采样数据。

代码清单:读取ADC采样值

  1. /********************************************************************************** 
  2.  * 描  述 : 读取ADC采样值 
  3.  * 入  参 : 无 
  4.  * 返回值 : 读取的ADC采样值 
  5.  **********************************************************************************/  
  6. u16 get_adc_value(void)   
  7. {  
  8.    u8 adc_H,adc_L;  
  9.   
  10.    ADC_CONTR &= 0xDF;        //将ADC_FLAG清0  
  11.    ADC_CONTR &= 0xBF;        //关闭AD转换,ADC_START=0  
  12.    adc_H = ADC_RES & 0x0F;   //读取计数值  
  13.    adc_L = ADC_RESL;         //12位AD结果的高4位放ADC_RES的低4位,低8位在ADC_RESL  
  14.    ADC_CONTR|=0x40;          //启动AD转换,ADC_START=1  
  15.    return (adc_H<<8)+adc_L;  //返回读取的计数值    
  16. }  
  1. 主函数

主函数中调用ADC初始化完成ADC的初始化,之后在主循环中每500ms启动一次ADC转换,启动后一直查询ADC 转换结束标志位ADC_FLAG的值,直到ADC_FLAG置位,即ADC转换完成。此后,读取ADC采样值并将其计算为电压值通过串口输出。

代码清单:主函数

  1. /************************************************************************** 
  2. 功能描述:主函数 
  3. 入口参数:无 
  4. 返回值:int类型 
  5.  *************************************************************************/  
  6. int main(void)  
  7. {  
  8.    u16 adc_value;  //存放ADC采样值  
  9.    float voltage;  //存放ADC采样值计算后的电压值  
  10.       
  11.    P2M1 &= 0xBF;   P2M0 &= 0xBF;     //设置P2.6为准双向口(LED1)  
  12.    P3M1 &= 0xFE;   P3M0 &= 0xFE;     //设置P3.0为准双向口(UART RxD)  
  13.    P3M1 &= 0xFD;   P3M0 |= 0x02;     //设置P3.1为推挽输出(UART TxD)  
  14.       
  15.    uart1_init();      //串口1初始化  
  16.    adc_config();      //初始化ADC  
  17.       
  18.    while(1)  
  19.    {  
  20.       adc_start();        //启动ADC转换  
  21.       while(adc_completed() == false) ;    //等待ADC转换完成  
  22.       adc_value = get_adc_value();         //读取ADC采样值  
  23.       voltage   = (2.5*adc_value)/4096;    //将ADC采样值转换为电压(单位V)  
  24.       printf("voltage: %.2fV\r\n",voltage);//串口打印ADC采样电压  
  25.       delay_ms(500); //延时500ms,方便在串口调试助手中观察实验数据  
  26.    }  
        1. 硬件连接

本实验需要使用ADC模拟通道输入14(即引脚P0.6)采样电位器抽头电压,因此需要将P06引脚和电位器电路通过跳线帽连接,如下图所示。

【STC8A8K64D4开发板】第2-11讲:模数转换ADC_第6张图片

图2:跳线帽短接

        1. 实验步骤
  1. 解压“…\第3部分:配套例程源码”目录下的压缩文件“实验2-11-1:ADC采样电位器电压(查询方式)”,将解压后得到的文件夹拷贝到合适的目录,如“D\STC8”(这样做的目的是为了防止中文路径或者工程存放的路径过深导致打开工程出现问题)。
  2. 双击“…\adc\project”目录下的工程文件“adc.uvproj”。
  3. 点击编译按钮编译工程,编译成功后生成的HEX文件“adc.hex”位于工程的“…\adc\Project\Object”目录下。
  4. 打开STC-ISP软件下载程序,下载使用内部IRC时钟,IRC频率选择:24MHz。
  5. 电脑上打开串口调试助手,选择开发板对应的串口号,将波特率设置为9600bps。程序运行后,在串口接收窗口可以看到开发板上报的ADC采样的电压值,如下图所示。

【STC8A8K64D4开发板】第2-11讲:模数转换ADC_第7张图片

图3:串口调试助手收发数据

  1. 旋转电位器改变电位器抽头电压,观察串口接收的数据,可以看到电压值的变化。
      1. ADC采样电位器电压(中断方式)
  • 注:本节的实验是在“实验2-11-1:ADC采样电位器电压(查询方式)”的基础上修改,本节对应的实验源码是:“实验2-11-2:ADC采样电位器电压(中断方式)”。
        1. 实验内容

本实验所实现的功能和“实验13-1”完全一样,不同的地方在于本例中ADC采样使用了中断的方式。即程序中开启ADC中断,当启动ADC采样后,无需查询ADC转换完成标志位,ADC转换完成后会进入ADC中断服务函数,此时,我们即可读取ADC采样结果。

由此可见,中断方式的效率要高于查询方式,因为CPU在启动ADC采样后,可以继续去做其他事情,而无需像查询方式一样去反复查询ADC采样是否完成。当ADC采样完成后,会触发中断,以此通知CPU,ADC采样已经完成。

        1. 代码编写

本例中使用了ADC中断,因此,ADC初始化函数中需要开启ADC中断,代码清单如下所示。

代码清单:ADC初始化函数

  1. /********************************************************************************************** 
  2. 功能描述:初始化ADC 
  3. 参    数:无 
  4. 返 回 值:无 
  5. ***********************************************************************************************/  
  6. void adc_config(void)         
  7. {  
  8.    //省略了无关的代码  
  9.    EADC = 1;          //使能 ADC 中断,注意:开启ADC中断的情况下,还需要开启总中断“EA=1”,中断才能起作用  
  10.    //省略了无关的代码  
  11. }  

当ADC采样完成后,会进入中断服务函数,我们在ADC中断服务函数中读取ADC采样值即可。读出的采样值计算为电压值后,同样使用串口输出,代码清单如下。

代码清单:ADC中断服务函数

  1. /********************************************************************************** 
  2.  * 描  述 : ADC中断服务函数 
  3.  * 入  参 : 无 
  4.  * 返回值 : 无 
  5.  **********************************************************************************/  
  6. void adc_isr() interrupt 5  
  7. {  
  8.    u8 adc_H,adc_L;  
  9.    u16 adc_value;  //存放ADC采样值  
  10.    float voltage;  //存放ADC采样值计算后的电压值  
  11.       
  12.    ADC_CONTR &= ~0x20;                      //清零ADC中断标志  
  13.    adc_H = ADC_RES & 0x0F;                 //读取计数值  
  14.    adc_L = ADC_RESL;         //12位AD结果的高4位放ADC_RES的低4位,低8位在ADC_RESL  
  15.    adc_value = (adc_H<<8)+adc_L;           //读取ADC采样值  
  16.    voltage   = (2.5*adc_value)/4096;       //将ADC采样值转换为电压(单位V)  
  17.    printf("voltage: %.1fV\r\n",voltage);   //串口打印ADC采样电压  
  18. }  

主函数中开启总中断,之后,在主循环中每500ms启动一次ADC采样,即每500ms采样一次电位器抽头电压,代码清单如下。

代码清单:主函数

  1. /************************************************************************** 
  2. 功能描述:主函数 
  3. 入口参数:无 
  4. 返回值:int类型 
  5.  *************************************************************************/  
  6. int main(void)  
  7. {  
  8.    //省略了无关的代码  
  9.    adc_config();      //初始化ADC  
  10.    EA = 1;            //使能总中断  
  11.       
  12.    while(1)  
  13.    {  
  14.       adc_start();   //启动ADC转换  
  15.       delay_ms(500); //延时500ms,方便在串口调试助手中观察实验数据  
  16.    }  
  17. }  

        1. 硬件连接

同“实验2-11-1:ADC采样电位器电压(查询方式)”。

        1. 实验步骤

同“实验2-11-1:ADC采样电位器电压(查询方式)”。

      1. ADC多通道采样

STC8A8K64D4单片机的ADC有16个通道,但是ADC转换器只有一个,因此,ADC模块每次只能对一个通道进行转换,而不能同时对多个通道进行转换。但是在实际应用时,我们经常会用到多个ADC通道采样电压值,这时,我们可以使用轮询的方式对各个ADC通道进行采样,具体的实现流程如下图所示,即对当前ADC通道采样完成后,切换到下一个ADC通道,之后启动ADC转换并在转换完成后读取采样结果,如此轮询对使用的各个ADC通道采样,从而实现ADC多通道采样。

【STC8A8K64D4开发板】第2-11讲:模数转换ADC_第8张图片

图4:ADC多通道采样流程

  • 注:本节的实验是在“实验2-11-1:ADC采样电位器电压(查询方式)”的基础上修改,本节对应的实验源码是:“实验2-11-3:ADC多通道采样(2个通道)”。
        1. 实验内容

本实验对2个ADC通道进行采样:ADC模拟通道输入2(P1.2)和ADC模拟通道输入14(P0.6)。由于开发板上只有一个电位器,为了实验方便,两个通道做如下处理。

  1. ADC通道14对应的P0.6:连接到电位器抽头,在实验时,可以通过旋转电位器改变电位器抽头电压,从而可以观察到ADC采样的电压值的变化。
  2. ADC通道2对应的P1.2:用杜邦线连接到GND,ADC采样的电压值应接近于0V。

程序中使用轮询的方式对2个ADC通道进行采样,采样的结果计算为电压值后通过串口输出,在串口调试助手里面即可观察到ADC采样的电压值。

        1. 代码编写

本例中使用了2个ADC通道,因此需要修改ADC初始化函数,将两个ADC通道的I/O设置为高阻输入模式。另外,为了方便轮询ADC通道,在ADC初始化函数中不设置ADC通道,而是编写了一个专门用于设置ADC通道的函数“adc_set_channel()”用于设置ADC通道。修改后的ADC初始化函数代码清单如下。

代码清单:ADC初始化函数

  1. /********************************************************************************** 
  2. 功能描述:初始化ADC 
  3. 参    数:无 
  4. 返 回 值:无 
  5. ***********************************************************************************/  
  6. void adc_config(void)         
  7. {  
  8.    P0M1 |= 0x40;P0M0 &= ~0x40; //设置P0.6为高阻输入  
  9.    P1M1 |= 0x04;P1M0 &= ~0x04; //设置P1.2为高阻输入  
  10.   
  11.    //内部时序配置  
  12.    P_SW2 |= 0x80;              //将EAXFR位置1,允许访问扩展RAM区特殊功能寄存器(XFR) 
  13.    ADCTIM = 0x3f;              //设置ADC内部时序  
  14.    P_SW2 &= 0x7f;              //将EAXFR位置0,禁止访问XFR
  15.       
  16.    ADCCFG |= 0x0F;             //ADC工作时钟频率设置为 SYSclk/2/16  
  17.    ADC_CONTR &= 0xDF;          //清AD转换完成标志  
  18.       
  19.    EADC = 0;          //关闭 ADC 中断  
  20.    ADCCFG|=0x20;      //ADC转换结果格式设置为右对齐,即ADC_RES存高4位,ADC_RESL存低8位    
  21.    ADC_CONTR|=0x80;   //ADC上电  
  22.    delay_ms(2);       //ADC上电后,延时不小于1ms以保证ADC供电稳定  

用于设置ADC通道的函数“adc_set_channel()”的代码清单如下。

代码清单:ADC通道设置函数

  1. /********************************************************************************** 
  2. 功能描述:设置ADC通道。ADC共有16个通道,对应的通道号为0~15,其中,通道15是没有输入引脚的,
  3.          他只能用于检测内部参考信号源 
  4. 参    数:ch[in]:adc通道,取值范围为0~15 
  5. 返 回 值:无 
  6. ***********************************************************************************/  
  7. void adc_set_channel(u8 ch)       
  8. {     
  9.    ADC_CONTR &= ~0x0F;      //先清零ADC通道选择位  
  10.    ADC_CONTR |= ch & 0x0F;  //再设置ADC通道  

使用函数“adc_set_channel()”选择ADC通道后,即可执行ADC采样。这里,我们编写了一个阻塞式的ADC采样函数,将通道切换、启动ADC采样和数据读取封装在该函数中。代码清单如下,首先设置ADC通道,接着启动ADC采样并一直查询ADC转换完成标志直到ADC转换完成标志置位,之后读出采样结果将之计算为电压并通过串口输出, 

代码清单:ADC采样函数

  1. /********************************************************************************** 
  2.  * 描  述 : 对给定的ADC通道执行一次ADC转换,这里使用的是阻塞的方式,即启动转换后一直等待直到转换完成 
  3.  * 入  参 : ch[in]:adc通道,取值范围为0~15 
  4.  * 返回值 : 无 
  5.  **********************************************************************************/  
  6. void adc_block_sample(u8 ch)      
  7. {  
  8.    u16 adc_value;  //存放ADC采样值  
  9.    float voltage;  //存放ADC采样值计算后的电压值  
  10.       
  11.    adc_set_channel(ch);                  //设置ADC通道  
  12.    adc_start();                          //启动ADC转换  
  13.    while(adc_completed() == false);      //等待ADC转换完成  
  14.    adc_value = get_adc_value();          //读取ADC采样值  
  15.    voltage   = (2.5*adc_value)/4096;     //将ADC采样值转换为电压(单位V)  
  16.    printf("voltage: %.2fV\r\n",voltage);//串口打印ADC采样电压  
  17. }  

主函数中,先调用ADC初始化函数“adc_config()”初始化ADC,之后在主循环里面轮询对两个ADC通道进行采样,每次采样后演示500ms,这样做是为了方便观察实验数据。

对于多通道采样,需要特别注意的是:如果采样的频率比较快,则需要对ADC内部进行放电,即启动采样时先将ADC通道对应的I/O设置为开漏模式进行放电,接着再将其配置为高阻输入执行ADC采样。

代码清单:主函数

  1. /************************************************************************** 
  2. 功能描述:主函数 
  3. 入口参数:无 
  4. 返回值:int类型 
  5.  *************************************************************************/  
  6. int main(void)  
  7. {  
  8.    P2M1 &= 0xBF;   P2M0 &= 0xBF;     //设置P2.6为准双向口(LED1)  
  9.    P3M1 &= 0xFE;   P3M0 &= 0xFE;     //设置P3.0为准双向口(UART RxD)  
  10.    P3M1 &= 0xFD;   P3M0 |= 0x02;     //设置P3.1为推挽输出(UART TxD)  
  11.       
  12.    uart1_init();      //串口1初始化  
  13.    adc_config();      //初始化ADC  
  14.           
  15.    while(1)  
  16.    {  
  17.       /*--如果ADC采样的速度比较快,加上下面的两条语句,可以提高采样精度--*/  
  18.       //P0M1 |= 0x40;P0M0 |= 0x40;  //设置P0.6为开漏输入,目的是给ADC内部放电  
  19.       //P0M1 |= 0x40;P0M0 &= ~0x40; //设置P0.6为高阻输入  
  20.       adc_block_sample(12);         //对ADC通道12采样一次  
  21.       delay_ms(500);//延时500ms,方便在串口调试助手中观察实验数据  
  22.           
  23.       /*--如果ADC采样的速度比较快,加上下面的两条语句,可以提高采样精度--*/  
  24.       //P1M1 |= 0x04;P1M0 |= 0x04;  //设置P1.2为开漏输入,目的是给ADC内部放电  
  25.       //P1M1 |= 0x04;P1M0 &= ~0x04; //设置P1.2为高阻输入  
  26.       adc_block_sample(2);          //对ADC通道2采样一次  
  27.       delay_ms(500); //延时500ms,方便在串口调试助手中观察实验数据  
  28.    }  
  29. }  
        1. 硬件连接

本实验需要使用ADC模拟输入通道14(即引脚P0.6)采样电位器抽头电压和ADC模拟输入通道2(P1.2)采样GND电压,因此需要将J27端子的P06和电位器电路(ADC)通过跳线帽连接,并用杜邦线将J9端子的P12连接到J18端子的GND,如下图所示

图5:硬件连接

        1. 实验步骤
  1. 解压“…\第3部分:配套例程源码”目录下的压缩文件“实验2-11-3:ADC多通道采样(2个通道)”,将解压后得到的文件夹拷贝到合适的目录,如“D\STC8”(这样做的目的是为了防止中文路径或者工程存放的路径过深导致打开工程出现问题)。
  2. 双击“…\adc_muti_channel\project”目录下的工程文件“adc.uvproj”。
  3. 点击编译按钮编译工程,编译成功后生成的HEX文件“adc.hex”位于工程的“…\adc\Project\Object”目录下。
  4. 打开STC-ISP软件下载程序,下载使用内部IRC时钟,IRC频率选择:24MHz。
  5. 电脑上打开串口调试助手,选择开发板对应的串口号,将波特率设置为9600bps。程序运行后,在串口接收窗口可以看到开发板上报的ADC采样的电压值,如下图所示。

【STC8A8K64D4开发板】第2-11讲:模数转换ADC_第9张图片

图6:串口调试助手收发数据

  1. 旋转电位器改变电位器抽头电压,观察串口接收的数据,可以看到电压值的变化(其中一路电压的值一直为0,是因为实验时将通道2的I/O P1.2用杜邦线连接到了GND)。

你可能感兴趣的:(STC8,单片机)