Continuous Mode

该示例通过使用 ADC 连续采样模式进行电压检测。该示例还包含了可配置宏用于控制是否使用DMA进行数据搬运。 若未开启DMA搬运,则ADC采样完成之后触发 ADC_INT_FIFO_THD 中断,在中断函数内读取raw data并进行电压转换计算。 若开启DAM搬运,DMA将数据从ADC FIFO搬运至数组中。

环境需求

该示例支持以下开发套件:

开发套件

Hardware Platforms

Board Name

RTL87x2G HDK

RTL87x2G EVB

更多信息请参考 快速入门

硬件连线

连接P2_4和外部输入电压。

配置选项

该示例可配置的宏如下:

  1. ADC_CONFIG_GDMA_MODE_EN :配置该宏可选择是否使用DMA进行ADC采样数据搬运, 1 表示使用DMA搬运, 0 表示不使用DMA搬运。

  2. ADC_MODE_DIVIDE_OR_BYPASS :配置该宏可选择ADC的电压采样范围,可选择的值如下。

    • ADC_DIVIDE_MODE :在Divide Mode下,ADC的采样电压值范围为0~3.3V。

    • ADC_BYPASS_MODE :在Bypass Mode下,ADC的采样电压值范围为0~0.9V。

编译和下载

该示例的工程路径如下:

Project file: samples\peripheral\adc\continuous_gdma\proj\rtl87x2g\mdk

Project file: samples\peripheral\adc\continuous_gdma\proj\rtl87x2g\gcc

请按照以下步骤操作构建并运行该示例:

  1. 打开工程文件。

  2. 按照 快速入门编译APP Image 给出的步骤构建目标文件。

  3. 编译成功后,在路径 mdk\bingcc\bin 下会生成 app bin app_MP_xxx.bin 文件。

  4. 按照 快速入门MPTool 给出的步骤将app bin烧录至EVB内。

  5. 按下复位按键,开始运行。

测试验证

  1. EVB启动后,在DebugAnalyzer工具内观察LOG。

    Start ADC Continuous Mode test !
    
  2. ADC初始化配置:

    1. 若配置为 ADC_DIVIDE_MODE ,则会打印如下LOG。

      [ADC]ADC sample mode is divide mode !
      
    2. 若配置为 ADC_BYPASS_MODE ,则会打印如下LOG。

      [ADC]ADC sample mode is bypass mode !
      
  3. ADC采样结束后,会在DebugAnalyzer工具内打印采集得到的raw data和转换后的电压值。若开启宏 ADC_CONFIG_GDMA_MODE_EN ,则会打印DMA中断信息;若未开启宏 ADC_CONFIG_GDMA_MODE_EN ,则会打印ADC中断信息。

    [io_adc]io_adc_voltage_calculate: ADC rawdata_0 = xxx, voltage_0 = xxxmV
    [io_adc]io_adc_voltage_calculate: ADC rawdata_1 = xxx, voltage_1 = xxxmV
    ...
    

代码介绍

该章节分为以下几个部分:

  1. 源码路径

  2. 初始化函数将在 初始化 章节介绍。

  3. 初始化后的功能实现将在 功能实现 章节介绍。

源码路径

  • 工程路径: sdk\samples\peripheral\adc\continuous_gdma\proj

  • 源码路径: sdk\samples\peripheral\adc\continuous_gdma\src

该工程的工程文件代码结构如下:

└── Project: continuous_gdma
    └── secure_only_app
        └── Device                   includes startup code
            ├── startup_rtl.c
            └── system_rtl.c
        ├── CMSIS                    includes CMSIS header files
        ├── CMSE Library             Non-secure callable lib
        ├── Lib                      includes all binary symbol files that user application is built on
            └── rtl87x2g_io.lib
        ├── Peripheral               includes all peripheral drivers and module code used by the application
            ├── rtl_rcc.c
            ├── rtl_pinmux.c
            ├── rtl_gdma.c
            └── rtl_adc.c
        └── APP                      includes the ble_peripheral user application implementation
            ├── main_ns.c
            └── io_adc.c

初始化

初始化流程包括了 global_adc_init board_adc_initdriver_adc_init。 若开启宏 ADC_CONFIG_GDMA_MODE_EN ,则初始化中仍包含 driver_gdma_adc_init


global_adc_init 中包含了全局初始化。

  1. 执行 ADC_CalibrationInit() 函数进行ADC校准。

  2. 初始化DMA接收数组 ADC_Recv_Buffer


board_adc_init 中包含了PAD设置。

  1. 配置PAD:设置引脚、SW模式、PowerOn、无内部上拉、输出失能。


driver_adc_init 包含了对ADC外设的初始化。

  1. 使能PCC时钟。

  2. 配置ADC的采样通道,配置通道0为P2_4单端模式,设置Bitmap为0x01。

  3. 若开启宏 ADC_CONFIG_GDMA_MODE_EN ,设置 ADC_WaterLevel

  4. 设置 ADC_DataWriteToFifo 为 ENABLE,将ADC采样数据存入FIFO,设置 ADC_FifoThdLevel

  5. 若开启Bypass Mode,执行函数 ADC_BypassCmd() 开启对应引脚的Bypass模式。

  6. 配置ADC中断。

RCC_PeriphClockCmd(APBPeriph_ADC, APBPeriph_ADC_CLOCK, ENABLE);
...
#if ADC_CONFIG_GDMA_MODE_EN
  ADC_InitStruct.ADC_WaterLevel       = 4;
#endif
  ADC_InitStruct.ADC_DataWriteToFifo  = ENABLE;
  ...
#if ADC_CONFIG_GDMA_MODE_EN
  ADC_INTConfig(ADC, ADC_INT_FIFO_RD_ERR, ENABLE);
  ADC_INTConfig(ADC, ADC_INT_FIFO_OVERFLOW, ENABLE);
#else
  ADC_INTConfig(ADC, ADC_INT_FIFO_THD, ENABLE);
#endif

备注

ADC的Continuous模式下,数据默认存放至ADC FIFO中。


driver_gdma_adc_init 包含了对DMA搬运的初始化。

  1. 指定GDMA传输方向为 外设内存 传输。

  2. 设置源地址为ADC FIFO,目的地址为 ADC_Recv_Buffer

  3. 设置源端地址 固定 ,目的端地址 自增

  4. 配置GDMA总传输完成中断 GDMA_INT_Transfer

GDMA_InitStruct.GDMA_DIR                = GDMA_DIR_PeripheralToMemory;
GDMA_InitStruct.GDMA_SourceInc          = DMA_SourceInc_Fix;
GDMA_InitStruct.GDMA_DestinationInc     = DMA_DestinationInc_Inc;
GDMA_InitStruct.GDMA_SourceAddr         = (uint32_t)(&(ADC->ADC_FIFO_READ));
GDMA_InitStruct.GDMA_DestinationAddr    = (uint32_t)ADC_Recv_Buffer;
...
GDMA_INTConfig(ADC_GDMA_CHANNEL_NUM, GDMA_INT_Transfer, ENABLE);
...

功能实现

  1. 若开启宏 ADC_CONFIG_GDMA_MODE_EN ,在初始化完DMA后,执行 GDMA_Cmd() ,开启DMA搬运。

  2. 在ADC采样前执行 ADC_ClearFIFO() 清空ADC FIFO内数据。

  3. 执行 ADC_Cmd() ,开始ADC采样。

  4. 若开启宏 ADC_CONFIG_GDMA_MODE_EN ,DMA搬运结束后,进入GDMA中断函数。

    1. 失能ADC连续采样模式。

    2. 对采样数据进行数据处理(详见第6点)。

    3. 重新设置GDMA搬运信息,包括源地址,目的地址,搬运长度。

    4. 重新使能GDMA搬运,清空ADC FIFO数据,重新使能ADC采样。

void ADC_GDMA_Channel_Handler(void)
{
    ADC_Cmd(ADC, ADC_CONTINUOUS_MODE, DISABLE);
    for (uint32_t i = 0; i < GDMA_TRANSFER_SIZE; i++)
    {
      ...
    }
    GDMA_SetSourceAddress(ADC_GDMA_Channel, (uint32_t)(&(ADC->ADC_FIFO_READ)));
    GDMA_SetDestinationAddress(ADC_GDMA_Channel, (uint32_t)(ADC_Recv_Buffer));
    GDMA_SetBufferSize(ADC_GDMA_Channel, GDMA_TRANSFER_SIZE);
    GDMA_ClearINTPendingBit(ADC_GDMA_CHANNEL_NUM, GDMA_INT_Transfer);
    GDMA_Cmd(ADC_GDMA_CHANNEL_NUM, ENABLE);

    ADC_ClearFIFO(ADC);
    ADC_Cmd(ADC, ADC_CONTINUOUS_MODE, ENABLE);
}
  1. 若未开启宏 ADC_CONFIG_GDMA_MODE_EN ,ADC FIFO内的值达到Threshold时,触发 ADC_INT_FIFO_THD 中断,进入ADC中断函数。

    1. 执行 ADC_GetFIFODataLen() 读取FIFO中数据长度,执行 ADC_ReadFIFOData() 函数读取FIFO内数据。

    2. 执行 ADC_ClearFIFO() 清空ADC FIFO内数据。

    3. 执行 ADC_GetVoltage() 函数,将采样数据转换为电压值。

if (ADC_GetINTStatus(ADC, ADC_INT_FIFO_THD) == SET)
{
    ADC_Cmd(ADC, ADC_CONTINUOUS_MODE, DISABLE);
    data_len = ADC_GetFIFODataLen(ADC);
    ADC_ReadFIFOData(ADC, sample_data, data_len);

    ADC_ClearFIFO(ADC);
    ...
    ADC_Cmd(ADC, ADC_CONTINUOUS_MODE, ENABLE);
}

备注

当ADC FIFO溢出时触发 ADC_INT_FIFO_OVERFLOW 中断,进入中断处理函数 SAR_ADC_Handler ,清空ADC FIFO数据。

常见问题

  1. 若拿到的IC未经过FT校验,ADC则无法转换为正确的电压值。在LOG工具内会打印如下信息。

    [ADC]ADC_CalibrationInit fail!
    
  2. 若ADC采样值不正确,则会打印错误状态信息。

    [ADC]adc_sample_demo: ADC parameter or efuse data error! i = xxx, error_status = xxx