[APM32F4] 【APM32F402R Micro-EVB】07:调试ADC的功能,通过串口1输出ADC数据

[复制链接]
聪聪哥哥 发表于 2025-8-25 16:52 | 显示全部楼层 |阅读模式
一:APM32F402的ADC知识分享:
  系列产品有 2个 ADC,精度为 12 位,每个 ADC 最多有 16 个外部通道和2个内部通道。各通道 AD 转换模式有单次、连续、扫描或间断,ADC 转换结果可以左对齐或右对齐存储在 16 位数据寄存器中。
二:ADC的简单编写思路:
首先开启PA口时钟和ADC1时钟,设置PA1为模拟输入模式,否则AD不能正常读数程序开启之前复位ADC1,同时设置ADC1分频因子和ADC的位数等相关信息。
初始化ADC1参数,配置规则通道参数:
开启软件转换: 等待转换完成,读取ADC值。
简单说明一下具体的思路
1:系统时钟的选择:根据电路板的硬件设计,选择外部时钟,主频设置为120MHZ,然后配置ADC的时钟分频器,确保ADC在工作时频率正确,最后使能使用的ADC1的时钟
2:ADC的基本参数配置:
选择合适的ADC模式,APM32F402支持多种转换模式,单次转换和多次转换的模式,我个人认为还是使用连续多次转换的模式效果更好些。设置好合适的采样周期,ADC的采集到的数据不仅仅和外部传感器输入的实际数据有关,和采样的频率也息息相关,经过我个人的工作经历来说,合适的采样时间才能采集到准确的AD数据,通常采集频率过快,会导致ADC数据不稳定。较高的采样时间会增加数据的稳定性,但是这样会增加程序运行的时间。
三软件代码如下所示:
3.1 ADC的初始化函数
  1. void ADC_Init(void)
  2. {
  3.     GPIO_Config_T GPIO_ConfigStruct;
  4.     ADC_Config_T  ADC_ConfigStruct;
  5.     /* Enable GPIOA clock */
  6.     RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA);
  7.     /* ADC channel 0 configuration */
  8.     GPIO_ConfigStructInit(&GPIO_ConfigStruct);
  9.     GPIO_ConfigStruct.mode    = GPIO_MODE_ANALOG;
  10.     GPIO_ConfigStruct.pin     = GPIO_PIN_0;
  11.     GPIO_Config(GPIOA, &GPIO_ConfigStruct);
  12.     /* ADCCLK = PCLK2/4 */
  13.     RCM_ConfigADCCLK(RCM_PCLK2_DIV_4);
  14.     /* Enable ADC clock */
  15.     RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC1);
  16.     /* ADC configuration */
  17.     ADC_Reset(ADC1);
  18.     ADC_ConfigStructInit(&ADC_ConfigStruct);
  19.     ADC_ConfigStruct.mode                  = ADC_MODE_INDEPENDENT;
  20.     ADC_ConfigStruct.scanConvMode          = DISABLE;
  21.     ADC_ConfigStruct.continuousConvMode    = ENABLE;
  22.     ADC_ConfigStruct.externalTrigConv      = ADC_EXT_TRIG_CONV_NONE;
  23.     ADC_ConfigStruct.dataAlign             = ADC_DATA_ALIGN_RIGHT;
  24.     /* channel number */
  25.     ADC_ConfigStruct.nbrOfChannel          = 1;
  26.     ADC_Config(ADC1, &ADC_ConfigStruct);
  27.     /* ADC channel Convert configuration */
  28.     ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_13CYCLES5);
  29.     /* Enable complete conversion interupt */
  30.     ADC_EnableInterrupt(ADC1, ADC_INT_EOC);
  31.     /* NVIC configuration */
  32.     NVIC_EnableIRQRequest(ADC1_2_IRQn, 1, 1);
  33.     /* Enable ADC */
  34.     ADC_Enable(ADC1);
  35.     /* Enable ADC1 reset calibration register */
  36.     ADC_ResetCalibration(ADC1);
  37.     /* Check the end of ADC1 reset calibration register */
  38.     while (ADC_ReadResetCalibrationStatus(ADC1));
  39.     /* Start ADC1 calibration */
  40.     ADC_StartCalibration(ADC1);
  41.     /* Check the end of ADC1 calibration */
  42.     while (ADC_ReadCalibrationStartFlag(ADC1));
  43.     /* Start ADC1 Software Conversion */
  44.     ADC_EnableSoftwareStartConv(ADC1);
  45. }
3.2 编写ADC转换完成的回调函数:
  1. void ADC_callback(void)
  2. {
  3.     /* Read ADC Conversion value */
  4.     iAdcData = ADC_ReadConversionValue(ADC1);
  5. //   printf("采集到编码器的原始数据 = : %4d \r\n", iAdcData);
  6. }
  1. void ADC1_2_IRQHandler(void)
  2. {
  3.     if (ADC_ReadIntFlag(ADC1, ADC_INT_EOC) == SET)
  4.     {
  5.         ADC_callback();
  6.         ADC_ClearIntFlag(ADC1, ADC_INT_EOC);
  7.     }
  8. }
3.3 在时间任务回调函数中,添加输出采集到的ADC数据函数:
  1. void task_100ms(void)
  2. {
  3.         //Usart1Txbuffer();
  4.         printf("采集到编码器的原始数据 = : %4d \r\n", iAdcData);
  5. }        
四:测试图片如下:
07-1.png
07-2.png
旋转电位器,可以观察到采集的ADC数据变化过程。
分享一下ADC数据处理方法如下所示:
1: 采用中值算法滤波
主要是程序在执行的时候,连续采集N次(需要注意下这里下,这里的N必须取值奇数),程序需要按大到小或者从小到达的顺序进行排序,然后取中间数值做为有效值。
  1. int MidValueDeal(int N)
  2. {      
  3.     int value_buf[N];      int i,j,k,temp;      
  4.     for( i = 0; i < N; ++i)      
  5.     {        
  6.      value_buf[i] = HAL_ADC_GetValue(&hadc1);  
  7.     }
  8.       for(j = 0 ; j < N-1; ++j)      
  9.       {         
  10.           for(k = 0; k < N-j-1; ++k)         
  11.           {              //从小到大排序,冒泡法排序              
  12.               if(value_buf[k] > value_buf[k+1])              
  13.               {               
  14.                   temp = value_buf[k];               
  15.                   value_buf[k] = value_buf[k+1];               
  16.                   value_buf[k+1] = temp;              
  17.               }         
  18.           }      
  19.       }
  20.       return value_buf[(N-1)/2];
  21.   }
2:算术平均数滤波
连续取值N个数据,对所有的数据进行取平均值;
代码如下:
  1. int AverValueDeal(int N)
  2. {   
  3.     int sum = 0;     
  4.     unsinged short i;   
  5.     for(i = 0; i < N; ++i)     
  6.     {        
  7.         sum += HAL_ADC_GetValue(&hadc1);      
  8.     }     
  9.     return sum/N;
  10. }
这里取值时候,需要根据被控对象进行选择,N值取值过大,会导致响应过慢,数据的灵敏度过低,所以采用算术平均值滤波时候,需要我们格外的注意;
3:滑动滤波算法
把连续取N个采样值看成一个队列,队列的长度固定为N。每次采样到一个新数据放入队尾,并扔掉原来队首的一次数据(先进先出原则)。把队列中的N个数据进行算术平均运算,就可获得新的滤波结果;
  1. #define N 10
  2. int value_buf[N];
  3. int sum=0; //数据总和
  4. int Num=0; //当前队列中数组下标
  5. int movedataFilter()
  6. {      
  7.     if(Num < N)      
  8.     {         
  9.         value_buf[Num] = HAL_ADC_GetValue(&hadc1);         
  10.         sum += value_buf[Num];         
  11.         Num++;         
  12.         return sum/Num;      
  13.     }      
  14.     else      
  15.     {         
  16.        sum -= sum/N;         
  17.        sum += HAL_ADC_GetValue(&hadc1);         
  18.        return sum/N;      
  19.     }
  20. }
空灵回声 发表于 2025-8-26 10:19 | 显示全部楼层
楼主 您的这个电位器看着好精致啊!
话说,楼主您的代码里面好多的除法运算呀
 楼主| 聪聪哥哥 发表于 2025-8-26 11:02 | 显示全部楼层
空灵回声 发表于 2025-8-26 10:19
楼主 您的这个电位器看着好精致啊!
话说,楼主您的代码里面好多的除法运算呀 ...

啊 做的数据处理取得平均数据,这样数据稳定些
您需要登录后才可以回帖 登录 | 注册

本版积分规则

93

主题

238

帖子

1

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