找回密码
 立即注册

QQ登录

只需一步,快速开始

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

基于FwLib_STC8的模数转换ADC介绍和演示用例说明

[复制链接]
跳转到指定楼层
楼主
ID:912806 发表于 2022-1-10 01:04 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 michaelchain 于 2022-1-10 01:10 编辑

STC8G和STC8H的ADC部分在寄存器设置上基本上一致, 但是不同型号对应的通道编号, 通道数量和精度有区别

通道数量和精度
对应STC8G/STC8H的各个系列的通道数量和精度如下.
[td]
产品线ADC 分辨率ADC 通道数
STC8H1K08 系列10 位9 通道
STC8H1K28 系列10 位12 通道
STC8H3K64S4 系列12 位12 通道
STC8H3K64S2 系列12 位12 通道
STC8H8K64U 系列12 位15 通道
STC8H2K64T 系列12 位15 通道
STC8H4K64TLR 系列12 位15 通道
STC8H4K64TLCD 系列12 位15 通道
STC8H4K64LCD 系列12 位15 通道

通道的选择使用寄存器ADC_CONTR的低4位, 对应STC8G/STC8H的各个系列, 这个寄存器的数值对应的通道如下
[td]
STC8H1K28STC8H1K08STC8H3K64S4
STC8H3K64S2
STC8H8K64U
STC8H2K64T
STC8H4K64TLR
STC8H4K64TLCD
STC8H4K64LCD
STC8G1K08ASTC8G1K08
STC8G1K08T
STC8G2K64S4
STC8G2K64S2
0000P1.0/ADC0P1.0/ADC0P1.0/ADC0P1.0/ADC0P1.0/ADC0P3.0/ADC0P1.0/ADC0P1.0/ADC0
0001P1.1/ADC1P1.1/ADC1P1.1/ADC1P1.1/ADC1P1.1/ADC1P3.1/ADC1P1.1/ADC1P1.1/ADC1
0010P1.2/ADC2N/AP1.2/ADC2P5.4/ADC2P5.4/ADC2P3.2/ADC2P1.2/ADC2P1.2/ADC2
0011P1.3/ADC3N/AN/AP1.3/ADC3P1.3/ADC3P3.3/ADC3P1.3/ADC3P1.3/ADC3
0100P1.4/ADC4N/AN/AP1.4/ADC4P1.4/ADC4P5.4/ADC4P1.4/ADC4P1.4/ADC4
0101P1.5/ADC5N/AN/AP1.5/ADC5P1.5/ADC5P5.5/ADC5P1.5/ADC5P1.5/ADC5
0110P1.6/ADC6N/AP1.6/ADC6P1.6/ADC6P6.2/ADC6N/AP1.6/ADC6P1.6/ADC6
0111P1.7/ADC7N/AP1.7/ADC7P1.7/ADC7P6.3/ADC7N/AP1.7/ADC7P1.7/ADC7
1000P0.0/ADC8P3.0/ADC8P0.0/ADC8P0.0/ADC8P0.0/ADC8N/AP3.0/ADC8P0.0/ADC8
1001P0.1/ADC9P3.1/ADC9P0.1/ADC9P0.1/ADC9P0.1/ADC9N/AP3.1/ADC9P0.1/ADC9
1010P0.2/ADC10P3.2/ADC10P0.2/ADC10P0.2/ADC10P0.2/ADC10N/AP3.2/ADC10P0.2/ADC10
1011P0.3/ADC11P3.3/ADC11P0.3/ADC11P0.3/ADC11P0.3/ADC11N/AP3.3/ADC11P0.3/ADC11
1100N/AP3.4/ADC12P0.4/ADC12P0.4/ADC12P0.4/ADC12N/AP3.4/ADC12P0.4/ADC12
1101N/AP3.5/ADC13P0.5/ADC13P0.5/ADC13P0.5/ADC13N/AP3.5/ADC13P0.5/ADC13
1110N/AP3.6/ADC14P0.6/ADC14P0.6/ADC14P0.6/ADC14N/AP3.6/ADC14P0.6/ADC14
11111.19Vref1.19Vref1.19Vref1.19Vref1.19Vref1.19Vref1.19Vref1.19Vref

转换结果的对齐格式

ADC采样的精度实际上是不能设置的, 采样都是用的当前型号的最大精度, 结果存储在[ADC_RES, ADC_RESL]这两个寄存器. 为方便不同场合使用不同精度的结果, 可以将结果设置为左对齐或右对齐.
  • 当设置为左对齐时, 可以只取ADC_RES的值(8位), 忽略最后两位.
  • 当设置位右对齐时, 根据实际的精度, 可以取ADC_RES的低4位(12位精度)或低2位(10位精度), 加上ADC_RESL得到最终结果.


转换的时间消耗

一个完整的 ADC 转换时间为 = Tsetup + Tduty + Thold + Tconvert
  • Tsetup: 转换的通道切换时间, 可以设置为1个或2个ADC时钟周期
  • Tduty: 转换的采样时间, 默认是最低的11个ADC时钟, 最高为32个ADC时钟周期
  • Thold: 通道选择的保持时间, 可以选择1, 2, 3, 4个ADC时钟周期
  • Tconvert: 转换时间是固定的, 10bit精度是10个ADC时钟, 12bit精度是12个ADC时钟


以上的时间单位都是ADC时钟周期, 每个ADC时钟周期占用系统时钟(SYSCLK)的数量是可以设置的, 使用ADCCFG寄存器的低三位, 可以设置为最低2个系统时钟周期到最高32个系统时钟周期
对于转换的最高频率, DS上写了全局限制

  • 10 位 ADC 的速度不能高于 500KHz
  • 12 位 ADC 的速度不能高于 800KHz
  • 转换的采样时间不能小于 10,建议设置为 15



硬件连线

STC8G/STC8H的ADC硬件连线有两种: 带AVcc,AGrnd和不带AVcc,AGrnd

带 AVcc,AGrnd

高端型号STC8H3K64S2系列, 例如会带这两个pin脚, 分别对应的是转换目标的电压参考值和对地参考值. 对于普通使用, 这两个可以直接接到VCC和GND, 连线为
  1.    AGrnd   -> GND
  2.    AVcc    -> VCC
  3.    AVref   -> VCC
  4.    Vcc     -> VCC
  5.    Gnd     -> GND
  6.    ADC1    -> 采样点
复制代码

不带 AVcc,AGrnd

低端型号以及STC8G系列不带这两个pin, 只需要接AVref, 采样点与MCU共地连接, 连线为
  1.    AVref   -> VCC
  2.    Vcc     -> VCC
  3.    Gnd     -> GND
  4.    ADC1    -> Test voltage
复制代码

演示用例说明

以下演示用例, 基于 FwLib_STC8, 源代码位于 FwLib_STC8/demo/adc 目录, 可以自行下载或查看. 因为版本演变, 代码可能与仓库中的代码有出入, 以仓库中的最新版本为准.

关于如何运行演示用例, 可以参考前面介绍的Keil C51和VSCode PlatformIO的配置说明

使用ADC1进行8位ADC转换, 主动查询(polling)方式

下面的例子, 使用主动查询的方式每隔0.1秒对P1.1口进行ADC转换, 精度8位, 将结果输出至串口
main.c代码
  1. #include "fw_hal.h"

  2. void main(void)
  3. {
  4.     uint8_t res;
  5.     // 调整系统频率, 如果使用STC-ISP设定频率, 需要将这行注释掉
  6.     SYS_SetClock();
  7.     // 用于结果输出
  8.     UART1_Config8bitUart(UART1_BaudSource_Timer2, HAL_State_ON, 115200);
  9.     // 将 ADC1(GPIO P1.1) 设为高阻输入
  10.     GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_Input_HIP);
  11.     // 使用通道: ADC1
  12.     ADC_SetChannel(0x01);
  13.     // 设置ADC时钟 = SYSCLK / 2 / (1+1) = SYSCLK / 4
  14.     ADC_SetClockPrescaler(0x01);
  15.     // 设置结果左对齐, 只需要取值 ADC_RES
  16.     ADC_SetResultAlignmentLeft();
  17.     // 开启ADC电源
  18.     ADC_SetPowerState(HAL_State_ON);

  19.     while(1)
  20.     {
  21.         // 开始转换
  22.         ADC_Start();
  23.         // 等待两个系统时钟
  24.         NOP();
  25.         NOP();
  26.         // 检查转换结果标志位是否置位
  27.         while (!ADC_SamplingFinished());
  28.         // 清除结果标志位
  29.         ADC_ClearInterrupt();
  30.         // 读取结果
  31.         res = ADC_RES;

  32.         // 通过串口1输出
  33.         UART1_TxString("Result: ");
  34.         UART1_TxHex(res);
  35.         UART1_TxString("\r\n");
  36.         // 等待100ms后再次进行转换
  37.         SYS_Delay(100);
  38.     }
  39. }
复制代码


使用ADC1进行10位/12位ADC转换, 中断(interrupt)方式

下面的例子, 使用中断的方式对P1.1口进行ADC连续转换, 精度10位(或12位, MCU型号不同精度不同), 每隔0.1秒将结果输出至串口
  1. #include "fw_hal.h"

  2. // 16位变量用于记录转换结果
  3. uint16_t res;

  4. // 处理中断的方法, 使用宏定义保证Keil C51和SDCC的兼容性
  5. INTERRUPT(ADC_Routine, EXTI_VectADC)
  6. {
  7.     // 先清除中断位
  8.     ADC_ClearInterrupt();
  9.     // 结果低8位
  10.     res = ADC_RESL;
  11.     // 结果高8位
  12.     res |= (ADC_RES & 0x0F) << 8;
  13.     // 再次启动, 使得ADC连续转换,
  14.     ADC_Start();
  15. }

  16. void main(void)
  17. {
  18.     // 设置系统频率
  19.     SYS_SetClock();
  20.     // 结果输出
  21.     UART1_Config8bitUart(UART1_BaudSource_Timer2, HAL_State_ON, 115200);
  22.     // 设置P11高阻输入模式
  23.     GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_Input_HIP);
  24.     // 使用通道: ADC1
  25.     ADC_SetChannel(0x01);
  26.     // ADC时钟 = SYSCLK / 2 / (1+15) = SYSCLK / 32
  27.     ADC_SetClockPrescaler(0x0F);
  28.     // 右对齐, 方便转换为双字节的结果
  29.     ADC_SetResultAlignmentRight();
  30.     // 开启全局中断和ADC中断
  31.     EXTI_Global_SetIntState(HAL_State_ON);
  32.     EXTI_ADC_SetIntState(HAL_State_ON);
  33.     // 开启ADC电源
  34.     ADC_SetPowerState(HAL_State_ON);
  35.     // 开始ADC转换
  36.     ADC_Start();

  37.     while(1)
  38.     {
  39.         // 转换结果输出
  40.         UART1_TxString("Result: ");
  41.         UART1_TxHex(res >> 8);
  42.         UART1_TxHex(res & 0xFF);
  43.         UART1_TxString("\r\n");
  44.         SYS_Delay(100);
  45.     }
  46. }
复制代码

使用ADC1, ADC2双通道进行转换, 中断(interrupt)方式

下面介绍一个更实用的例子, 中断形式进行多通道ADC转换, 可以用于无线小车遥控, 双声道音频采样等
  1. #include "fw_hal.h"

  2. // 用于记录当前采样的通道编号
  3. uint8_t pos;
  4. // 记录各通道的采样结果
  5. uint16_t res[2];

  6. // 中断处理方法
  7. INTERRUPT(ADC_Routine, EXTI_VectADC)
  8. {
  9.     ADC_ClearInterrupt();
  10.     // 记录采样结果
  11.     res[pos] = ADC_RESL;
  12.     res[pos] |= (ADC_RES & 0x0F) << 8;
  13.    
  14.     // 切换到下一个通道
  15.     pos = (pos+1) & 0x1;
  16.     if (pos == 0)
  17.     {
  18.         /**
  19.          * 在采样频率较高时, 加上这两句能提高精度. 其机制是切换到开漏模式清除采样口上的残留电压
  20.         GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_InOut_OD);
  21.         GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_Input_HIP);
  22.         */
  23.         ADC_SetChannel(0x01);
  24.     }
  25.     else
  26.     {
  27.         /**
  28.          * Uncomment these lines in high speed ADC
  29.         GPIO_P1_SetMode(GPIO_Pin_2, GPIO_Mode_InOut_OD);
  30.         GPIO_P1_SetMode(GPIO_Pin_2, GPIO_Mode_Input_HIP);
  31.         */
  32.         ADC_SetChannel(0x02);
  33.     }
  34.     ADC_Start();
  35. }

  36. // 下面的代码和前面的基本上是一样的, 就不详细注释了
  37. void main(void)
  38. {
  39.     SYS_SetClock();
  40.     // For debug print
  41.     UART1_Config8bitUart(UART1_BaudSource_Timer2, HAL_State_ON, 115200);
  42.     // Channel: ADC1
  43.     ADC_SetChannel(0x01);
  44.     // ADC Clock = SYSCLK / 2 / (1+15) = SYSCLK / 32
  45.     ADC_SetClockPrescaler(0x0F);
  46.     // Right alignment, high 2-bit in ADC_RES, low 8-bit in ADC_RESL
  47.     ADC_SetResultAlignmentRight();
  48.     // Enable interrupts
  49.     EXTI_Global_SetIntState(HAL_State_ON);
  50.     EXTI_ADC_SetIntState(HAL_State_ON);
  51.     // Turn on ADC power
  52.     ADC_SetPowerState(HAL_State_ON);
  53.     // Set ADC1(P1.1), ADC2(P1.2) HIP
  54.     GPIO_P1_SetMode(GPIO_Pin_1|GPIO_Pin_2, GPIO_Mode_Input_HIP);
  55.     // Start ADC
  56.     ADC_Start();

  57.     while(1)
  58.     {
  59.         UART1_TxString("Result: ");
  60.         UART1_TxHex(res[0] >> 8);
  61.         UART1_TxHex(res[0] & 0xFF);
  62.         UART1_TxChar(' ');
  63.         UART1_TxHex(res[1] >> 8);
  64.         UART1_TxHex(res[1] & 0xFF);
  65.         UART1_TxString("\r\n");
  66.         SYS_Delay(100);
  67.     }
  68. }
复制代码


结束


以上就是STC8H使用FwLib_STC8封装库进行ADC转换的演示用例说明. 在实际使用中, 主动查询(polling)方式下的延时时间精度不高,
如果对采样的时间间隔精度有要求, 建议使用中断的形式.




评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

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

使用道具 举报

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

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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