Continuous Mode

该示例通过使用 ADC 连续采样模式进行电压检测,示例中还可以通过修改配置用于控制是否使用 GDMA 进行数据搬运。

在连续采样模式下,ADC 的采样数据默认存放在 ADC FIFO 中。若使用 GDMA 搬运功能,GDMA 将数据从 ADC FIFO 搬运至内存中。

用户可以通过不同的宏配置改变示例中 ADC 通道的采样模式,输入电压范围等信息,具体宏配置详见 配置选项

环境需求

该示例的环境需求,可参考 环境需求

硬件连线

使用杜邦线短接 P2_4 和外部输入电压。

配置选项

  1. 可配置如下宏修改是否使用 GDMA 搬运 ADC FIFO 数据。 1 表示使用 GDMA, 0 表示不使用 GDMA。

    #define ADC_CONFIG_GDMA_MODE_EN             0                   /*< Set this macro to configure whether to use GDMA for transferring ADC FIFO data. */
    
  2. 可配置如下宏修改 ADC 输入电压范围。

    #define ADC_DIVIDE_MODE                     1                   /*< Divide Mode, input voltage range is 0V~Vbat. */
    #define ADC_BYPASS_MODE                     0                   /*< Bypass Mode, input voltage range is 0V~0.9V. */
    
    #define ADC_MODE_DIVIDE_OR_BYPASS           ADC_DIVIDE_MODE     /*< Set this macro to select the ADC input voltage range. */
    
  3. 可配置如下宏修改引脚定义。

    #define ADC_SAMPLE_PIN_0                    P2_4
    #define ADC_SAMPLE_CHANNEL_0                ADC_Channel_Index_4
    
  4. 可配置如下宏修改 GDMA 通道相关配置。

    // Set the following macros to modify the ADC GDMA Channel configurations.
    #define ADC_GDMA_CHANNEL_NUM                0
    #define ADC_GDMA_Channel                    GDMA_Channel0
    #define ADC_GDMA_Channel_IRQn               GDMA0_Channel0_IRQn
    #define ADC_GDMA_Channel_Handler            GDMA0_Channel0_Handler
    
  5. 可配置如下宏修改 ADC GDMA 数据传输长度。

    #define GDMA_TRANSFER_SIZE                  100                       /*< Set this macro to modify the transfer size. */
    
  6. 可配置如下宏修改 ADC 连续采样模式的采样时间。

    #define ADC_CONTINUOUS_SAMPLE_PERIOD        (200-1)//20us             /*< Set this macro to modify the ADC sample period. */
    

编译和下载

该示例的编译和下载流程,可参考 编译和下载

测试验证

  1. EVB 启动后,在 Debug Analyzer 工具内观察 log。

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

    1. ADC_MODE_DIVIDE_OR_BYPASS 配置为 ADC_DIVIDE_MODE ,则会打印如下 log:

      [ADC]ADC sample mode is divide mode !
      
    2. ADC_MODE_DIVIDE_OR_BYPASS 配置为 ADC_BYPASS_MODE ,则会打印如下 log:

      [ADC]ADC sample mode is bypass mode !
      
  3. 初始化完成后 ADC 开始采样。当 ADC FIFO 内数据达到阈值时,会触发 ADC 中断。当 GDMA 搬运的数据达到设定值时,会触发 GDMA 中断。在中断函数内打印采样得到的 raw data 和转换后的电压值。

    [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
    ...
    
  4. 若 ADC FIFO 溢出,则会触发 ADC_INT_FIFO_OVERFLOW 中断,log 打印相关信息。

    ADC_INT_FIFO_OVERFLOW
    
  5. 如果 ADC FIFO 为空时读取数据,则会触发 ADC_INT_FIFO_RD_ERR 中断,log 打印相关信息。

    ADC_INT_FIFO_RD_ERR
    

代码介绍

该章节主要介绍示例中的初始化和相应功能实现的代码和流程说明。

源码路径

工程文件和源码路径如下:

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

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

初始化

外设的初始化流程可参考 General Introduction 中的 初始化流程 部分。

  1. 调用 Pad_Config()Pinmux_Config(),配置对应引脚的 PAD 和 PINMUX。注意需要配置 PAD 为 Shutdown 模式,防止漏电。

    void board_adc_init(void)
    {
        Pad_Config(ADC_SAMPLE_PIN_0, PAD_SW_MODE, PAD_NOT_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, PAD_OUT_LOW);
    }
    
  2. 调用 RCC_PeriphClockCmd(),开启 ADC 时钟。

  3. 对 ADC 外设进行初始化:

    1. 定义 ADC_InitTypeDef 类型 ADC_InitStruct,调用 ADC_StructInit()ADC_InitStruct 预填默认值。

    2. 根据需求修改 ADC_InitStruct 参数,当 ADC_CONFIG_GDMA_MODE_EN 配置为不同值时,初始化配置会有所不同。ADC 的初始化参数配置如下表。

    3. 调用 ADC_Init(),初始化 ADC 外设。

ADC 初始化参数

ADC Hardware Parameters

Setting in the ADC_InitStruct

ADC_CONFIG_GDMA_MODE_EN=0

ADC_CONFIG_GDMA_MODE_EN=1

Sample Time

ADC_InitTypeDef::ADC_SampleTime

199

199

Schedule Index

ADC_InitTypeDef::ADC_SchIndex

Index 0 is set to EXT_SINGLE_ENDED(4)

Index 0 is set to EXT_SINGLE_ENDED(4)

Bit Map

ADC_InitTypeDef::ADC_Bitmap

0x01

0x01

Data Write to FIFO

ADC_InitTypeDef::ADC_DataWriteToFifo

ENABLE

ENABLE

ADC Water Level

ADC_InitTypeDef::ADC_WaterLevel

-

4

FIFO Threshold Level

ADC_InitTypeDef::ADC_FifoThdLevel

16

-

Power Always On

ADC_InitTypeDef::ADC_PowerAlwaysOnEn

ENABLE

ENABLE

  1. ADC_MODE_DIVIDE_OR_BYPASS 配置为 ADC_BYPASS_MODE,需要调用函数 ADC_BypassCmd() 开启对应引脚的 Bypass 模式。

  2. 调用 ADC_INTConfig(),配置 ADC 中断和 NVIC。NVIC 相关配置可参考 中断配置

    1. ADC_CONFIG_GDMA_MODE_EN 配置为 0,配置 ADC FIFO 达到阈值中断 ADC_INT_FIFO_THD

    2. ADC_CONFIG_GDMA_MODE_EN 配置为 1,配置 ADC FIFO 溢出中断 ADC_INT_FIFO_OVERFLOW 和 ADC FIFO 读错误中断 ADC_INT_FIFO_RD_ERR

  3. 在 ADC 开始采样前,必须调用 ADC_CalibrationInit() 进行 ADC 电压校准。若返回值为 false,则代表该 IC 未经校验,无法正确转换电压值,但仍可读到 raw data。

  4. ADC_CONFIG_GDMA_MODE_EN 配置为 1,则需要初始化 GDMA 外设:

    1. 定义 GDMA_InitTypeDef 类型 GDMA_InitStruct,调用 GDMA_StructInit()GDMA_InitStruct 预填默认值。

    2. 根据需求修改 GDMA_InitStruct 参数。GDMA 通道的初始化参数配置如下表。调用 GDMA_Init(),初始化 GDMA 外设。

    3. 配置 GDMA 总传输完成中断 GDMA_INT_Transfer 和 NVIC。NVIC 相关配置可参考 中断配置

    4. 调用 GDMA_Cmd() 使能对应 GDMA 通道传输。

GDMA 初始化参数

GDMA Hardware Parameters

Setting in the GDMA_InitStruct

GDMA Channel

Channel Num

GDMA_InitTypeDef::GDMA_ChannelNum

0

Transfer Direction

GDMA_InitTypeDef::GDMA_DIR

GDMA_DIR_PeripheralToMemory

Buffer Size

GDMA_InitTypeDef::GDMA_BufferSize

100

Source Address Increment or Decrement

GDMA_InitTypeDef::GDMA_SourceInc

DMA_SourceInc_Fix

Destination Address Increment or Decrement

GDMA_InitTypeDef::GDMA_DestinationInc

DMA_DestinationInc_Inc

Source Data Size

GDMA_InitTypeDef::GDMA_SourceDataSize

GDMA_DataSize_HalfWord

Destination Data Size

GDMA_InitTypeDef::GDMA_DestinationDataSize

GDMA_DataSize_HalfWord

Source Burst Transaction Length

GDMA_InitTypeDef::GDMA_SourceMsize

GDMA_Msize_4

Destination Burst Transaction Length

GDMA_InitTypeDef::GDMA_DestinationMsize

GDMA_Msize_4

Source Address

GDMA_InitTypeDef::GDMA_SourceAddr

(&(ADC->ADC_FIFO_READ))

Destination Address

GDMA_InitTypeDef::GDMA_DestinationAddr

ADC_Recv_Buffer

Source Handshake

GDMA_InitTypeDef::GDMA_SourceHandshake

GDMA_Handshake_ADC_RX

功能实现

ADC 采样数据不使用 GDMA 搬运

当 ADC 采样数据不使用 GDMA 搬运时,连续模式采样的流程如图所示:

这里应该是 ADC continuous not GDMA flow

ADC 连续采样流程图(不使用 GDMA 搬运数据)

  1. 在 ADC 采样前调用 ADC_ClearFIFO() 清空 ADC FIFO 内数据。

  2. 调用 ADC_Cmd() ,开始 ADC 连续采样。

  3. 当 FIFO 中的采样数据达到设定的阈值时,触发 ADC ADC_INT_FIFO_THD 中断,在中断函数内调用 ADC_GetFIFODataLen() 读取 FIFO 中数据长度;调用 ADC_ReadFIFOData() 函数读取 FIFO 内数据;调用 ADC_GetVoltage() 函数,输入对应采样模式,将采样数据转换为电压值。

  4. 数据处理完毕后,调用 ADC_ClearFIFO() 清空 ADC FIFO 内数据。

    void SAR_ADC_Handler(void)
    {
        ...
        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_ClearINTPendingBit(ADC, ADC_INT_FIFO_THD);
    
            for (uint8_t i = 0; i < data_len; i++)
            {
                sample_voltage[i] = ADC_GetVoltage(DIVIDE_SINGLE_MODE, (int32_t)sample_data[i], &error_status);
                ...
            }
        }
        for (uint32_t i = 0; i < 1000000; i++) {};
        ADC_Cmd(ADC, ADC_CONTINUOUS_MODE, ENABLE);
    
    }
    

ADC 采样数据使用 GDMA 搬运

当 ADC 采样数据使用 GDMA 搬运时,连续模式采样的流程如图所示:

这里应该是 ADC continuous GDMA flow

ADC 连续采样流程图(使用 GDMA 搬运数据)

  1. 使能 GDMA 传输通道后,当 ADC FIFO 内采样数据达到设定的阈值时,会触发 GDMA 搬运,GDMA 将数据从 (&(ADC->ADC_FIFO_READ)) 搬运至 ADC_Recv_Buffer

  2. 当 GDMA 数据传输完成后,触发 GDMA GDMA_INT_Transfer 中断。

    1. 在中断函数内首先调用 ADC_Cmd() 使能 ADC 连续采样模式,防止在中断处理过程中继续采样导致 ADC FIFO 溢出。

    2. 调用 ADC_GetVoltage() 函数,将采样数据转换为电压值。

    3. 重新使能 GDMA 的源地址、目的地址,重新使能 GDMA 传输。

    4. 调用 ADC_ClearFIFO() 清空 ADC FIFO 内数据,调用 ADC_Cmd() 重新使能 ADC 连续采样模式。

      void ADC_GDMA_Channel_Handler(void)
      {
          ADC_Cmd(ADC, ADC_CONTINUOUS_MODE, DISABLE);
          DBG_DIRECT("into ADC_GDMA_Channel_Handler");
          float sample_voltage = 0;
          ADC_ErrorStatus error_status = NO_ERROR;
      
          for (uint32_t i = 0; i < GDMA_TRANSFER_SIZE; i++)
          {
              uint16_t sample_data = ADC_Recv_Buffer[i];
              sample_voltage = ADC_GetVoltage(DIVIDE_SINGLE_MODE, (int32_t)sample_data, &error_status);
              ...
          }
      
          platform_delay_ms(1000);
      
          /* Restart dma and adc */
          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);
      
      }
      
  3. 当 ADC FIFO 溢出时,触发 ADC_INT_FIFO_OVERFLOW 中断,在 ADC 中断函数内清空 ADC FIFO 数据,并打印相关信息。

    void SAR_ADC_Handler(void)
    {
        if (ADC_GetINTStatus(ADC, ADC_INT_FIFO_RD_ERR) == SET)
        {
            DBG_DIRECT("ADC_INT_FIFO_RD_ERR!");
            ADC_ClearINTPendingBit(ADC, ADC_INT_FIFO_RD_ERR);
        }
        if (ADC_GetINTStatus(ADC, ADC_INT_FIFO_OVERFLOW) == SET)
        {
            ADC_WriteFIFOCmd(ADC, DISABLE);
            DBG_DIRECT("ADC_INT_FIFO_OVERFLOW");
    
            ADC_ClearFIFO(ADC);
            ADC_WriteFIFOCmd(ADC, ENABLE);
            ADC_ClearINTPendingBit(ADC, ADC_INT_FIFO_OVERFLOW);
        }
    }