请选择 进入手机版 | 继续访问电脑版
搜索
查看: 80|回复: 5

[求助] STM32H743 flash read 进入hardfault

[复制链接]

该用户从未签到

2

主题

7

帖子

2

蝴蝶豆

初级会员

最后登录
2019-8-16
发表于 5 天前 | 显示全部楼层 |阅读模式
使用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

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

图片1.png


bus fault1.png













回复

使用道具 举报

该用户从未签到

6

主题

565

帖子

0

蝴蝶豆

金牌会员

最后登录
2019-8-20
发表于 5 天前 | 显示全部楼层
抢个沙发吧
回复 支持 反对

使用道具 举报

该用户从未签到

35

主题

401

帖子

125

蝴蝶豆

金牌会员

最后登录
2019-8-19
发表于 5 天前 | 显示全部楼层
不是,一般是你指针操作有问题,
回复 支持 反对

使用道具 举报

该用户从未签到

39

主题

416

帖子

129

蝴蝶豆

论坛元老

最后登录
2019-8-20
发表于 5 天前 | 显示全部楼层
#define        Reload_value_IWDG_ADDR 0x0800C000
volatile uint8_t Start_DATA[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};               

Start_DATA[0]=*(unsigned int *)Reload_value_IWDG_ADDR;

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

回复 支持 反对

使用道具 举报

该用户从未签到

2

主题

7

帖子

2

蝴蝶豆

初级会员

最后登录
2019-8-16
 楼主| 发表于 5 天前 | 显示全部楼层
奏奏奏 发表于 2019-8-15 17:18
#define        Reload_value_IWDG_ADDR 0x0800C000
volatile uint8_t Start_DATA[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0 ...

我试试先
回复 支持 反对

使用道具 举报

该用户从未签到

2

主题

7

帖子

2

蝴蝶豆

初级会员

最后登录
2019-8-16
 楼主| 发表于 4 天前 | 显示全部楼层
不是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访问正常
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

小黑屋|手机版|Archiver|论坛-意法半导体STM32/STM8技术社区

GMT+8, 2019-8-20 16:38 , Processed in 0.153981 second(s), 19 queries , MemCache On.

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表