zoukankan      html  css  js  c++  java
  • STC8H开发(三): 基于FwLib_STC8的模数转换ADC介绍和演示用例说明

    目录

    前面介绍了在Keil5和PlatformIO环境下使用FwLib_STC8, 接下来以STC8H系列为主, 结合demo中的演示用例介绍ADC(模数转换)

    STC8G和STC8H的ADC模数转换

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

    通道数量和精度

    对应STC8G/STC8H的各个系列的通道数量和精度如下.

    产品线 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的各个系列, 这个寄存器的数值对应的通道如下

    STC8H1K28 STC8H1K08 STC8H3K64S4
    STC8H3K64S2
    STC8H8K64U
    STC8H2K64T
    STC8H4K64TLR
    STC8H4K64TLCD
    STC8H4K64LCD
    STC8G1K08A STC8G1K08
    STC8G1K08T
    STC8G2K64S4
    STC8G2K64S2
    0000 P1.0/ADC0 P1.0/ADC0 P1.0/ADC0 P1.0/ADC0 P1.0/ADC0 P3.0/ADC0 P1.0/ADC0 P1.0/ADC0
    0001 P1.1/ADC1 P1.1/ADC1 P1.1/ADC1 P1.1/ADC1 P1.1/ADC1 P3.1/ADC1 P1.1/ADC1 P1.1/ADC1
    0010 P1.2/ADC2 N/A P1.2/ADC2 P5.4/ADC2 P5.4/ADC2 P3.2/ADC2 P1.2/ADC2 P1.2/ADC2
    0011 P1.3/ADC3 N/A N/A P1.3/ADC3 P1.3/ADC3 P3.3/ADC3 P1.3/ADC3 P1.3/ADC3
    0100 P1.4/ADC4 N/A N/A P1.4/ADC4 P1.4/ADC4 P5.4/ADC4 P1.4/ADC4 P1.4/ADC4
    0101 P1.5/ADC5 N/A N/A P1.5/ADC5 P1.5/ADC5 P5.5/ADC5 P1.5/ADC5 P1.5/ADC5
    0110 P1.6/ADC6 N/A P1.6/ADC6 P1.6/ADC6 P6.2/ADC6 N/A P1.6/ADC6 P1.6/ADC6
    0111 P1.7/ADC7 N/A P1.7/ADC7 P1.7/ADC7 P6.3/ADC7 N/A P1.7/ADC7 P1.7/ADC7
    1000 P0.0/ADC8 P3.0/ADC8 P0.0/ADC8 P0.0/ADC8 P0.0/ADC8 N/A P3.0/ADC8 P0.0/ADC8
    1001 P0.1/ADC9 P3.1/ADC9 P0.1/ADC9 P0.1/ADC9 P0.1/ADC9 N/A P3.1/ADC9 P0.1/ADC9
    1010 P0.2/ADC10 P3.2/ADC10 P0.2/ADC10 P0.2/ADC10 P0.2/ADC10 N/A P3.2/ADC10 P0.2/ADC10
    1011 P0.3/ADC11 P3.3/ADC11 P0.3/ADC11 P0.3/ADC11 P0.3/ADC11 N/A P3.3/ADC11 P0.3/ADC11
    1100 N/A P3.4/ADC12 P0.4/ADC12 P0.4/ADC12 P0.4/ADC12 N/A P3.4/ADC12 P0.4/ADC12
    1101 N/A P3.5/ADC13 P0.5/ADC13 P0.5/ADC13 P0.5/ADC13 N/A P3.5/ADC13 P0.5/ADC13
    1110 N/A P3.6/ADC14 P0.6/ADC14 P0.6/ADC14 P0.6/ADC14 N/A P3.6/ADC14 P0.6/ADC14
    1111 1.19Vref 1.19Vref 1.19Vref 1.19Vref 1.19Vref 1.19Vref 1.19Vref 1.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, 连线为

       AGrnd   -> GND
       AVcc    -> VCC
       AVref   -> VCC 
       Vcc     -> VCC
       Gnd     -> GND
       ADC1    -> 采样点
    

    不带 AVcc,AGrnd

    低端型号以及STC8G系列不带这两个pin, 只需要接AVref, 采样点与MCU共地连接, 连线为

       AVref   -> VCC 
       Vcc     -> VCC
       Gnd     -> GND
       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代码

    #include "fw_hal.h"
    
    void main(void)
    {
        uint8_t res;
        // 调整系统频率, 如果使用STC-ISP设定频率, 需要将这行注释掉
        SYS_SetClock();
        // 用于结果输出
        UART1_Config8bitUart(UART1_BaudSource_Timer2, HAL_State_ON, 115200);
        // 将 ADC1(GPIO P1.1) 设为高阻输入
        GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_Input_HIP);
        // 使用通道: ADC1
        ADC_SetChannel(0x01);
        // 设置ADC时钟 = SYSCLK / 2 / (1+1) = SYSCLK / 4
        ADC_SetClockPrescaler(0x01);
        // 设置结果左对齐, 只需要取值 ADC_RES
        ADC_SetResultAlignmentLeft();
        // 开启ADC电源
        ADC_SetPowerState(HAL_State_ON);
    
        while(1)
        {
            // 开始转换
            ADC_Start();
            // 等待两个系统时钟
            NOP();
            NOP();
            // 检查转换结果标志位是否置位
            while (!ADC_SamplingFinished());
            // 清除结果标志位
            ADC_ClearInterrupt();
            // 读取结果
            res = ADC_RES;
    
            // 通过串口1输出
            UART1_TxString("Result: ");
            UART1_TxHex(res);
            UART1_TxString("\r\n");
            // 等待100ms后再次进行转换
            SYS_Delay(100);
        }
    }
    

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

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

    #include "fw_hal.h"
    
    // 16位变量用于记录转换结果
    uint16_t res;
    
    // 处理中断的方法, 使用宏定义保证Keil C51和SDCC的兼容性
    INTERRUPT(ADC_Routine, EXTI_VectADC)
    {
        // 先清除中断位
        ADC_ClearInterrupt();
        // 结果低8位
        res = ADC_RESL;
        // 结果高8位
        res |= (ADC_RES & 0x0F) << 8;
        // 再次启动, 使得ADC连续转换, 
        ADC_Start();
    }
    
    void main(void)
    {
        // 设置系统频率
        SYS_SetClock();
        // 结果输出
        UART1_Config8bitUart(UART1_BaudSource_Timer2, HAL_State_ON, 115200);
        // 设置P11高阻输入模式
        GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_Input_HIP);
        // 使用通道: ADC1
        ADC_SetChannel(0x01);
        // ADC时钟 = SYSCLK / 2 / (1+15) = SYSCLK / 32
        ADC_SetClockPrescaler(0x0F);
        // 右对齐, 方便转换为双字节的结果
        ADC_SetResultAlignmentRight();
        // 开启全局中断和ADC中断
        EXTI_Global_SetIntState(HAL_State_ON);
        EXTI_ADC_SetIntState(HAL_State_ON);
        // 开启ADC电源
        ADC_SetPowerState(HAL_State_ON);
        // 开始ADC转换
        ADC_Start();
    
        while(1)
        {
            // 转换结果输出
            UART1_TxString("Result: ");
            UART1_TxHex(res >> 8);
            UART1_TxHex(res & 0xFF);
            UART1_TxString("\r\n");
            SYS_Delay(100);
        }
    }
    

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

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

    #include "fw_hal.h"
    
    // 用于记录当前采样的通道编号
    uint8_t pos;
    // 记录各通道的采样结果
    uint16_t res[2];
    
    // 中断处理方法
    INTERRUPT(ADC_Routine, EXTI_VectADC)
    {
        ADC_ClearInterrupt();
        // 记录采样结果
        res[pos] = ADC_RESL;
        res[pos] |= (ADC_RES & 0x0F) << 8;
        
        // 切换到下一个通道
        pos = (pos+1) & 0x1;
        if (pos == 0)
        {
            /**
             * 在采样频率较高时, 加上这两句能提高精度. 其机制是切换到开漏模式清除采样口上的残留电压
            GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_InOut_OD);
            GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_Input_HIP);
            */
            ADC_SetChannel(0x01);
        }
        else
        {
            /**
             * Uncomment these lines in high speed ADC
            GPIO_P1_SetMode(GPIO_Pin_2, GPIO_Mode_InOut_OD);
            GPIO_P1_SetMode(GPIO_Pin_2, GPIO_Mode_Input_HIP);
            */
            ADC_SetChannel(0x02);
        }
        ADC_Start();
    }
    
    // 下面的代码和前面的基本上是一样的, 就不详细注释了
    void main(void)
    {
        SYS_SetClock();
        // For debug print
        UART1_Config8bitUart(UART1_BaudSource_Timer2, HAL_State_ON, 115200);
        // Channel: ADC1
        ADC_SetChannel(0x01);
        // ADC Clock = SYSCLK / 2 / (1+15) = SYSCLK / 32
        ADC_SetClockPrescaler(0x0F);
        // Right alignment, high 2-bit in ADC_RES, low 8-bit in ADC_RESL
        ADC_SetResultAlignmentRight();
        // Enable interrupts
        EXTI_Global_SetIntState(HAL_State_ON);
        EXTI_ADC_SetIntState(HAL_State_ON);
        // Turn on ADC power
        ADC_SetPowerState(HAL_State_ON);
        // Set ADC1(P1.1), ADC2(P1.2) HIP
        GPIO_P1_SetMode(GPIO_Pin_1|GPIO_Pin_2, GPIO_Mode_Input_HIP);
        // Start ADC
        ADC_Start();
    
        while(1)
        {
            UART1_TxString("Result: ");
            UART1_TxHex(res[0] >> 8);
            UART1_TxHex(res[0] & 0xFF);
            UART1_TxChar(' ');
            UART1_TxHex(res[1] >> 8);
            UART1_TxHex(res[1] & 0xFF);
            UART1_TxString("\r\n");
            SYS_Delay(100);
        }
    }
    

    结束

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

  • 相关阅读:
    五角星评分小例子
    手风琴案列
    隔行变色
    全选和单选(有一个单选没有打钩,全选也自动不打钩)
    随机数封装
    数组的升降序排列
    js面向对象倒计时与文字左右滚动
    linux知识点总结与随笔(关注linux爱好者公众号的一些笔记)
    线程与进程(我的理解)
    @property在python类中的应用
  • 原文地址:https://www.cnblogs.com/milton/p/15782407.html
Copyright © 2011-2022 走看看