CH32V307 ADC DMA多通道采集 - RISC-V MCU技术社区 - 电子技术论坛 - 广受欢迎的专业电子论坛
分享 收藏 返回

[文章]

CH32V307 ADC DMA多通道采集

CH32V307 ADC 模块包含 2 个 12 位的逐次逼近型的模拟数字转换器,最高 14MHz 的输入时钟。支持 16 个外部通道和 2 个内部信号源采样源,支持DMA。
DMA提供在外设和存储器之间或者存储器和存储器之间的高速数据传输方式,无须CPU干预,数据可以通过DMA快速地移动,以节省CPU的资源来做其他操作。
CH32V307 ADC使用DMA时,需使用DMA通道1,如下图,因此在程序配置中要对ADC对应通道以及DMA通道1进行配置,具体程序如下:
1、ADC对应GPIO引脚以及ADC外设初始化
  1. //ADC对应GPIO初始化配置以及ADC初始化配置
  2. void adc_Init(void)
  3. {
  4.     GPIO_InitTypeDef GPIO_InitStructure={0};
  5.     ADC_InitTypeDef  ADC_InitStructure={0};

  6.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE ); //使能GPIOA时钟和ADC
  7.     RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC时钟分频为6分频

  8.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5;   //
  9.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //GPIO模式为模拟输入
  10.     GPIO_Init(GPIOA, &GPIO_InitStructure);

  11.     ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;  //配置ADC为独立模式
  12.     ADC_InitStructure.ADC_ScanConvMode = ENABLE;        //多通道模式下开启扫描模式
  13.     ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;  //设置开启连续转换模式
  14.     ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //设置转换不是由外部触发启动,软件触发启动
  15.     ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //设置ADC数据右对齐
  16.     ADC_InitStructure.ADC_NbrOfChannel = length;                //规则转换的ADC通道的数目
  17.     ADC_Init(ADC1, &ADC_InitStructure);                    //根据ADC_InitStructure中指定的参数初始化ADC1寄存器

  18.     ADC_Cmd(ADC1, ENABLE);      //使能ADC1

  19.     ADC_ResetCalibration(ADC1); //重置ADC1校准寄存器。

  20.     while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束

  21.     ADC_StartCalibration(ADC1); //开启AD校准

  22.     while(ADC_GetCalibrationStatus(ADC1));      //等待校准结束
  23. }
GPIO引脚初始化注意要使能开启GPIO时钟,GPIO引脚要配置为模拟输入模式。注意ADC初始化配置时,ADC多通道需要开启扫描模式。

2、ADC对应DMA通道1初始化配置
  1. //ADC DMA模式配置
  2. void DMA_Tx_Init( void )
  3. {
  4.     DMA_InitTypeDef DMA_InitStructure={0};

  5.     RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE ); //使能开启DMA时钟

  6.     DMA_DeInit(DMA1_Channel1); //复位DMA控制器

  7.     DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->RDATAR;  //配置外设地址为ADC数据寄存器地址
  8.     DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ADC_ConvertedValue; //配置存储器地址为读取ADC值地址
  9.     DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;              //配置数据源为外设,即DMA传输方式为外设到存储器
  10.     DMA_InitStructure.DMA_BufferSize = length;                      //设置DMA数据缓冲区大小,此处设置为length
  11.     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//设置DMA外设递增模式关闭
  12.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;         //设置DMA存储器递增模式开启
  13.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //设置外设数据大小为半字,即两个字节
  14.     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;         //设置存储器数据大小为半字,即两个字节
  15.     DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;     //设置DMA模式为循环传输模式
  16.     DMA_InitStructure.DMA_Priority = DMA_Priority_High; //设置DMA传输通道优先级为高,当使用一 DMA通道时,优先级设置不影响
  17.     DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;        //因为此DMA传输方式为外设到存储器,因此禁用存储器到存储器传输方式
  18.     DMA_Init( DMA1_Channel1, &DMA_InitStructure );      //初始化DMA

  19.     DMA_Cmd(DMA1_Channel1 , ENABLE);  //使能DMA
  20. }
DMA初始化配置时需要注意,此处使用了两个DMA通道,因此DMA缓冲区大小需要配置为2,关于DMA的模式,可配置为正常模式或循环模式,此处配置为循环模式。正常模式和循环模式的区别如下:
  • DMA_Mode_Normal,当DMA通道配置为正常模式时,传输结束(DMA通道传输数量寄存器变为0)后将不再产生DMA操作。要开启新的DMA传输,需要进行以下操作配置:在关闭DMA通道后,为DMA通道传输数量寄存器重新赋值,然后重新开启DMA。
  • DMA_Mode_Circular,当DMA通道配置为循环模式时,最后一次传输结束后,DMA通道传输数量寄存器将会自动重新加载为之前配置时的数值。内部的当前外设/存储器地址寄存器也被重新加载为外设/存储器地址寄存器设定的初始基地址。

3、在配置完成ADC初始化以及DMA初始化之后,需要使能开启ADC DMA传输
  1. void ADC_DMA_CONF(void)
  2. {
  3.     adc_Init();

  4.     DMA_Tx_Init();

  5.     // 配置 ADC 通道转换顺序为1,第一个转换,采样时间为55.5个时钟周期
  6.     ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_55Cycles5);
  7.     ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 2, ADC_SampleTime_55Cycles5);

  8.     // 使能ADC DMA 请求
  9.     ADC_DMACmd(ADC1, ENABLE);

  10.     // 由于没有采用外部触发,所以使用软件触发ADC转换
  11.     ADC_SoftwareStartConvCmd(ADC1, ENABLE);
  12. }

4、上述程序配置完成后,可在main函数while循环中打印输出对应通道的ADC值
  1. extern u16 ADC_ConvertedValue[length];

  2. /*********************************************************************
  3. * @fn      main
  4. *
  5. * [url=home.php?mod=space&uid=2666770]@Brief[/url]   Main program.
  6. *
  7. * [url=home.php?mod=space&uid=1141835]@Return[/url]  none
  8. */
  9. int main(void)
  10. {
  11.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  12.     Delay_Init();
  13.     USART_Printf_Init(115200);

  14.     ADC_DMA_CONF();

  15.     printf("SystemClk:%d
  16. ", SystemCoreClock);
  17.     printf("This is printf example
  18. ");

  19.     while(1)
  20.     {
  21.         printf("
  22. The current ADCH4 value = %d
  23. ", ADC_ConvertedValue[0]);
  24.         printf("
  25. The current ADCH5 value = %d
  26. ", ADC_ConvertedValue[1]);

  27.         Delay_Ms(500);
  28.     }
  29. }
下载程序,打印结果如下:

注意,在使用 ADC 常规通道的扫描模式采集多路模拟信号时,可能存在各路信号转换相同结果的情况(实际各路电压不同)。问题原因是相邻通道之间透过采样电容Cs发生了藕合。
解决办法:
(1)增大 ADC 相邻两个通道采样之间的延时,增加采样保持时间或在电压信号相差较大的信道中间,增加对基准电压的采样,使信号处于平均值,在切换通道时不会因为充放电不及时导致数据错误。
(2)精度要求不高的话,可以把采样周期设置的大一些,在一定程度上可以降低干扰。
(3)相邻两个通道使用不同的ADC单元,分别配置到ADC1和ADC2上面,彻底隔离。



更多回帖

×
发帖