[APM32F4] 【APM32F411V Tiny Board测评】+ADC采样测试

[复制链接]
 楼主| WoodData 发表于 2024-5-27 16:07 | 显示全部楼层 |阅读模式
本次研究一下APM32F411的ADC外设功能。 该MCU有 2 个 ADC,精度为 12 位,每个 ADC 最多有 16 个外部通道和 3 个内部通道, 各通道 A/D 转换模式有单次、连续、扫描或间断, ADC 转换结果可以左对齐或右对齐存储在 16 位数据寄存器中。支持模拟看门狗,支持 DMA功能。 内置 1 个温度传感器(TSensor),内部连接 ADC_IN18 通道,传感器产生的电压随着温度线性变化,可通过 ADC 获取转换的电压值换算成温度。
内置 1 个 VBAT 监控器,内部连接 ADC_IN18 通道。 当同时设置温度传感器和 VBAT转换时,仅执行 VBAT转换。
内置参考电压 VREFINT,内部连接 ADC_IN17 通道,可通过 ADC 获取该 VREFINT; VREFINT 为ADC、比较器提供稳定的电压输出。




下面开始测试ADC转换功能。使用了PA0,PA1,PA2,PA3这4个ADC通道引脚。
1.png
(框内有个书写错误)。

间断模式和扫描模式差别:
3.png


4.png
扫描模式是一次触发,组内通道全部转换,然后产生标志位。
间断模式是一次触发转换一个通道,等组内所有通道转换完,再产生标志位。

ADC规则序列转换测试:

先使用ADC的规则序列转换功能。初始化ADC如下,间断模式和单次转换模式。使用中断方式读取数据:
  1. void ADC_Init(void)
  2. {
  3.     GPIO_Config_T       gpioConfig;
  4.     ADC_Config_T        adcConfig;
  5.     ADC_CommonConfig_T  adcCommonConfig;

  6.     /* Enable GPIOA clock */
  7.     RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOA);
  8.     /* ADC channel 0 configuration */
  9.     GPIO_ConfigStructInit(&gpioConfig);
  10.     gpioConfig.mode    = GPIO_MODE_AN;
  11.     gpioConfig.pupd    = GPIO_PUPD_NOPULL;
  12.     gpioConfig.pin     = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
  13.     GPIO_Config(GPIOA, &gpioConfig);

  14.     /* Enable ADC clock */
  15.     RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC1);
  16.     /* ADC configuration */
  17.     ADC_Reset();
  18.     adcConfig.resolution            = ADC_RESOLUTION_12BIT;
  19.     adcConfig.scanConvMode          = DISABLE;
  20.     adcConfig.continuousConvMode    = DISABLE;
  21.     adcConfig.extTrigEdge           = ADC_EXT_TRIG_EDGE_NONE;
  22.     adcConfig.extTrigConv           = ADC_EXT_TRIG_CONV_TMR1_CC1;
  23.     adcConfig.dataAlign             = ADC_DATA_ALIGN_RIGHT;
  24.     adcConfig.nbrOfChannel          = 4;
  25.     ADC_Config(ADC1, &adcConfig);

  26.     ADC_CommonConfigStructInit(&adcCommonConfig);
  27.     /*Set ADC Clock Prescaler. ADC_Clock = APB2_Clock/4, 84/4=21MHz*/
  28.     adcCommonConfig.prescaler = ADC_PRESCALER_DIV4;
  29.     ADC_CommonConfig(&adcCommonConfig);
  30.    
  31.    
  32.     /* ADC channel 0-3 Convert configuration */
  33.     ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_112CYCLES);
  34.     ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_1, 2, ADC_SAMPLETIME_112CYCLES);
  35.     ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_2, 3, ADC_SAMPLETIME_112CYCLES);
  36.     ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_3, 4, ADC_SAMPLETIME_112CYCLES);
  37.     /* Enable complete conversion interupt */
  38.     ADC_EnableInterrupt(ADC1, ADC_INT_EOC);
  39.    
  40.    
  41.     /* NVIC configuration */
  42.     NVIC_EnableIRQRequest(ADC_IRQn, 1, 1);
  43.     /* Enable ADC */
  44.     ADC_Enable(ADC1);
  45.    
  46. }
复制代码
中断函数:
  1. uint16_t adcData[4] = {0};
  2. uint16_t adcIndex = 0;
  3. void ADC_IRQHandler(void)
  4. {
  5.     if (ADC_ReadStatusFlag(ADC1, ADC_FLAG_EOC))
  6.     {
  7.         ADC_ClearStatusFlag(ADC1, ADC_FLAG_EOC);
  8.         
  9.         adcData[adcIndex++] = ADC_ReadConversionValue(ADC1);
  10.         if(adcIndex == 4)
  11.         {
  12. //            adcIndex = 0;
  13.         }else{
  14.             ADC_SoftwareStartConv(ADC1);
  15.         }
  16.     }
  17. }
复制代码


在使用规则通道采样时要注意:多路采样时如果不用DMA方式读取数据的话,不要使用连续扫描模式。因为规则采样在采集多路ADC时只有一个转换完成标志。而且数据寄存器也只有1个。
这里我使用的是触发一次采样一次。没有使用连续扫描模式。

测试代码:
  1. #include "nr_micro_shell.h"

  2. void adc_read(char argc, char *argv)
  3. {
  4.     ADC_SoftwareStartConv(ADC1);    //ADC_EnableSoftwareStartInjectedConv();
  5.     printf("ADC:\r\n");
  6.     while(adcIndex < 4);
  7.     adcIndex = 0;
  8.     printf("1=%X\r\n",adcData[0]);
  9.     printf("2=%X\r\n",adcData[1]);
  10.     printf("3=%X\r\n",adcData[2]);
  11.     printf("4=%X\r\n",adcData[3]);
  12. }

  13. NR_SHELL_CMD_EXPORT(adc,   adc_read,     "adc data");
复制代码

通过串口查看ADC采样值:
2.png


ADC注入序列采样模式:

接下来试试注入序列采样模式。
初始化如下:
  1. void ADC_Init(void)
  2. {
  3.     GPIO_Config_T       gpioConfig;
  4.     ADC_Config_T        adcConfig;
  5.     ADC_CommonConfig_T  adcCommonConfig;

  6.     /* Enable GPIOA clock */
  7.     RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOA);
  8.     /* ADC channel 0 configuration */
  9.     GPIO_ConfigStructInit(&gpioConfig);
  10.     gpioConfig.mode    = GPIO_MODE_AN;
  11.     gpioConfig.pupd    = GPIO_PUPD_NOPULL;
  12.     gpioConfig.pin     = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
  13.     GPIO_Config(GPIOA, &gpioConfig);

  14.     /* Enable ADC clock */
  15.     RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC1);
  16.     /* ADC configuration */
  17.     ADC_Reset();
  18.     adcConfig.resolution            = ADC_RESOLUTION_12BIT;
  19.     adcConfig.scanConvMode          = ENABLE; //DISABLE;
  20.     adcConfig.continuousConvMode    = DISABLE;
  21.     adcConfig.extTrigEdge           = ADC_EXT_TRIG_EDGE_NONE;
  22.     adcConfig.extTrigConv           = ADC_EXT_TRIG_CONV_TMR1_CC1;
  23.     adcConfig.dataAlign             = ADC_DATA_ALIGN_RIGHT;
  24.     adcConfig.nbrOfChannel          = 4;
  25.     ADC_Config(ADC1, &adcConfig);

  26.     ADC_CommonConfigStructInit(&adcCommonConfig);
  27.     /*Set ADC Clock Prescaler. ADC_Clock = APB2_Clock/4, 84/4=21MHz*/
  28.     adcCommonConfig.prescaler = ADC_PRESCALER_DIV4;
  29.     ADC_CommonConfig(&adcCommonConfig);
  30.    
  31.    
  32. //    /* ADC channel 0-3 Convert configuration */
  33. //    ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_112CYCLES);
  34. //    ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_1, 2, ADC_SAMPLETIME_112CYCLES);
  35. //    ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_2, 3, ADC_SAMPLETIME_112CYCLES);
  36. //    ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_3, 4, ADC_SAMPLETIME_112CYCLES);
  37. //    /* Enable complete conversion interupt */
  38. //    ADC_EnableInterrupt(ADC1, ADC_INT_EOC);
  39.    
  40.    
  41.     ADC_ConfigInjectedSequencerLength(ADC1, 4);
  42.     ADC_ConfigInjectedChannel(ADC1, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_112CYCLES);
  43.     ADC_ConfigInjectedChannel(ADC1, ADC_CHANNEL_1, 2, ADC_SAMPLETIME_112CYCLES);
  44.     ADC_ConfigInjectedChannel(ADC1, ADC_CHANNEL_2, 3, ADC_SAMPLETIME_112CYCLES);
  45.     ADC_ConfigInjectedChannel(ADC1, ADC_CHANNEL_3, 4, ADC_SAMPLETIME_112CYCLES);
  46.     ADC_EnableInterrupt(ADC1, ADC_INT_INJEOC);
  47.    
  48.    
  49.     /* NVIC configuration */
  50.     NVIC_EnableIRQRequest(ADC_IRQn, 1, 1);
  51.     /* Enable ADC */
  52.     ADC_Enable(ADC1);
  53.    
  54. }
复制代码
中断读取数据:
  1. uint16_t adcData[4] = {0};
  2. uint16_t adcIndex = 0;
  3. void ADC_IRQHandler(void)
  4. {
  5. //    if (ADC_ReadStatusFlag(ADC1, ADC_FLAG_EOC))
  6. //    {
  7. //        ADC_ClearStatusFlag(ADC1, ADC_FLAG_EOC);
  8. //        
  9. //        adcData[adcIndex++] = ADC_ReadConversionValue(ADC1);
  10. //        if(adcIndex == 4)
  11. //        {
  12. //        }else{
  13. //            ADC_SoftwareStartConv(ADC1);
  14. //        }
  15. //    }
  16.    
  17.     if (ADC_ReadStatusFlag(ADC1, ADC_FLAG_INJEOC))
  18.     {
  19.         ADC_ClearStatusFlag(ADC1, ADC_FLAG_INJEOC);
  20.         adcData[0] = ADC_ReadInjectedConversionValue(ADC1,1);
  21.         adcData[1] = ADC_ReadInjectedConversionValue(ADC1,2);
  22.         adcData[2] = ADC_ReadInjectedConversionValue(ADC1,3);
  23.         adcData[3] = ADC_ReadInjectedConversionValue(ADC1,4);
  24.         adcIndex = 4;
  25.     }
  26. }
复制代码
测试代码:
  1. void adcin_read(char argc, char *argv)
  2. {
  3.     ADC_EnableSoftwareStartInjectedConv(ADC1);
  4.     printf("ADC1 Inject:\r\n");
  5.     while(adcIndex < 4);
  6.     adcIndex = 0;
  7.     printf("1=%X\r\n",adcData[0]);
  8.     printf("2=%X\r\n",adcData[1]);
  9.     printf("3=%X\r\n",adcData[2]);
  10.     printf("4=%X\r\n",adcData[3]);
  11. }
  12. NR_SHELL_CMD_EXPORT(adc,   adcin_read,     "adc data");
复制代码
最终串口打印数据:
5.png



星辰大海不退缩 发表于 2024-6-22 20:50 | 显示全部楼层
传感器产生的电压随着温度线性变化,可通过 ADC 获取转换的电压值换算成温度。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

127

主题

4778

帖子

28

粉丝
快速回复 在线客服 返回列表 返回顶部