本帖最后由 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
|