RISCV_ADCLP使用参考


REVISION HISTORY

Revision No.
Description
Date
1.0
  • Initial release
  • 09/06/2024
    1.1
  • 完善硬件功能和FAQ描述
  • 04/14/2025
    1.2
  • 增加通道模式的说明
  • 06/13/2025

    1. 概述

    逐次逼近型模数转换器(Successive Approximation ADC)采用的是一种反馈比较型电路结构。实现方式简要概述为:取一个数字量加到DAC上,可得到一个对应的输出模拟电压,将这个模拟电压和输入的模拟电压信号相比较,如果两者不相等,则调整所取的数字量,直到两个模拟电压相等为止,最后所取的这个数字量就是所求的转换结果。

    2. 关键字说明

    • ADCLP

      Analog-to-digital converter Low Precision,低精度(10bit)模数转换器

    • ADCMP

      Analog-to-digital converter Medium Precision,中精度(12bit)模数转换器

    • upper/lower bound

      ADCLP可设置外部输入电压数字量的上下限,超出范围则触发中断

    • 基准电压

      用于模数转换计算时使用的参考电压,也是最大量程,若基准电压设定为1.8v,当外部输入电压>=1.8v时,数字量达到最大值1023

    3. 功能描述

    3.1. 硬件功能说明

    • SAR ADCLP共计5个Channel可支持外部输入电压的模数转换

    • 采样精度为10bit,因此获取到的寄存器数值范围在0~0x3ff之间

    • 支持两个档位的基准电压(即满量程)切换,分别为1.8V和1.0V

    • 采样频率 = 12Mhz / 104 = 115384hz(相当于硬件上每点的采样间隔为8667ns)

    • 通道模式可选择1 channel / 4 channel / 8 channel / 9 channel,不同模式决定了数据更新的时间间隔,如果选用1 channel模式,说明硬件只启用了1个通道,ADC数据更新的时间间隔为8667ns,如果选用4 channel模式,ADC数据更新的时间间隔为(8667ns * 4)

    • 支持外部输入电压的阈值设定,当电压超出阈值范围后,会触发中断,硬件中断的响应时间(不包含软件处理时间)会受到ADC数据更新的时间间隔影响,如果选用1 channel模式,硬件中断的响应时间至少大于8667ns,如果选用4 channel模式,硬件中断的响应时间至少大于(8667ns * 4)

    3.2. 计算说明

    SAR ADCLP的主要功能是将模拟信号转换为相应的数字信号,即可以将输入电压转换为数字量存储于寄存器中,通过公式计算出输入电压,

    计算公式:电压 = ( 寄存器数值 / 满量程 )* 基准电压

    即如果读到的数值是0x1D2,可得电压为0x1D2/0x3FF *1.8=0.82v左右

    4. 硬件连接介绍

    如下图,可将外部电压接入引脚PM_SAR_GPIO0~PM_SAR_GPIO4

    5. RTOS用法介绍

    5.1. DRIVER PATH

    sc/driver/sysdriver/saradc/os/adclp_os.h
    sc/driver/sysdriver/saradc/drv/pub/drv_adclp.h
    sc/driver/sysdriver/saradc/drv/src/drv_adclp.c
    sc/driver/sysdriver/saradc/drv/src/drv_adclp_test.c
    sc/driver/sysdriver/saradc/hal/chipname/src/hal_adclp.c
    sc/driver/sysdriver/saradc/hal/chipname/inc/hal_adclp.h
    sc/driver/sysdriver/saradc/hal/chipname/inc/hal_adclp_cfg.h
    

    5.2. CONFIG配置

    config文件位于mak/options_chipname_riscv_isw.mak,使能CONFIG_SARADC_SUPPORT

    # Feature_Name = [DRV] SARADC driver support
    # Description = SARADC driver support
    # Option_Selection = TRUE, FALSE
    CONFIG_SARADC_SUPPORT = TRUE
    

    5.3. SYSDESC配置

    chipname_xxx.sys文件位于sc/driver/sysdriver/sysdesc/hal/chipname/pub

    <adclp0>
        [reg_u32] 0x2002800;
        [interrupts_u32] INT_PM_IRQ_SAR_KP;
        [camclk_u16] CAMCLK_sar;
        [interrupts_en_u8] 1;
        [ref_vol_u32] 1800;
        [upper_bound_u16] 0x3FF;
        [lower_bound_u16] 0;
        [status_u8] 0;
    
    属性 描述 设定值 备注
    reg_u32 设定adclp bank地址 0x2002800 禁止修改
    interrupts_u32 设定硬件中断号 INT_PM_IRQ_SAR_KP 禁止修改
    camclk_u16 设定时钟源 CAMCLK_sar 禁止修改
    interrupts_en_u8 使能中断 1:enable, 2:disable 可根据需要修改
    ref_vol_u32 设定基准电压档位 以mv为单位,支持1800mv和1000mv 可根据需要修改
    upper_bound_u16 设定阈值上限电压 0~0x3FF,使能中断有效 可根据需要修改
    lower_bound_u16 设定阈值下限电压 0~0x3FF,使能中断有效 可根据需要修改
    status_u8 是否adclp使能驱动 1:enable, 2:disable 可根据需要修改

    adclp使能的通道数量决定硬件的通道模式:

    • 仅使能任意一个通道时,硬件使用1 channel模式

    • 使能多个通道时,节点名称"adclpX"中X的最大值 < 4,硬件使用4 channel模式

    • 使能多个通道时,节点名称"adclpX"中X的最大值 >= 4,硬件使用8 channel模式

    5.4. PADMUX设定

    SAR-ADCLP无需进行padmux的配置,PIN脚默认配置为ADC采样功能

    5.5. Sample Code

    demo源码位于sc/driver/sysdriver/saradc/drv/src/drv_adclp_test.c

    #define ADC_CHANNEL_NUM 8
    static adclp_cb_t cb_t[ADC_CHANNEL_NUM]       = {0};
    static u8         adclp_init[ADC_CHANNEL_NUM] = {0};
    
    /* 回调函数,当电压超出阈值范围后该函数会被调用 */
    int adclp_get_data(u8 channel)
    {
        u16 data;
    
        drv_adclp_get_data(channel, &data);
        cliPrintf("adclp%hhu data[%hu] exceeding bound\n", channel, data);
    
        return 0;
    }
    
    static int adclp_test(CLI_t *cli, char *p)
    {
        u8    i;
        int   ret;
        char *cmd;
        u16   data;
        u8    argc;
        u32   channel;
    
        argc = CliTokenCount(cli);
        if (argc < 1)
            goto adclp_help_exit;
    
        cmd = CliTokenPop(cli);
        if (strcmp(cmd, "init") == 0)
        {
            argc = CliTokenCount(cli);
            if (argc != 1)
                goto adclp_help_exit;
    
            if (CliTokenPopNum(cli, &channel, 0) != eCLI_PARSE_OK)
                goto adclp_help_exit;
    
            if (channel >= ADC_CHANNEL_NUM)
            {
                cliPrintf("channel[%hhu] not supported\n", (u8)channel);
                goto adclp_help_exit;
            }
    
            if (adclp_init[channel])
            {
                cliPrintf("channel[%hhu] already init\n", (u8)channel);
                return eCLI_PARSE_OK;
            }
    
            //注册回调函数
            cb_t[channel] = adclp_get_data;
            ret           = drv_adclp_register_callback(channel, cb_t[channel]);
            if (ret)
                return -eCLI_PARSE_INVALID_PARAMETER;
    
            //设定阈值
            ret = drv_adclp_set_bound((u8)channel, 600, 400);
            if (ret)
                return -eCLI_PARSE_INVALID_PARAMETER;
    
            adclp_init[channel] = 1;
        }
        else if (strcmp(cmd, "single") == 0)
        {
            argc = CliTokenCount(cli);
            if (argc != 1)
                goto adclp_help_exit;
    
            if (CliTokenPopNum(cli, &channel, 0) != eCLI_PARSE_OK)
                goto adclp_help_exit;
    
            if (!adclp_init[channel])
            {
                cliPrintf("channel[%hhu] must init first\n", (u8)channel);
                return eCLI_PARSE_OK;
            }
    
            //获取单个通道的电压数字量
            ret = drv_adclp_get_data((u8)channel, &data);
            if (ret)
                return -eCLI_PARSE_INVALID_PARAMETER;
            cliPrintf("channel[%hhu] data is[%hu]\n", (u8)channel, data);
        }
        else if (strcmp(cmd, "scan") == 0)
        {
            for (i = 0; i < ADC_CHANNEL_NUM; i++)
            {
                channel = i;
                ret     = drv_adclp_get_data((u8)channel, &data);
                if (ret)
                    return -eCLI_PARSE_INVALID_PARAMETER;
                cliPrintf("channel[%hhu] data is[%hu]\n", (u8)channel, data);
            }
        }
        else
        {
        adclp_help_exit:
            cliPrintf("command format : adclp init   [channel]\n");
            cliPrintf("command format : adclp single [channel]\n");
            cliPrintf("command format : adclp scan\n");
            return -eCLI_PARSE_INVALID_PARAMETER;
        }
    
        return eCLI_PARSE_OK;
    }
    

    6. API参考

    API可参考头文件sc/driver/sysdriver/saradc/drv/pub/drv_adclp.h

    enum adclp_vdd_type
    {
        ADCLP_VDD_CPU = 0,
        ADCLP_VDD_DLA,
        ADCLP_VDD_MIU,
        ADCLP_VDD_CORE,
        ADCLP_VDD_NODIE,
        ADCLP_VSS,
    };
    
    typedef int (*adclp_cb_t)(u8 channel);
    
    int drv_adclp_enable(u8 channel, u8 enable);
    int drv_adclp_get_data(u8 channel, u16 *data);
    int drv_adclp_set_bound(u8 channel, u16 upper_bound, u16 lower_bound);
    int drv_adclp_vdd_data(u8 channel, u16 *data, enum adclp_vdd_type type);
    int drv_adclp_register_callback(u8 channel, adclp_cb_t cb_t);
    int drv_adclp_unregister_callback(u8 channel, adclp_cb_t cb_t);
    

    6.1. drv_adclp_set_bound

    • 目的

      设定指定通道的阈值

    • 语法

      int drv_adclp_set_bound(u8 channel, u16 upper_bound, u16 lower_bound)
      
    • 参数

      参数名称 描述
      channel 采样通道
      upper_bound 阈值上限
      lower_bound 阈值下限
    • 返回值

      返回值 描述
      0 设定成功
      -4 通道不支持

    6.2. drv_adclp_get_data

    • 目的

      获取指定通道的外部输入电压数字量

    • 语法

      int drv_adclp_get_data(u8 channel, u16 *data)
      
    • 参数

      参数名称 描述
      channel 采样通道
      data 电压数字量
    • 返回值

      返回值 描述
      0 采样成功
      -4 通道不支持

    6.3. drv_adclp_register_callback

    • 目的

      注册指定通道的回调函数(同一通道支持注册多个回调函数),当采样结果超出阈值后可在回调函数里作出相应的处理

    • 语法

      int drv_adclp_register_callback(u8 channel, adclp_cb_t cb_t)
      
    • 参数

      参数名称 描述
      channel 采样通道
      cb_t 函数指针
    • 返回值

      返回值 描述
      0 注册成功
      -6 注册失败

    6.4. drv_adclp_unregister_callback

    • 目的

      释放指定通道的回调函数和注册时申请的内存

    • 语法

      int drv_adclp_unregister_callback(u8 channel, adclp_cb_t cb_t)
      
    • 参数

      参数名称 描述
      channel 采样通道
      cb_t 函数指针
    • 返回值

      返回值 描述
      0 取消注册成功
      -6 取消注册失败

    6.5. drv_adclp_enable

    • 目的

      使能指定通道的采样功能

    • 语法

      int drv_adclp_enable(u8 channel, u8 enable);
      
    • 参数

      参数名称 描述
      channel 采样通道
      enable 使能与否
    • 返回值

      返回值 描述
      0 成功
      -4 通道不支持

    6.6. drv_adclp_vdd_data

    • 目的

      获取特殊通道的adc code,无需外接输入电压,adc code值取决于adclp_vdd_type传入的类型

    • 语法

      int drv_adclp_vdd_data(u8 channel, u16 *data, enum adclp_vdd_type type)
      
    • 参数

      参数名称 描述
      channel 此处指定使用8
      data 电压数字量
      type 枚举,可选择VDD_CORE,VDD_CPU,VDD_DLA等
    • 返回值

      返回值 描述
      0 设定成功
      -4 通道不支持

    7. FAQ

    Q1:SAR ADCLP接口不存在

    1. 检查sysdesc adclp节点的status是否为1

    2. 检查config是否配置,详见[5.2 CONFIG配置]

    Q2:外部输入电压变化,SAR ADCLP采样数据没有变化

    1. 当PIN脚处于GPIO MODE时,采样数据不会变化,可读取寄存器的数值判断PIN脚是否被切为GPIO MODE:

      0x14 0x11 BIT0-BIT5:
      BIT0~BIT5的每个BIT对应一个通道,当value=0时,该通道PIN脚处于GPIO MODE,当value=1时,该通道PIN脚处于ADC MODE,比如BIT0=0时,通道0处于GPIO MODE,BIT1=1时,通道1处于ADC MODE
      
      0x14 0x11 BIT8-BIT13:
      BIT8~BIT13的每个BIT对应一个通道,前提条件是PIN脚已处于GPIO MODE,当value=0时,PIN脚切换为output,当value=1时,PIN脚切换为input
      
      0x14 0x12 BIT0-BIT5:
      BIT0~BIT5的每个BIT对应一个通道,前提条件是PIN脚已处于GPIO MODE并且设定为ouput,当value=0时,该通道PIN脚切换为低电平,当value=1时,该通道PIN脚切换为高电平
      
      例如:riu_r 0x14 0x11
      返回值:0x3F3F -> 全部通道为ADC MODE
      返回值:0x3E3E -> 通道0为GPIO MODE,其余通道为ADC MODE
      返回值:0x0000 -> 全部通道为GPIO MODE且切换为output
      
    2. 当PIN脚寄存器设定非GPIO MODE时,采样数据仍然没有变化,可将PIN脚设为GPIO MODE并进行output high/low的试验,如果PIN脚电平无法拉高拉低则可判断为硬件问题

    Q3:第一次或者前几次采样数据与实际输入电压偏差大

    此问题大概率与采样时机有关,可通过操作GPIO作为触发源,获取每次采样时元件的电压情况,

    如下图所示,触发ADC采样前,GPIO从高电平切换为低电平,ADC采样时机刚好处于电压的下降过程,并不是在电压稳定时去采样,因此被判定为采样异常,此时需要等元件稳定时再去采样