使用STM32H743VIT6芯片时,系统时钟为400MHz,从片上Flash读取数据时,有时会进入hardfault。对flash全部擦除后再次下载后,从片上Flash读取数据恢复正常。有没有大牛知道该如何解决这个问题?这是芯片本身的bug吗? 系统时钟配置如下: //时钟设置函数 //Fvco=Fs*(plln/pllm); //Fsys=Fvco/pllp=Fs*(plln/(pllm*pllp)); //Fq=Fvco/pllq=Fs*(plln/(pllm*pllq)); //Fvco:VCO频率 //Fsys:系统时钟频率,也是PLL1的p分频输出时钟频率 //Fq:PLL1的q分频输出时钟频率 //Fs:PLL输入时钟频率,可以是HSI,CSI,HSE等. //plln:PLL1倍频系数(PLL倍频),取值范围:4~512. //pllm:PLL1预分频系数(进PLL之前的分频),取值范围:2~63. //pllp:PLL1的p分频系数(PLL之后的分频),分频后作为系统时钟,取值范围:2~128.(且必须是2的倍数) //pllq:PLL1的q分频系数(PLL之后的分频),取值范围:1~128. //CPU频率(rcc_c_ck)=sys_d1cpre_ck=400Mhz //rcc_aclk=rcc_hclk3=200Mhz //AHB1/2/3/4(rcc_hclk1/2/3/4)=200Mhz //APB1/2/3/4(rcc_pclk1/2/3/4)=100Mhz //FMC时钟频率=pll2_r_ck=((25/25)*512/2)=256Mhz //外部晶振为25M的时候,推荐值:plln=160,pllm=5,pllp=2,pllq=2. //得到:Fvco=25*(160/5)=800Mhz // Fsys=800/2=400Mhz // Fq=800/2=400Mhz //外部晶振为16M的时候,推荐值:plln=250,pllm=5,pllp=2,pllq=2. //得到:Fvco=16*(250/5)=800Mhz // Fsys=800/2=400Mhz // Fq=800/2=400Mhz //返回值:0,成功;1,失败。 void Stm32_Clock_Init(u32 plln,u32 pllm,u32 pllp,u32 pllq) { HAL_StatusTypeDef ret=HAL_OK; RCC_ClkInitTypeDef RCC_ClkInitStruct; RCC_OscInitTypeDef RCC_OscInitStruct; RCC_PeriphCLKInitTypeDef periphClkInitStruct; MODIFY_REG(PWR->CR3,PWR_CR3_SCUEN, 0); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); while ((PWR->D3CR & (PWR_D3CR_VOSRDY)) != PWR_D3CR_VOSRDY) {} RCC_OscInitStruct.OscillatorType=RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState=RCC_HSE_ON; RCC_OscInitStruct.HSIState=RCC_HSI_OFF; RCC_OscInitStruct.CSIState=RCC_CSI_OFF; RCC_OscInitStruct.PLL.PLLState=RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource=RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLN=plln; RCC_OscInitStruct.PLL.PLLM=pllm; RCC_OscInitStruct.PLL.PLLP=pllp; RCC_OscInitStruct.PLL.PLLQ=pllq; RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE; RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2; ret=HAL_RCC_OscConfig(&RCC_OscInitStruct); if(ret!=HAL_OK) while(1); //选中 PLL 作为系统时钟源并且配置 HCLK,PCLK1、 PCLK2、 PCLK3 和 PCLK4 RCC_ClkInitStruct.ClockType=(RCC_CLOCKTYPE_SYSCLK|\ RCC_CLOCKTYPE_HCLK |\ RCC_CLOCKTYPE_D1PCLK1 |\ RCC_CLOCKTYPE_PCLK1 |\ RCC_CLOCKTYPE_PCLK2 |\ RCC_CLOCKTYPE_D3PCLK1); RCC_ClkInitStruct.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK;//系统时钟源 PLL RCC_ClkInitStruct.SYSCLKDivider=RCC_SYSCLK_DIV1;//SYSCLK 分频系数 1 RCC_ClkInitStruct.AHBCLKDivider=RCC_HCLK_DIV2;//AHB 分频系数 2 RCC_ClkInitStruct.APB1CLKDivider=RCC_APB1_DIV2;//APB1 分频系数 2 RCC_ClkInitStruct.APB2CLKDivider=RCC_APB2_DIV2;//APB2 分频系数 2 RCC_ClkInitStruct.APB3CLKDivider=RCC_APB3_DIV2;//APB3 分频系数 2 RCC_ClkInitStruct.APB4CLKDivider=RCC_APB4_DIV2;//APB4 分频系数 2 /*ST 官方例程使用的就是 4 个 WS, 其实大于 2 个 WS 都可以(最大不 能超过 7 个 WS),延时越久越稳定,但是肯定影响性能 */ FLASH->ACR &= ~FLASH_ACR_WRHIGHFREQ_Msk; FLASH->ACR |= FLASH_ACR_WRHIGHFREQ_2;//rcc_aclk 设置的是 200Mhz,设置 WRHIGHFREQ[1:0]=10 即可 ret=HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4); if(ret!=HAL_OK) while(1); //设置SPI1的时钟源 periphClkInitStruct.PeriphClockSelection=RCC_PERIPHCLK_SPI1 | RCC_PERIPHCLK_SPI3 | RCC_PERIPHCLK_SPI6; //设置SPI1 、 SPI3、SPI6时钟源 periphClkInitStruct.Spi123ClockSelection=RCC_SPI123CLKSOURCE_PLL; //SPI1时钟源使用PLL1Q periphClkInitStruct.Spi6ClockSelection =RCC_SPI6CLKSOURCE_D3PCLK1; //SPI6时钟源使用PCLK4 100MHz ret=HAL_RCCEx_PeriphCLKConfig(&periphClkInitStruct); if(ret!=HAL_OK) while(1); __HAL_RCC_CSI_ENABLE() ; __HAL_RCC_SYSCFG_CLK_ENABLE() ; HAL_EnableCompensationCell(); } flash 读写函数具体如下: #include "hal_flash.h" //读取指定地址的字(32 位数据) //addr:读地址 //返回值:对应数据 uint32_t HAL_FLASH_ReadWord(uint32_t addr) { uint32_t value = 0; value = *(__IO uint32_t*)addr;//此处进入hardfault,传入的地址为0x08060000 return value; } uint16_t HAL_FLASH_GetFlashSector(uint32_t addr) { if(addr<ADDR_FLASH_SECTOR_1_BANK1)return FLASH_SECTOR_0; else if(addr<ADDR_FLASH_SECTOR_2_BANK1)return FLASH_SECTOR_1; else if(addr<ADDR_FLASH_SECTOR_3_BANK1)return FLASH_SECTOR_2; else if(addr<ADDR_FLASH_SECTOR_4_BANK1)return FLASH_SECTOR_3; else if(addr<ADDR_FLASH_SECTOR_5_BANK1)return FLASH_SECTOR_4; else if(addr<ADDR_FLASH_SECTOR_6_BANK1)return FLASH_SECTOR_5; else if(addr<ADDR_FLASH_SECTOR_7_BANK1)return FLASH_SECTOR_6; else if(addr<ADDR_FLASH_SECTOR_0_BANK2)return FLASH_SECTOR_7; else if(addr<ADDR_FLASH_SECTOR_1_BANK2)return FLASH_SECTOR_0; else if(addr<ADDR_FLASH_SECTOR_2_BANK2)return FLASH_SECTOR_1; else if(addr<ADDR_FLASH_SECTOR_3_BANK2)return FLASH_SECTOR_2; else if(addr<ADDR_FLASH_SECTOR_4_BANK2)return FLASH_SECTOR_3; else if(addr<ADDR_FLASH_SECTOR_5_BANK2)return FLASH_SECTOR_4; else if(addr<ADDR_FLASH_SECTOR_6_BANK2)return FLASH_SECTOR_5; else if(addr<ADDR_FLASH_SECTOR_7_BANK2)return FLASH_SECTOR_6; return FLASH_SECTOR_7; } uint8_t HAL_FLASH_GetFlashBank(uint32_t addr) { if(addr >= ADDR_FLASH_BANK1_START && addr <= ADDR_FLASH_BANK1_END) return FLASH_BANK_1; else if(addr >= ADDR_FLASH_BANK2_START && addr <= ADDR_FLASH_BANK2_END) return FLASH_BANK_2; else return 0; } uint32_t HAL_FLASH_EraseSector(uint32_t addr)//擦除片区 { FLASH_EraseInitTypeDef FlashEraseInit; uint32_t SectorError=0; uint8_t FLASH_BANK = 0; FLASH_BANK = HAL_FLASH_GetFlashBank(addr); //操作 BANK FlashEraseInit.TypeErase=FLASH_TYPEERASE_SECTORS; //擦除类型 FlashEraseInit.Sector=HAL_FLASH_GetFlashSector(addr);//要擦除的扇区 FlashEraseInit.Banks= FLASH_BANK; FlashEraseInit.NbSectors=1; //一次只擦除一个扇区 FlashEraseInit.VoltageRange=FLASH_VOLTAGE_RANGE_3; //电压范围 if(HAL_FLASHEx_Erase(&FlashEraseInit,&SectorError)!=HAL_OK) { return 1; //发生错误了 } SCB_CleanInvalidateDCache(); //清除无效的 D-C return 0; } //从指定地址开始写入指定长度的数据 //特别注意:因为 STM32H7 的扇区实在太大,没办法本地保存扇区数据,所以本函数 // 写地址如果非 0XFF,那么会先擦除整个扇区且不保存扇区数据.所以 // 写非 0XFF 的地址,将导致整个扇区数据丢失.建议写之前确保扇区里 // 没有重要数据,最好是整个扇区先擦除了,然后慢慢往后写. //该函数对 OTP 区域也有效!可以用来写 OTP 区! //OTP 区域地址范围:0X1FF0F000~0X1FF0F41F //WriteAddr:起始地址(此地址必须为 4 的倍数!!) //pBuffer:数据指针 //NumToWrite:字(32 位)数(就是要写入的 32 位数据的个数.) void HAL_FLASH_Write(uint32_t WriteAddr,uint32_t *pBuffer,uint32_t NumToWrite) { FLASH_EraseInitTypeDef FlashEraseInit; HAL_StatusTypeDef FlashStatus=HAL_OK; uint32_t SectorError=0; uint32_t addrx=0; uint32_t endaddr=0; uint8_t FLASH_BANK = 0; if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4) return; //非法地址 HAL_FLASH_Unlock(); //解锁 addrx=WriteAddr; //写入的起始地址 endaddr=WriteAddr+NumToWrite*4; //写入的结束地址 if(addrx<0X1FF00000) { while(addrx<endaddr) //扫清一切障碍.(对非 FFFFFFFF 的地方,先擦除) { if(HAL_FLASH_ReadWord(addrx)!=0XFFFFFFFF) //要擦除这个扇区 { FLASH_BANK = HAL_FLASH_GetFlashBank(addrx); //操作 BANK FlashEraseInit.TypeErase=FLASH_TYPEERASE_SECTORS; //擦除类型 FlashEraseInit.Sector=HAL_FLASH_GetFlashSector(addrx);//要擦除的扇区 FlashEraseInit.Banks= FLASH_BANK; FlashEraseInit.NbSectors=1; //一次只擦除一个扇区 FlashEraseInit.VoltageRange=FLASH_VOLTAGE_RANGE_3; //电压范围 if(HAL_FLASHEx_Erase(&FlashEraseInit,&SectorError)!=HAL_OK) { break; //发生错误了 } SCB_CleanInvalidateDCache(); //清除无效的 D-Cache }else addrx+=4; FLASH_WaitForLastOperation(FLASH_WAITETIME,FLASH_BANK); } } FlashStatus=FLASH_WaitForLastOperation(FLASH_WAITETIME,FLASH_BANK); if(FlashStatus==HAL_OK) { while(WriteAddr<endaddr)//写数据 { if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD,WriteAddr, (uint64_t)pBuffer)!=HAL_OK)//写入数据 { break; //写入异常 } WriteAddr+=32; pBuffer+=8; } } HAL_FLASH_Lock(); //上锁 } //从指定地址开始读出指定长度的数据 //ReadAddr:起始地址 //pBuffer:数据指针 //NumToRead:字(32 位)数 void HAL_FLASH_Read(uint32_t ReadAddr,uint32_t *pBuffer,uint32_t NumToRead) { uint32_t i; for(i=0;i<NumToRead;i++) { pBuffer=HAL_FLASH_ReadWord(ReadAddr);//读取 4 个字节. ReadAddr+=4;//偏移 4 个字节. } } 主函数如下:int main(void) {__IO uint32_t data[API_FLASH_UPDATELEN] = {0};//API_FLASH_UPDATELEN为宏定义即32 Cache_Enable(); //打开L1-Cache HAL_Init(); //初始化HAL库 Stm32_Clock_Init(250,5,2,4); //设置时钟,400Mhz HAL_FLASH_Read(API_FLASH_UPDATE_ADDR, (uint32_t*)data, API_FLASH_UPDATELEN/4);//API_FLASH_UPDATE_ADDR宏定义为0x08060000 #if 1 while(1){ } #endif } 报错时,相关寄存器的值如下图所示: |
STM32H743采用flash swap 进行IAP升级,reset后启动不了
STM32双核H7核间通信的方法
STM32H743IIC,HAL库驱动硬件I2C,hi2c2.state一直是busy. 为什么?
H747 DSI 模块的官方例程跑不通
MAC回环或以太网PHY芯片回环怎么实现?
STM32745/747芯片固件升级问题
H743 DAC通道缓冲器的校准问题
STM32H750更改时钟后,程序无法运行
疑似STM32CUBEIDE出现BUG
单片机复位后引脚状态
volatile uint8_t Start_DATA[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
Start_DATA[0]=*(unsigned int *)Reload_value_IWDG_ADDR;
难道不是像上面这样的,直接一行代码去读取FLASH中某个字节吗?
我就是这么操作的呀。
至少F1系列与L4系列是这么干的。
我试试先