HAL库教程13:AD+DMA采集数据的滤波_hal_adc_convhalfcpltcallback-程序员宅基地

技术标签: HAL  STM32  DMA  算数平均  

  借助DMA可以快速采集大量数据,如果数据采集过来却不使用就是浪费。在我的板子上,AD值代表温度,如果系统检测到温度过高,可能会采取一些强制的保护措施。而AD采样是容易受干扰的,所以要对采样数据进行滤波,减少噪声对系统的干扰。接下来我们采用计算平均值的算法来滤波。
  我们从每256个数据中,提取出1个算数平均值。2个通道,每个通道采集256个数据的话,共需要采集512个数据。由于DMA转换数据是循环进行的,如果等512个数据全部采集完,再做算数平均的话,新的数据已经采集完毕,旧的数据被覆盖,发生了改变。因此可以采用分两段处理的思想。
  开启一个1024大小的数组,在前半段转化完成时,也就是前512个数据个数据已经处理完毕的时候,遍历前512个数据,计算算数平均值。此时DMA正在处理后512个数据,并且需要保证在后半段数据处理完之前,计算完前半段数据的平均值。然后等DMA处理完所有的数据以后,会从头开始,重新转换数据。我们要赶在DMA处理前半段数据的期间,计算出后半段数据的平均值。
  HAL库提供了HAL_ADC_ConvHalfCpltCallback函数,在转换完成一半时会调用。另外,由于前512个数据包含了两个通道的AD值,所以要分开计算平均值。函数HAL_ADC_ConvCpltCallback在转换完成时调用。为了区分是前半段还是后半段的数据处理,可以新增标志位。

//AD.c
u8 DMA_FLG = 0;
uint32_t ADC1_RANK1_AVG = 0;
uint32_t ADC1_RANK2_AVG = 0;

/**
  * @brief 获取不同通道的平均值
  * @param DMA转换进度,是前一半还是后一半
  * @retval None
  */
void Get_ADC_Avg(u8 flg)
{
    
  int start,end;
  int ADC1_RANK1_SUM = 0;
  int ADC1_RANK2_SUM = 0;
  if(flg)//DMA后半段
  {
    
    start = ADC_CHANNEL_CNT>>1;
    end = ADC_CHANNEL_CNT;
  }
  else
  {
    
    start = 0;
    end = ADC_CHANNEL_CNT>>1;
  }
  for(int i = start; i < end;i++)
  {
    
    if(0 == i%2)//偶数
    {
    
      ADC1_RANK1_SUM += AD_Buf[i];
    }
    else
    {
    
      ADC1_RANK2_SUM += AD_Buf[i];
    }
  }
  ADC1_RANK1_AVG = ADC1_RANK1_SUM/(ADC_CHANNEL_CNT>>2);
  ADC1_RANK2_AVG = ADC1_RANK2_SUM/(ADC_CHANNEL_CNT>>2);
}


/**
  * @brief ADC通道转化结束以后触发回调函数
  * @param 触发转换完成中的的ADC句柄
  * @retval None
  */    
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    
  if(hadc==(&hadc1))
  {
    
    DMA_CNT++;
    DMA_FLG = 1;
    Get_ADC_Avg(DMA_FLG);  
  }    
}

/**
  * @brief ADC通道转化完成一半时触发回调函数
  * @param 触发转换完成中的的ADC句柄
  * @retval None
  */    
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{
    
  if(hadc==(&hadc1))
  {
    
    DMA_FLG = 0;
    Get_ADC_Avg(DMA_FLG);  
  }
}

  在main函数中,打印平均AD值,并区分是前/后半段处理的结果。

//main()
  while (1)
  {
    
    HAL_Delay(1000);
    printf("采样次数: %d\n",ADC_CHANNEL_CNT/2);
    printf("TempA均值 %d ;TempB均值 %d. \n",ADC1_RANK1_AVG,ADC1_RANK2_AVG);
    if(DMA_FLG)
      printf("当前是后半段\n");
    else
      printf("当前是前半段\n");
    printf("DMA采集数据的次数是 %d\n",DMA_CNT);
    DMA_CNT=0;
  }

  观察现象可以发现,当1s的时间到达的时候,刚刚处理的是DMA的前半段数据还是后半段,并不能确定。但不论是前后半段,由于这种类似于“互斥”做法,在读取数据计算平均值的时候,可以保证数据没有被“篡改”。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/geek_monkey/article/details/89327369

智能推荐

【Linux】进程的优先级&&环境变量-程序员宅基地

文章浏览阅读1.4k次,点赞67次,收藏75次。上一篇在进程中提到了【Linux】进程状态&&僵尸进程和孤儿进程&&阻塞、挂起和运行,这次来继续来谈进程。在进程的PCB中存在一个进程的优先级,那么什么是进程的优先级?进程的优先级就是指定一个进程获取某种资源的顺序。进程中使用task_struct进程控制块结构体中的内部字段用一个整型prio表示优先级。Linux中优先级数字越小,优先级越高。比较一下优先级和权限:权限决定一件事能不能做,而有优先级就表示一件事情能做只是代表获取资源的顺序。

振动信号常用的时域和频域指标_振动信号时域分析-程序员宅基地

文章浏览阅读6.5k次,点赞6次,收藏68次。通常振动信号为一时间序列,衡量振动信号的指标包括时域指标和频域指标,网上分享计算公式和源程序的比较少,本文给出了公式定义以及matlab/python源码。常见时域指标:对应的Matlab程序%%%%matlab程序close allclearclcst = 0.01;data = sin(0:st:10);Xr = mean(sqrt(abs(data)))*mean(sqrt(abs(data)));Xmean = mean(abs(data));Xrms = rm_振动信号时域分析

Python: sklearn库中数据预处理函数fit_transform()和transform()的区别_fit_transform函数参数-程序员宅基地

文章浏览阅读1.4k次。敲《Python机器学习及实践》上的code的时候,对于数据预处理中涉及到的fit_transform()函数和transform()函数之间的区别很模糊,查阅了很多资料,这里整理一下:涉及到这两个函数的代码如下:# 从sklearn.preprocessing导入StandardScalerfrom sklearn.preprocessing import StandardScale..._fit_transform函数参数

一起talk C栗子吧(第四十七回:C语言实例--走迷宫一)-程序员宅基地

文章浏览阅读1.2k次。图文并茂走迷宫_一起talk c栗子吧

基于SpringBoot+微信小程序的失物招领小程序(前后端分离)-程序员宅基地

文章浏览阅读666次,点赞29次,收藏20次。JAVA:Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程。Vue:Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的JavaScript框架。

【C语言】数据在内存中的存储-程序员宅基地

文章浏览阅读1.3k次,点赞72次,收藏27次。字节序——是以字节为单位,来讨论存储顺序的其实超过一个字节的数据在内存中存储的时候,就有存储顺序的问题,按照不同的存储顺序,我们分为大端字节序存储和小端字节序存储,下面是具体的概念:大端(存储)模式:是指数据的低位字节内容保存在内存的高地址处,而数据的高位字节内容,保存在内存的低地址处。小端(存储)模式:是指数据的低位字节内容保存在内存的低地址处,而数据的高位字节内容,保存在内存的高地址处。

随便推点

不用USB连接线或没有ADB驱动如何调试安卓_不使用usb调试连接电脑-程序员宅基地

文章浏览阅读1.2w次,点赞5次,收藏18次。USB数据线的调试方式,大家应该比较常用,今天就专门说说怎么在不使用USB数据线的方式下进行安卓的开发调试。_不使用usb调试连接电脑

MySQL 创建表时出现 Tablespace for `xxx`.`xxx` exists._please discard the tablespace before import.-程序员宅基地

文章浏览阅读2.7k次,点赞2次,收藏2次。但在 SQLyog 上查看时却是不存在的。这是因为 MySQL 异常停止后,导致某些文件丢失或损害引起的,具体为什么会到至 MySQL 异常停止,只能进一步查看。本地 MySQL 异常停止后,手动启动服务,热庵后执行 sql 脚本时,发现又报错信息,于是手动创建表,发现还是无法创建,报了。表空间已存在,在导入数据之前需要将表空间释放掉。_please discard the tablespace before import.

四足机器人|机器狗|仿生机器人|多足机器人|PPT|汇报|科研汇报PPT|技术汇报_四足机器人关键技术ppt-程序员宅基地

文章浏览阅读2.7k次,点赞11次,收藏28次。四足机器人|机器狗|仿生机器人|多足机器人|PPT|汇报|科研汇报PPT|技术汇报_四足机器人关键技术ppt

org.springframework.data.redis.serializer.SerializationException: Could not read JSON-程序员宅基地

文章浏览阅读1k次。org.springframework.data.redis.serializer.SerializationException: Could not read JSON将数据存储到redis中报错,由于对象(实体)中缺少json的某个字段属性引起解决办法。@JsonIgnoreProperties(ignoreUnknown = true) _org.springframework.data.redis.serializer.serializationexception: could not

Angular官网学习4:Angular入门,你的第一个应用(4)输出_angular notifychange$-程序员宅基地

文章浏览阅读375次。在本节中,将设置商品提醒组件,当用户点击‘Notify Me’的时候,像商品列表组件发出事件。1、打开 product-alerts.component.ts, 从 @angular/core 中导入 Output 和 EventEmitter。2、在组件类中,用 @Output 装饰器和一个事件发射器(EventEmitter)实例定义一个名为 notify 的属性。这可以让商品提醒组件在 ..._angular notifychange$

[flask 优化] 由flask-bootstrap,flask-moment引起的访问速度慢的原因及解决办法-程序员宅基地

文章浏览阅读661次。一周时间快速阅读了400页的《javascript基础教程》,理解了主要概念。解决了一个很久之前的疑问。我的网站是使用flask框架搭建的,介绍flask web的一本著名的书(之前提到过)作者搭建个人博客时,向读者推荐了flask-bootstrap,flask_moment这两个库,前者能快速的解决前端样式问题,后者提供了时间戳功能。但在某种情况下,比如网络延迟或者运营商的问题,访问网..._bootstrap 速度慢 maxcdn