打印
[STM32H7]

关于STM32H750使用BDMA的方法

[复制链接]
78|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 xyzjacky 于 2025-1-17 11:12 编辑

第一部分 使用片内2M的flash
STM32H750VBTx的flash官方规定只能使用128K的flash,但是其实是可以绕过限制,使用其片内2M的flash空间。

这里介绍一种较为简单的实现的办法,这个办法不同网络上介绍的办法,可以在keil上较轻松地实现。因为它可以使用较高STM32CubeMX(6.12.0)和keil(5.29)的版本。

首先按照正常的操作顺序,使用STM32CubeMX生成一个keil工程。然后只需要对keil工程做以下更改就可以使用片内2M的flash空间。

(1)先把IROM1的Size改为0x200000





若是有bootlaoder的,请自行计算你要用flash的起始地址,以及Size。

另外要把片内flash作为数据存储区(参数存储区)使用,也请自己计算Size的大小。

(2)Flash Downloader编程算法的Size的大小,这里要改为0x200000。其它的不要管。



(3)使用关键词搜索整个工程,找到FLASH_END这个宏定义。这个宏在stm32h750xx.h文件中。

将这个FLASH_END改为0x081FFFFFUL,其它的不要管。






完成以上变更,就完成了绕过限制,可以使用片内2M的flash,此时编译就能通过了。

  第二部分 使用BDMA
这里介绍,在不打开MPU的方式下,使用BDMA的办法。

此处,以ADC3采集MCU片内温度为例。

一、先看STM32CubeMX的配置。

(1)首先要设置DCache为使能状态。

注意这里是没有打开MPU的。




(2)配置ADC3




这里关于ADC3的配置,可以参看这位博主的帖子(链接1),这篇帖子写得很好,望仔细阅读:

https://blog.csdn.net/weifengdq/article/details/121802176

(3)然后是ADC3要使用到的DMA。

在这里就可以将其配置为BDMA了,channel可以根据你的实际情况任选。



(4)BDMA的中断优先级的配置。

这里请根据你的工程的实际情况进行配置。



到第(4)点就算是完成了在STM32CubeMX中的配置,接着我们来改动keil中的配置。




二、在keil中的相关配置。

如果不在keil更改相关配置,那么要想得到bin文件,你是得不到的。你只会得到这样一个文件夹,以及其中的文件。





粗看这2个文件,你大概就能知道编译不成功,这两个文件是有错的。

那么正确的做法是什么么?

请按照下面的步骤来就行了。

(1)先来完成,ADC3启动DMA采集,以及从BDMA中取数据的操作。

这段代码如下:

#define ADC3_CHNL_NUM 1 // ADC3用了8通道,
#define ADC3_BUFFER_SIZE 32 // 存32组, 方便做平均
#define ADC3_TOTAL_SIZE (ADC3_BUFFER_SIZE*ADC3_CHNL_NUM)

ALIGN_32BYTES (uint16_t adc3_drv_buf[ADC3_TOTAL_SIZE]) __attribute__((section(".ARM.__at_0x38000000")));

void adc3_init(void)
{
uint8_t err_sta =0;

while(HAL_ADCEx_Calibration_Start(&hadc3, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK)
{
err_sta++;
}

while(HAL_ADC_Start_DMA(&hadc3, (uint32_t *)adc3_drv_buf, ADC3_TOTAL_SIZE) != HAL_OK)
{
err_sta++;
}

if(err_sta >=2)
{
err_sta =0;
}
}

static float mcu_temp = 0.0f;
uint8_t adc3_done_flag;

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 == ADC3)
{
SCB_InvalidateDCache_by_Addr((uint32_t *) &adc3_drv_buf[0], ADC3_TOTAL_SIZE);
adc3_done_flag++;
}
}

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 == ADC3)
{
SCB_InvalidateDCache_by_Addr((uint32_t *) &adc3_drv_buf[ADC3_TOTAL_SIZE/2], ADC3_TOTAL_SIZE);
adc3_done_flag++;
}
}

void get_mcu_temp(void)
{
uint32_t TS_DATA = 0;
uint16_t TS_CAL1 = *(__IO uint16_t *)(0x1FF1E820);
uint16_t TS_CAL2 = *(__IO uint16_t *)(0x1FF1E840);

if(adc3_done_flag)
{
adc3_done_flag =0;

for(uint8_t i =0; i< ADC3_BUFFER_SIZE; i++)
{
TS_DATA += adc3_drv_buf;
}

TS_DATA = TS_DATA >>5;

mcu_temp = (110.0f -30.0f)/(TS_CAL2 - TS_CAL1) * (TS_DATA - TS_CAL1) + 30.0f;
}
}

此段代码里,请注意函数HAL_ADC_ConvHalfCpltCallback(),和函数HAL_ADC_ConvCpltCallback()中的描述,即以下注释文字:

/* Invalidate Data Cache to get the updated content of the SRAM on the first half of the ADC converted data buffer */

/* Invalidate Data Cache to get the updated content of the SRAM on the second half of the ADC converted data buffer */

一个函数是取了the SRAM on the first half中buf数组的前半部分,另一个函数是取了the SRAM on the second half中buf数组的后半部分。这是它ADC硬件决定的,所以必须要这么用。





关于main函数的的代码:

static uint64_t timestamp =0;
static uint64_t last_timestamp =0;
static uint64_t loop =0;
/* USER CODE END 0 */

/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{

/* USER CODE BEGIN 1 */
static uint64_t tick =0;
/* USER CODE END 1 */

/* Enable the CPU Cache */

/* Enable I-Cache---------------------------------------------------------*/
SCB_EnableICache();

/* Enable D-Cache---------------------------------------------------------*/
SCB_EnableDCache();

/* MCU Configuration--------------------------------------------------------*/

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();

/* USER CODE BEGIN Init */

/* USER CODE END Init */

/* Configure the system clock */
SystemClock_Config();

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_BDMA_Init();
MX_ADC3_Init();
MX_TIM6_Init();
/* USER CODE BEGIN 2 */
adc3_init();
HAL_TIM_Base_Start_IT(&htim6);
/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
tick++;
timestamp = hrt_absolute_time();
get_mcu_temp();
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
if(timestamp - last_timestamp > 5*1e3 )
{
last_timestamp = timestamp;
if(mcu_temp > 22.5f)
{
HAL_GPIO_TogglePin(MCU_LED_GPIO_Port, MCU_LED_Pin);
}
}

if(timestamp - loop > 1e3)
{
loop = timestamp;
HAL_GPIO_TogglePin(MCU_LED_GPIO_Port, MCU_LED_Pin);
}
}
/* USER CODE END 3 */
}



这其中有一句关键代码,它指定了从BDMA中取ADC3的数据的数组,是在内存中的哪个位置。




为何有这样的指定呢,简单的来说,是由于:

① STM32H750VBTx比较特殊,它的内存分布有一些要求与限制。

相关介绍,可以看这个贴子(链接2):

https://shequ.stmicroelectronics.cn/thread-632551-1-1.html

② BDMA只能访问SRAM4(0x38000000开始)

关于这个问题,请看上面链接1中的介绍,那位博主写得已经比价详细了。

也就是说用于读取BDMA的缓存数组buf[],只能定义到SRAM4中去。

在没有使用MPU的时候,keil是不知道有这么一个SRAM4的。所以就有了后面的一些配置。



(2)使用自定义的 .sct 文件。

按照下图,先取消keil自定义的memory的配置。然后选择自定义的 .sct文件。




注意:这个.sct文件,又是由keil自定义memory的配置时,编译出来(也就是说要先编译一次,再按照上图更改Linker中的设置)。它的位置在这里:



       使用写字板,或者Notepad++打开这个文件,并在其中添加SRAM4的相关定义。

         RW_IRAM4 0x38000000 0x00010000  {      ;SRAM4--64K

   .ANY (+RW +ZI)

  }




(3)再次编译,使keil 生成bin文件。




如果重新编译后,可以生成一个正常的bin文件,而不是文件夹,则说明以上的配置和更改都已经成功。




   从debug的结果可以看出,系统已经采集到MCU的片内温度。且tick在不断增加,说明程序在持续地稳定运行。

最后提交一下本实例代码的git仓的地址:
https://gitee.com/xyzjacky/h750_2-m_mcu_temp.git

使用特权

评论回复
沙发
xyzjacky|  楼主 | 2025-1-17 11:14 | 只看该作者
这个帖子是我从自己在另一个博客搬运过来的,有可能复制粘贴有问题,所以看到不完整的了,可以去博客园看原帖,帖子的标题是一样的。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

25

主题

114

帖子

4

粉丝