在线时间8 小时
UID3126533
ST金币36
蝴蝶豆2
注册时间2015-6-16
该用户从未签到
初级会员
- 最后登录
- 2019-8-16
|
使用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
}
报错时,相关寄存器的值如下图所示:
|
|