文章目录

  • 前言
  • STM32CubeMX配置
  • ADC DMA工作流程注意事项
  • 相关代码和测试结果
  • 内存分配
  • 采样率计算
  • 内部温度传感器
  • 参考

前言

前段时间被STM32H7的以太网和printf坑的不行, 昨天又被ADC坑了一下午, 这里怎么也得提笔记录一下了. 这次先来总结一下注意的地方:

  • 开DCache
  • ADC1的初始化不自动生成, 手动放到DMA1初始化后面, 数组定义到AXI SRAM, 如0x24000000
  • ADC3的初始化不自动生成, 手动放到BDMA初始化后面, 数组定义到SRAM4, 如0x38000000
  • SCB_InvalidateDCache_by_Addr (void *addr, int32_t dsize) 使得数据更新

走通了流程, 解决了眼前困难, 留下一些问题, 接下来进入正文.

STM32CubeMX配置

这里只介绍ADC1+DMA1ADC3+BDMA相关的配置

ADC时钟默认75MHz(可能为了xx对齐, 也有很多人调成72M)

ADC1用了四个通道

DMA1的数据宽度用半字, 因为16bit精度, 定义uint16_t数组就可以了, 多了浪费, 所以这里是Half Word. 如果非要定义成uint32_t, 那DMA1这里的数据宽度就选Word

ADC3用了8个通道(7个外部+1个内部的温度传感器)

对应的BDMA数据宽度也是半字

开启DCache(牵涉到DMA的一般要开启DCache, 不然可能下载进去运行, 但是断电再上电就不运行的情况):

不生成初始化调用, 为了让ADC的初始化放到DMA初始化的后面

ADC DMA工作流程注意事项

stm32_h7xx_hal_adc.c里面

        (++) ADC conversion with transfer by DMA:(+++) Activate the ADC peripheral and start conversionsusing function HAL_ADC_Start_DMA()(+++) Wait for ADC conversion completion by call of functionHAL_ADC_ConvCpltCallback() or HAL_ADC_ConvHalfCpltCallback()(these functions must be implemented in user program)(+++) Conversion results are automatically transferred by DMA intodestination variable address.(+++) Stop conversion and disable the ADC peripheralusing function HAL_ADC_Stop_DMA()

STM32CUBE_FW_H7_V1.9.0中的ADC_DMA_Transer例子, readme.txt

@Note If the  application is using the DTCM/ITCM memories (@0x20000000/ 0x0000000: not cacheable and only accessibleby the Cortex M7 and the  MDMA), no need for cache maintenance when the Cortex M7 and the MDMA access these RAMs.If the application needs to use DMA(or other masters) based access or requires more RAM, then  the user has to:- Use a non TCM SRAM. (example : D1 AXI-SRAM @ 0x24000000)- Add a cache maintenance mechanism to ensure the cache coherence between CPU and other masters(DMAs,DMA2D,LTDC,MDMA).- The addresses and the size of cacheable buffers (shared between CPU and other masters)must be   properly defined to be aligned to L1-CACHE line size (32 bytes).

相关代码和测试结果

先是定义到指定地址的数组, 为什么下面再解释

#define ADC1_BUFFER_SIZE 32*4    //ADC1用了4通道, 存32组, 方便做平均
#define ADC3_BUFFER_SIZE 32*8   //ADC3用了8通道, 存32组, 方便做平均//32字节对齐(地址+大小)
//adc1_data指定到 AXI SRAM 的0x24000000
//adc3_data指定到 SRAM4 的0x38000000
ALIGN_32BYTES (uint16_t adc1_data[ADC1_BUFFER_SIZE]) __attribute__((section(".ARM.__at_0x24000000")));
ALIGN_32BYTES (uint16_t adc3_data[ADC3_BUFFER_SIZE]) __attribute__((section(".ARM.__at_0x38000000")));

ADC的初始化

void adc_init(void)
{MX_ADC1_Init();    //初始化调用放这里, 确保在MX_DMA_Init()初始化后面MX_ADC3_Init();    //在MX_BDMA_Init()初始化后面//HAL_Delay(100); //有地方说这里可以等等电压稳定后再校准if (HAL_ADCEx_Calibration_Start(&hadc1, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK){printf("hadc1 error with HAL_ADCEx_Calibration_Start\r\n");Error_Handler();}if (HAL_ADCEx_Calibration_Start(&hadc3, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK){printf("hadc3 error with HAL_ADCEx_Calibration_Start\r\n");Error_Handler();}if (HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc1_data, ADC1_BUFFER_SIZE) != HAL_OK){printf("hadc1 error with HAL_ADC_Start_DMA\r\n");Error_Handler();}if (HAL_ADC_Start_DMA(&hadc3, (uint32_t *)adc3_data, ADC3_BUFFER_SIZE) != HAL_OK){printf("hadc3 error with HAL_ADC_Start_DMA\r\n");Error_Handler();}
}

类似ping-pong存储

ADC转换半满中断中把数据存到数组的前半部分?

ADC转换完成中断中把数据存到数组的后半部分?

void SCB_InvalidateDCache_by_Addr (void *addr, int32_t dsize)dsize是字节数, 所以ADC1_BUFFER_SIZE刚好是uint16_t类型adc1_data数组一半的容量, 这点要特别注意

如果嫌中断太频繁, 可以把SCB_InvalidateDCache_by_Addr((uint32_t *)adc1_data, sizeof(adc1_data));放到读取之前.

【STM32H7教程】第46章 STM32H7的ADC应用之DMA方式多通道采样 里面解释了数据一致性的问题

由于STM32H7 Cache的存在,凡是CPU和DMA都会操作到的存储器,我们都要注意数据一致性问题。对于本章节要实现的功能,要注意读Cache问题,防止DMA已经更新了缓冲区的数据,而我们读取的却是Cache里面缓存的。这里提供两种解决办法:

  • 方法一: 关闭DMA所使用SRAM存储区(MPU操作的)。
  • 方法二: 设置SRAM的缓冲区做32字节对齐,大小最好也是32字节整数倍,然后调用函数SCB_InvalidateDCache_by_Addr做无效化操作即可,保证CPU读取到的数据是刚更新好的。

由于CubeMX里面开了DCache, 但没有用MPU, 就用第二种方法

uint32_t flag0 = 0;
uint32_t flag1 = 0;
uint32_t flag2 = 0;
uint32_t flag3 = 0;void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{/* Invalidate Data Cache to get the updated content of the SRAM on the first half of the ADC converted data buffer */if(hadc->Instance == ADC1) {flag0++;SCB_InvalidateDCache_by_Addr((uint32_t *) &adc1_data[0], ADC1_BUFFER_SIZE);} else if(hadc->Instance == ADC3) {flag1++;SCB_InvalidateDCache_by_Addr((uint32_t *) &adc3_data[0], ADC3_BUFFER_SIZE);}
}void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{/* Invalidate Data Cache to get the updated content of the SRAM on the second half of the ADC converted data buffer */if(hadc->Instance == ADC1) {flag2++;SCB_InvalidateDCache_by_Addr((uint32_t *) &adc1_data[ADC1_BUFFER_SIZE/2], ADC1_BUFFER_SIZE);} else if(hadc->Instance == ADC3) {flag3++;SCB_InvalidateDCache_by_Addr((uint32_t *) &adc3_data[ADC3_BUFFER_SIZE/2], ADC3_BUFFER_SIZE);}}

看看1s到底转换了多少次, 把结果和温度打印出来

void soft_timer_1000ms_callback(void)
{if(flag0) {printf("flag0 = %d\r\n", flag0);flag0 = 0;}if(flag1) {printf("flag1 = %d\r\n", flag1);flag1 = 0;}if(flag2) {printf("flag2 = %d\r\n", flag2);flag2 = 0;}if(flag3) {printf("flag3 = %d\r\n", flag3);flag3 = 0;}printf("\r\n");//温度计算uint32_t TS_DATA = 0;for(uint32_t i = 7; i < ADC3_BUFFER_SIZE; i+=8) {TS_DATA += adc3_data[i];}TS_DATA /= 32; //32组数据求平均uint16_t TS_CAL1 = *(__IO uint16_t *)(0x1FF1E820);uint16_t TS_CAL2 = *(__IO uint16_t *)(0x1FF1E840);float temperature = (110.0-30.0)/(TS_CAL2-TS_CAL1) * (TS_DATA - TS_CAL1) + 30.0;printf("temperature = %d, %.2f\r\n", TS_DATA, temperature);//这里没有考虑ping-pong转换, 直接全取了, 新旧数据差别不是太大?printf("\r\n+++++++++++++++++++++++++++++++++++++++\r\n");for(uint32_t i = 0; i < ADC1_BUFFER_SIZE; i++) {if(i % 4 == 0) {printf("\r\n%d    ", i/4);}printf("%d, ",adc1_data[i]);}printf("\r\n$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\r\n");for(uint32_t i = 0; i < ADC3_BUFFER_SIZE; i++) {if(i % 8 == 0) {printf("\r\n%d    ", i/8);}printf("%d, ", adc3_data[i]);}printf("\r\n");
}

结果如下, 两组

  • ADC1(PA6, PB1, PB0, PC0)
  • ADC3(PF9, PF7, PF5, PF3, PF8, PF6, PF4, TSensor)

PB1, PF6接GND, PB0接3V3, 温度传感器报出来的温度是41.86℃

内存分配

用户手册里面

先来看内存的地址

再来看总线联系

图中画X的是可以访问的:

  • DMA1-MEM访问不了默认的DTCM(0x20000000开始), 但可以访问AXI SRAM(0x24000000)
  • BDMA只能访问SRAM4(0x38000000开始)

关于SRAM的说明里面也明确指明了这一点, 如以太网和USB只能访问SRAM3(0x30040000):

系统架构图也有说明:

采样率计算

按24MHz, 整周期对齐可能计算比较准确, 这里要求不高, 有数就行

# ADC时钟源75MHz
# 6分频后 75/6=12.5MHz, 这就是ADC Clock
# 16bit分辨率
# 转换时间 = 采样时间 + 逐次逼近时间
# TCONV = Sampling time + TSAR = 64.5 + 8.5 = 73 ADC clock cycles
# 转换频率 = 12.5M / 73 = 171.233KHz
# 转换周期 = 1 / 171.233kHz = 5.84us# 334和668的计算?
# 5.84 * 32 * 8 * 334 = ? 少一个 * 2

8.5是24MHz下, 16bit分辨率对应的TSAR, 如下, 不太清楚对12.5MHz是怎样的, 所以可以调整时钟树, 让时钟源72MHz, 这样好算一点

内部温度传感器

内部温度传感器的测量范围在-40~125℃,

在datasheet里面

参考

如下

  • stm32h743 datasheet
  • stm32h743 usermanual
  • 【STM32H7教程】第44章 STM32H7的ADC基础知识和HAL库API
  • 【STM32H7教程】第46章 STM32H7的ADC应用之DMA方式多通道采样
  • STM32H743+CubeMX-ADC+DMA采样四路AD
  • STM32H743关于ADC与DMA不工作问题
  • STM32H743Nucleo ADC使用DMA配置无法读取数据的问题及解决
  • stm32H7 CubeMX 配置ADC DMA 目标数组无值更新

欢迎扫描二维码关注本人微信公众号, 及时获取最新文章:

STM32H743 ADC1+DMA1 ADC3+BDMA CubeMX配置使用相关推荐

  1. stm32H743基于CubeMX配置为双ADC多通道DMA规则采样

    stm32H743的ADC支持的最大频率为36MHz,有相关资料上说是可以超频,具体技术细节不多讨论. 需求: 使用stm32H743的ADC采集5路数据,当然还要配合DMA,现在把5路数据分在两个A ...

  2. STM32开发(14)----CubeMX配置ADC

    CubeMX配置ADC 前言 一.什么是ADC? 二.实验过程 1.单通道ADC采集 STM32CubeMX配置 代码实现 2.多通道ADC采样(非DMA) STM32CubeMX配置 代码实现 3. ...

  3. 【CubeMX配置STM32的ADC】

    CubeMX配置STM32的ADC 包含阻塞式和非阻塞式以及多通道ADC 一.CubeMX配置STM32 1.单通道ADC 单通道的配置比较简单,需要注意的是需要中断的使能,因为在使用非阻塞式的函数时 ...

  4. STM32 CubeMX配置ADC+DMA进行FFT(1)

    STM32 CubeMX配置ADC+DMA,计算采样率和ADC电压 原创:睿智嵌入式 最近,因为项目需要处理音频信号,对AD采集的音频信号进行FFT运算,记录一下学习过程. 主要内容: 1.ADC和D ...

  5. cubemx 配置多通道ADC进行ADC采样

    cubemx 配置多通道ADC进行ADC采样 AD的基础知识 AD很复杂,其实也不复杂,因为我们用的不多. AD:模拟量转换数字量(模拟信号转换数字信号). ADC:模拟量转数字量的转换器. 为什么需 ...

  6. STM32 HAL库 CUBEMX配置 ADC采集

    文章目录 STM32 HAL库 CUBEMX配置 ADC采集 软硬件型号 1.单通道不定时任意时刻采集信号 CUBEMX配置 代码添加 2.单通道ADC采集+DMA传输 CUBEMX添加配置 代码添加 ...

  7. 【SDRAM】STM32外扩SDRAM学习总结 + CubeMX配置教程

    目录 一.SDRAM概述 二.CubeMX配置 三.程序配置 一.SDRAM概述 (内容参考正点原子 + 个人经验总结) STM32H743自带1M左右的RAM,虽然已经很大了,但总有不够的时候.RA ...

  8. CubeMX配置串口的程序烧入板子不跑的解决方法

    对于cubeMX配置串口,keil5编译通过的,自己确定无问题的程序,以ISP烧入,烧入板子后无法运行的情况,我暂时的解决策略是按住reset键点击"开始编程",点击后松开rese ...

  9. c语言定时器定时1ms程序,STM32 Cubemx 配置定时器定时1mS

    最近才发现原来我把定时器里的配置参数代表的意义给搞混了,这里记录一下,防止以后自己忘记. 以建立一个定时1mS定时器为例: 1.先打开定时器 2.配置好时钟 3.配置定时器设置 重点来了,以前在这里我 ...

最新文章

  1. 转 Oracle 删除表,oracle 中删除表 drop delete truncate 的区别
  2. 【视频课】图像分割重磅上新-人像抠图(Matting)实战
  3. CNN几种经典模型比较
  4. [WEB API] CLIENT 指定请求及回应格式(XML/JSON)
  5. 业务的可变性和不可变性分析_不可变性真的意味着线程安全吗?
  6. 记录C++ Builder 6.0开发过程中的一个linker error
  7. html5 plus.push,Push - 《HTML5 Plus API 指南》 - 书栈网 · BookStack
  8. Android Studio 第七十六期 - Android webview长按识别二维码
  9. 纯js开发android,js开发app_安卓app开发引擎
  10. ESP32 超声波避障小车
  11. 怎么禁用计算机usb驱动,在Windows中启用或禁用USB驱动器或端口的5种方法 | MOS86...
  12. 描述羽毛针禾及其特殊结构——“根鞘”的词有哪些呢?已经做完的项目和目前羽毛针禾面临的科学问题有哪些?
  13. react 跳转外部链接
  14. 知识图谱论文读后感001
  15. 技术之路也要懂得理财-------林左鸣:建设军工强国的5条锦囊妙计
  16. html+css+js适合前端小白的实战全解(超详细)——2048小游戏(三)
  17. Centos7安装cuda10.1
  18. 从那堆代码中,你懂得了哪些道理?
  19. 关于-3db截止频率
  20. 北峰校园巡更管理+智能通信解决方案

热门文章

  1. 重庆春季高考计算机试题,2017年重庆春季高考数学模拟练习题一
  2. vue使用mintUI踩坑——不显示样式/导入mui.css报错/build不了
  3. html同时播放多个文件夹,ios – 如何同时播放多个音频文件
  4. Andersen Global拓展巴西业务版图;在巴西新增合作公司
  5. java计算机毕业设计基于ssm的志愿者招募网站(源代码+数据库+Lw文档)
  6. 不用管别人怎么评论自己_管好自己少管别人句子
  7. 【生活】空调工作原理及基本操作
  8. 计算机主机启动不了系统安装系统安装软件,最全面电脑主机启动不了怎么办呢?...
  9. Excel自动化办公(一) | 满足你对Excel数据的所有幻想,python-office一键生成模拟数据
  10. unity学习途中奇奇怪怪的问题——Unity打不开VS的问题