当前位置: 首页 > news >正文

Cortex-M3-STM32F1 开发:(四十六)ADC 实例 4 :单通道 ADC 过采样实例,使用 DMA,两种编程方式

上一篇下一篇
ADC 实例 3 :多通道 ADC 采集实例,使用 DMA

目 录

  • 3.4)单通道 ADC 过采样实例,使用 DMA
    • ① 背景:
    • ② 原理:
    • ③ 代价:
    • ④ 上限:
    • ⑤ 公式与计算:
    • ⑥ 举例:
    • ⑦ 实验:
    • ⑧ 代码与实验结果:

3.4)单通道 ADC 过采样实例,使用 DMA

方程推导过程:https://max.book118.com/html/2018/0506/165038217.shtm

提高 ADC 分辨率一般来说有三种方法:

  • 采用 H7 这种分辨率为 16 位的单片机;
  • 外接专用的高分辨率 ADC 采集板,然后单片机用串口或IIC这些通信接口读取;
  • 过采样+取平均提高等效分辨率

① 背景:

ADC 的有效分辨率并非仅由其量化位数决定,而是受到系统噪声水平的显著影响。当噪声幅度接近或超过量化步长时,部分低位信息将被噪声淹没。通过降低噪声功率或采用过采样与平均等方法提高信噪比,可有效降低噪声地板(让更小的信号变化能“浮出噪声地板”),从而提升系统的最小可分辨信号变化量,即提高等效分辨率。

这个多出来的是“等效分辨率”,反映更小更稳定的读数,而不是 ADC 真正能区分的最小电压单位。

② 原理:

由独立随机变量方差叠加规律(类似大数定理)可得,取均值可以降低类白噪声(均匀量化噪声+热噪声)的方差,提高 SNR 。而想要取均值的同时提高分辨率,就需要在取均值之前进行过采样(类白噪声:均值为 0 ,样本间不相关)。

③ 代价:

过采样和求均值对 SNR 和测量分辨率的改善是以增加 CPU 时间和降低数据吞吐率为代价的。

④ 上限:

过采样和取平均,本质上只能消除 ADC 测量中的随机抖动。当系统噪声主要来自热噪声或量化噪声时,增加平均次数可以让 ADC 的读数更细腻,从而获得比标称位数更高的等效分辨率。但当随机噪声已经被压到很低之后,剩下的误差主要来自低频噪声、非线性和参考电压不稳定等因素,这些误差无法通过平均消除,此时继续过采样只能让数值看起来更稳定,而无法再分辨更小的电压变化。

以 STM32F103 的 12 位 ADC 为例,在 3.3 V 参考条件下,其理论最小分辨电压为约 0.8 mV,但在实际系统中,未进行过采样时 ADC 输出抖动通常达到 2~3 LSB。通过 16~64 倍过采样与平均,可将随机噪声有效降低至亚 LSB 量级,使等效分辨率提升至约 11.5 bit。然而,当随机噪声被充分抑制后,低频噪声、非线性误差及参考电压波动成为主要限制因素,此时继续增加平均次数已难以进一步提高系统的有效分辨能力。

⑤ 公式与计算:

结合原理,根据要增加的分辨率位数计算过采样频率方程
f o s = 4 w ⋅ f s \Large f_{os} = 4^w ⋅ f_sfos=4wfs
其中f o s f_{os}fos是过采样频率,w ww是希望增加的分辨率位数,f s f_sfs是原采样频率。

假设原分辨率为N NN,则新的等效分辨率为N + w N+wN+w,想要获得一次分辨率为N + w N+wN+w的数据,那么就要以原分辨率采样4 w 4^w4w次,然后将这4 w 4^w4w个结果求和再除以2 w 2^w2w结果可能是小数,在工程中,我们往往是将求和结果右移w ww,以此来达到除以2 w 2^w2w并取整的效果,许多技术文档都是这样写的。然后再按新的量纲(最小刻度=V R E F + ÷ 2 N + w V_{REF+}÷2^{N+w}VREF+÷2N+w,刻度数=2 N + w 2^{N+w}2N+w换算成真实电压值。

【注意】求和结果除以2 w 2^w2w其实这是一种人为拉大刻度数的方法,后面要换算成实际电压时,要根据新的量纲来换算。其实际效果其实就等于 ”求和结果除以4 w 4^w4w,用 flaot 来存储结果,然后按原量纲(最小刻度=V R E F + ÷ 2 N V_{REF+}÷2^{N}VREF+÷2N)换算“(这种其实是学术理论上更正确的计算方法) 。

⑥ 举例:

要想让 12 位分辨率的 ADC 提高 1 位分辨率,采样频率就要提高4 1 = 4 4^1=441=4倍。

即需要 4 次采集,然后将这 4 次采集结果求和,求和的结果再取平均(除以2 1 = 2 2^1=221=2,二进制运算相当于右移 1 位),就得到提高等效分辨率后的结果(等效 13 位分辨率)。

⑦ 实验:

实验简要:

  • 功能描述:
    • 通过 ADC1 通道1(PA1)过采样实现 16 位分辨率采集 3.3V 的电压,并显示 ADC 转换的数字量及换算后的电压值
    • 使用 DMA 传输
    • 由于过采样这个方法的上限,这 16 位中其实只有 11/12 位左右的真实准确度
  • 确定最小刻度:
    • V R E F + = 3.3 V V_{REF+} = 3.3VVREF+=3.3V0 V ≤ V I N ≤ 3.3 V 0V≤V_{IN}≤3.3V0VVIN3.3V→ 最小刻度 =3.3 / 65536 V 3.3/65536V3.3/65536V
  • 确定转换时间:
    • 采样时间设置为最小值 1.5 个 ADC 时钟周期,可以得到转换时间为 1.17us * 256= 299.52us(得到 1 个有效数据)
  • 模式组合:
    • 连续转换模式、不使用扫描模式

⑧ 代码与实验结果:

adc.c 文件代码:

#include"adc.h"#include"delay.h"#include"usart.h"#include"dma.h"/* 全局变量 */ADC_HandleTypeDef hadc1;DMA_HandleTypeDef hdma_adc1;uint16_tadc_dma_buf[ADC_DMA_BUF_SIZE];volatileuint8_tadc_dma_done=0;/* ADC1 初始化 */voidMX_ADC1_Init(void){ADC_ChannelConfTypeDef sConfig={0};__HAL_RCC_ADC1_CLK_ENABLE();__HAL_RCC_DMA1_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();/* PA1 → ADC1_CH1 */GPIO_InitTypeDef gpio={0};gpio.Pin=GPIO_PIN_1;gpio.Mode=GPIO_MODE_ANALOG;HAL_GPIO_Init(GPIOA,&gpio);/* DMA1_Channel1 */hdma_adc1.Instance=DMA1_Channel1;hdma_adc1.Init.Direction=DMA_PERIPH_TO_MEMORY;hdma_adc1.Init.PeriphInc=DMA_PINC_DISABLE;hdma_adc1.Init.MemInc=DMA_MINC_ENABLE;hdma_adc1.Init.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD;hdma_adc1.Init.MemDataAlignment=DMA_MDATAALIGN_HALFWORD;hdma_adc1.Init.Mode=DMA_NORMAL;hdma_adc1.Init.Priority=DMA_PRIORITY_MEDIUM;HAL_DMA_Init(&hdma_adc1);/* ADC1 */hadc1.Instance=ADC1;hadc1.Init.ScanConvMode=ADC_SCAN_DISABLE;hadc1.Init.ContinuousConvMode=ENABLE;hadc1.Init.DiscontinuousConvMode=DISABLE;hadc1.Init.ExternalTrigConv=ADC_SOFTWARE_START;hadc1.Init.DataAlign=ADC_DATAALIGN_RIGHT;hadc1.Init.NbrOfConversion=1;HAL_ADC_Init(&hadc1);__HAL_LINKDMA(&hadc1,DMA_Handle,hdma_adc1);/* ADC 校准 */HAL_ADCEx_Calibration_Start(&hadc1);/* 通道配置 */sConfig.Channel=ADC_CHANNEL_1;sConfig.Rank=ADC_REGULAR_RANK_1;sConfig.SamplingTime=ADC_SAMPLETIME_1CYCLE_5;HAL_ADC_ConfigChannel(&hadc1,&sConfig);/* DMA 中断 */HAL_NVIC_SetPriority(DMA1_Channel1_IRQn,2,0);HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);}/* 启动一次 ADC DMA 采集 */voidADC_DMA_Start(void){adc_dma_done=0;HAL_ADC_Start_DMA(&hadc1,(uint32_t*)adc_dma_buf,ADC_DMA_BUF_SIZE);}/* DMA 传输完成回调 */voidHAL_ADC_ConvCpltCallback(ADC_HandleTypeDef*hadc){if(hadc->Instance==ADC1){adc_dma_done=1;}}/* DMA 中断入口 */voidDMA1_Channel1_IRQHandler(void){HAL_DMA_IRQHandler(&hdma_adc1);}

adc.h 文件代码:

#ifndef__ADC_H#define__ADC_H#include"sys.h"#defineADC_ADD_RESOLUTION4// ADC新增分辨率#defineADC_OVERSAMPLE_TIMES(1<<(2*ADC_ADD_RESOLUTION))// ADC过采样次数, 这里提高4bit分辨率, 需要256倍采样#defineADC_DMA_BUF_SIZE(10*ADC_OVERSAMPLE_TIMES)// ADC DMA采集 BUF大小, 应等于过采样次数的整数倍externADC_HandleTypeDef hadc1;externDMA_HandleTypeDef hdma_adc1;externuint16_tadc_dma_buf[ADC_DMA_BUF_SIZE];externvolatileuint8_tadc_dma_done;voidMX_ADC1_Init(void);voidADC_DMA_Start(void);#endif

main.c 文件代码:

#include"sys.h"#include"delay.h"#include"usart.h"#include"led.h"#include"key.h"#include"string.h"#include"dma.h"#include"adc.h"/* ==================================================================== */intmain(void){uint16_ti,j;uint32_tsum;floatv;/* 初始化 */HAL_Init();Stm32_Clock_Init(RCC_PLL_MUL9);delay_init(72);LED_Init();uart_init(115200);MX_ADC1_Init();/* 启动第一次 DMA 采集 */ADC_DMA_Start();while(1){if(adc_dma_done){printf("DMA采集完成,10个结果分别为:");for(i=0;i<ADC_DMA_BUF_SIZE/ADC_OVERSAMPLE_TIMES;i++){for(j=0;j<ADC_OVERSAMPLE_TIMES;j++){sum+=adc_dma_buf[(i*ADC_OVERSAMPLE_TIMES)+j];}v=sum>>ADC_ADD_RESOLUTION;v=v*3.3f/65535.0f;printf("%.6fV ",v);sum=0;}printf("\r\n");/* 再来一次 */ADC_DMA_Start();}LED0=!LED0;delay_ms(100);}}

实验结果:

从结果可以看出,与之前的 3.299194V 还是有一些差别的。


http://www.cnnetsun.cn/news/4123487.html

相关文章:

  • 收藏夹里500个“稍后再看“吃灰?这款免费AI视频总结工具帮你3分钟榨干B站课程
  • gruf 健康检查集成:接入官方 gRPC 健康检查协议,让 Ruby 微服务秒过探针
  • S-mall-ssm 订单状态机设计:5种状态如何驱动电商订单的完整生命周期?
  • 你的macOS菜单栏为什么总是乱成一团?这个叫Ice的菜单栏管理工具给出了答案
  • 老Mac免费吃上macOS Sequoia:OpenCore Legacy Patcher从零到上手完整指南
  • 磁盘空间告急怎么办?免费开源Czkawka的14个清理工具,帮你找出重复文件、相似图片和空文件夹
  • 深入源码:THCalendarDatePicker 日历网格布局算法是如何工作的?
  • 老款Mac焕新三步走:用OpenCore Legacy Patcher免费升级最新macOS完整指南
  • DesktopAssistant打包发布实战:生成Windows安装程序与deb包的完整步骤
  • ZCode与DeepSeek集成指南:打造本地AI编程助手
  • Dtims CPB4612-LF 控制板
  • 对比评测:GifHub vs 其他 GitHub GIF 插件,谁才是评论区动图之王?
  • TypeScript-New-Handbook 新手教程:10 分钟理解静态类型检查的核心理念
  • TypeScript-New-Handbook 完全指南:微软官方 TypeScript 新手册,一篇读懂全貌
  • 磁盘清理工具免费推荐:开源 Czkawka,把重复文件与相似图片一次清干净
  • THCalendarDatePicker 手势交互揭秘:滑动切换月份与年份的 5 个实现细节
  • 基于SpringBoot+Vue的个性化音乐平台网站(源码+讲解视频+LW)
  • obs-shaderfilter扩展像素功能解析:轻松实现投影与边框的渲染技巧
  • 【软考信息安全】密码学考点“涮“明白!5张美食思维导图带你一锅端
  • 给 Copilot for Xcode 开发自定义工具:3 个实战案例,把 AI 助手调教成真正会干活的搭档
  • ECC 智能体钩子系统架构解析:如何为 AI 编码 Agent 装上“会记忆、能拦截、可审计“的行为护栏
  • 洛雪音乐音源怎么装最省心?免费无损音源合集实测导入指南
  • 免费通用设计字体 Morisawa BIZ UDGothic 上手教程:三步搞定下载、安装与排版
  • FIFA 23 修改器完整免费指南:从首条脚本到高阶模组,快速掌控生涯模式全细节
  • 微信聊天记录终极备份指南:用WeChatMsg免费导出HTML、Word、CSV与年度报告
  • ZoneMTA 智能退信处理:如何识别灰名单、拒收与 IP 拉黑
  • 小芯片运行模型时的资源边界
  • vue-star-rating 无障碍实践:用 screen-reader 插槽提升评分的可访问性
  • gruf 性能调优指南:线程池与服务器参数最佳实践
  • 基于微信小程序的剧本杀预约系统(源码+lw+部署文档+讲解等)