澜月亮 发表于 2019-8-15 11:06:12

STM32H743 flash read 进入hardfault

使用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=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 = {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

}
报错时,相关寄存器的值如下图所示:
















s7631001 发表于 2019-8-15 12:21:19

抢个沙发吧:)

tanic 发表于 2019-8-15 14:03:10

不是,一般是你指针操作有问题,

奏奏奏 发表于 2019-8-15 17:18:36

#define        Reload_value_IWDG_ADDR 0x0800C000
volatile uint8_t Start_DATA={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};               

Start_DATA=*(unsigned int *)Reload_value_IWDG_ADDR;

难道不是像上面这样的,直接一行代码去读取FLASH中某个字节吗?
我就是这么操作的呀。
至少F1系列与L4系列是这么干的。

澜月亮 发表于 2019-8-15 17:29:01

奏奏奏 发表于 2019-8-15 17:18
#define        Reload_value_IWDG_ADDR 0x0800C000
volatile uint8_t Start_DATA={0xFF,0xFF,0xFF,0xFF,0xFF,0 ...

我试试先

澜月亮 发表于 2019-8-16 09:51:00

不是Flash 读取有问题,应该是HAL_FLASH_Write(uint32_t WriteAddr,uint32_t *pBuffer,uint32_t NumToWrite)函数写入数据异常导致下次读取数据有问题。现已将HAL_FLASH_Write(uint32_t WriteAddr,uint32_t *pBuffer,uint32_t NumToWrite)函数进行了调整。目前Flash访问正常

lyh04048 发表于 2020-3-17 08:55:00

楼主,我也遇到你这样的问题了,请问你怎么解决的?麻烦指教一下。

lyh04048 发表于 2020-3-17 09:07:52

楼主,你是怎么解决这问题的?
页: [1]
查看完整版本: STM32H743 flash read 进入hardfault