STM32H743 ADC1+DMA1 ADC3+BDMA CubeMX配置使用

前言

前段时间被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 conversions
                using function HAL_ADC_Start_DMA()
          (+++) Wait for ADC conversion completion by call of function
                HAL_ADC_ConvCpltCallback() or HAL_ADC_ConvHalfCpltCallback()
                (these functions must be implemented in user program)
          (+++) Conversion results are automatically transferred by DMA into
                destination variable address.
          (+++) Stop conversion and disable the ADC peripheral
                using 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 accessible
      by 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里面

在这里插入图片描述

参考

如下

欢迎扫描二维码关注本人微信公众号, 及时获取最新文章:
在这里插入图片描述

  • 4
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
STM32H743的RCC(Reset and Clock Control)模块是用于控制系统的复位和时钟的模块。RCC模块可以配置和控制系统时钟源、时钟分频器以及各个外设的时钟使能。具体的RCC配置使用方法可以参考STM32H743编程手册的相关章节。\[1\] 在RCC模块中,有一个RCC_D3AMR寄存器,该寄存器用于配置RCC的自动模式(Autonomous mode)。在Stop模式下,只有部分“L”字号的外设可以通过RCC的自动模式和BDMA来保持超低功耗和数据采集。而且,只有STM32L系列和STM32H系列的芯片才支持这种低功耗设置。\[3\] 如果你需要更具体的关于STM32H743的RCC模块的信息,可以参考STM32H743编程手册中的相关章节。\[1\] #### 引用[.reference_title] - *1* *3* [STM32H743 RCC时钟树的形象化理解](https://blog.csdn.net/NoDistanceY/article/details/103835987)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [STM32H743串口同时收发问题总结](https://blog.csdn.net/m0_58644391/article/details/121777248)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值