stm32中的adc多次采样求均值减少误差的方法

就是我用的是f429挑战者的板子。用adc接地的时候发现有零点误差,于是就移植了网上一个f103的例程,通过多次测量求均值的方法来实现adc的精准测量。
思路大概就是先定义一个get_adc_average的函数,在函数里用
for(t=0;t<times;t++)
    {
       
        temp_val+=Get_Adc();
        
    }
实现求平均值。然后Get_Adc()程序如下:

u16 Get_adc()  
{
      //设置指定ADC的规则组通道,一个序列,采样时间
    ADC_RegularChannelConfig(RHEOSTAT_ADC, RHEOSTAT_ADC_CHANNEL, 1, ADC_SampleTime_56Cycles);;    //ADC1,ADC通道,采样时间为239.5周期                     
  ADC_Cmd(RHEOSTAT_ADC, ENABLE);
        ADC_SoftwareStartConv(RHEOSTAT_ADC);    //使能指定的ADC1的软件转换启动功能   
     
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束

    return ADC_GetConversionValue(ADC1);    //返回最近一次ADC1规则组的转换结果
}

编译正常。然后运行的时候发现屏幕显示一次数后就不跳变了。

然后用野火的仿真器硬件仿真。发现程序卡死在while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));一直出不来。
参考官方库,发现对ADC_GetFlagStatus就是:

if ((ADCx->SR & ADC_FLAG) != (uint8_t)RESET)
  {
    /* ADC_FLAG is set */
    bitstatus = SET;
  }
  else
  {
    /* ADC_FLAG is reset */
    bitstatus = RESET;
  }

  return  bitstatus;
然后在看while内的内容,就是要求执行else后的程序段,就是要求(ADCx->SR & ADC_FLAG)=0
其中根据我设置好的ADC_FLAG_EOC是0x02,即二进制的10.而sr寄存器对应的该位的确是EOC.
还有在f429的参考手册中EOC置位表示转化已完成。所以这样配置应该没有错啊。
然后吊诡的事情是当我把while里面的!去掉的时候再次硬件调试,发现程序依旧卡死在while里面。
然后分析到这里就分析不下去了,所以就在这里问如下三个问题:
1.为什么会卡在这里并且怎么解决?
2.为什么我将!去掉之后仍然是这种情况
3.对于sr的相关位我发现都是硬件配置,我可以通过软件来配置吗?


我把这个问题放在论坛上好几天,但是没有人来回答我。于是我开始自己想办法。

既然程序是卡在while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));,那么我能不能不调用这个语句而实现连续读取adc值得功能呢?

答案是肯定的。

我们可以将adc的读取函数写在adc转化中断里。因为每次adc读取完毕都可以触发中断,也就是触发中断的时候一定是完成adc转化的时候,所以就自然不用用while来判断adc是否转化完毕。具体的配置就是adc使能转化完毕中断,adc采用连续转化模式。

中断里的代码如下:

if(ADC_GetITStatus(RHEOSTAT_ADC,ADC_IT_EOC)==SET)
    {
        if(i!=10)
        {
  // 读取ADC的转换值
        V+= ADC_GetConversionValue(RHEOSTAT_ADC);
       i++;
        }
        else
            {
                ADC_ConvertedValue=V/(float)(10.0);
              i=0;
              V=0;
            
          }
    }
    ADC_ClearITPendingBit(RHEOSTAT_ADC,ADC_IT_EOC);

}   

这里在函数里定义char i和float V;i用来实现计算转化次数的功能,然后V用来实现存储每次数据的功能。而ADC_ConvertedValue则是在main.c里定义的一个外部变量。这里就用中断实现了求平均值来求adc的精确值。


然后这回其实最大的收获是以后在移植程序出现问题的时候,会不在纠结问题出在那里,而是试图去尝试自己写一段代码来解决问题。


### STM32 ADC多通道DMA采样均值计算 #### 配置环境与初始化 为了实现STM32ADC多通道DMA采样并计算平均值,首先需要通过STM32CubeMX配置项目。选择目标MCU型号(如STM32F103C8或STM32F407),然后添加ADC外设及其DMA支持[^1]。 在STM32CubeMX界面中,点击`Add`按钮来增加ADC模块对应的DMA流/通道,并将工作模式设定为循环模式(Circular Mode),这能确保数据持续不断地被转移到内存缓冲区直到程序停止该过程[^2]。 #### 编写代码逻辑 完成硬件抽象层(HAL)库的基础设置之后,在应用程序源文件里定义必要的宏常量用于指定参与测量的信道数量以及每条路径上获取样本的数量: ```c #define NUM_CHANNELS 2 /* 被测物理量数目 */ #define NUM_SAMPLES_PER_CHANNEL 100 /* 单次取样次数 */ uint32_t dma_adc_buffer[NUM_CHANNELS * NUM_SAMPLES_PER_CHANNEL]; // DMA接收缓存 uint32_t dma_adc_buffer_av[NUM_CHANNELS]; // 平均值保存位置 ``` 接着编写函数处理接收到的数据,这里提供了一个简单的例子展示如何遍历所有收集到的结果进而得各路输入信号强度的算术平均数: ```c void CalculateAverage(void){ int i, j; for(i = 0; i < NUM_CHANNELS ; ++i){ // 对每一个通道执行操作 uint32_t sum = 0; for(j = 0; j < NUM_SAMPLES_PER_CHANNEL; ++j){ sum += dma_adc_buffer[i * NUM_SAMPLES_PER_CHANNEL + j]; } dma_adc_buffer_av[i] = (sum / NUM_SAMPLES_PER_CHANNEL); // 计算平均值 } } ``` 此段代码会逐一遍历由DMA传输过来的数据集(`dma_adc_buffer`)中的每一项,累加以获得总和后再除以样品总数得到最终结果存储至`dma_adc_buffer_av[]`数组内表示各个传感器输出电压水平的大致情况[^4]。 #### 注意事项 - **稳定性考量**: 实际应用过程中可能会遇到噪声干扰等问题造成读数波动较大;因此建议适当调整`NUM_SAMPLES_PER_CHANNEL`参数大小以平衡响应速度与精度之间的关系。 - **资源占用评估**: 当涉及多个模拟输入端口的同时监测时,请务必确认所选微控制器具备足够的内部RAM空间容纳整个批次的数据记录。 - **校准机制引入**: 如果发现某些特定条件下存在系统误差,则可以在软件层面加入补偿算法修正偏差影响。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值