ST MCU Finder
安装免费手机应用,
寻找理想的ST MCU

[STM32L4] 写片内Flash有时会失败

[复制链接]
127|3
 楼主 | 2019-11-29 15:10 | 显示全部楼层 |阅读模式
因为片内FLASH有使用寿命,我们写了个函数:
1、如果目标地址已经写过,先擦除再写
2、如果目标地址没有写过,直接写到目标地址
RetType_t Flash_AdvantageWrite(uint32_t address, uint16_t * buf, uint32_t bytenum)
{  
        uint8_t needErase = 0;
        HAL_StatusTypeDef status = HAL_OK;
        uint16_t *addp = NULL;
        uint32_t tadd = (address-address%FLASHPAGESIZE);
        int i;
        uint32_t FirstPage = 0, NbOfPages = 0, BankNumber = 0;
        uint32_t PAGEError = 0;
        FLASH_EraseInitTypeDef EraseInitStruct;
        uint64_t data_64_A = 0,data_64_B = 0,data_64_C = 0,data_64_D = 0;
        uint32_t startCheckAddr, endCheckAddr;
        uint16_t byteNum;

        Voice_WaitEnd();
        FeedDog();

        _say_("FlashWrite:address=0x%x,bytenum=%d\r\n", address, bytenum);

        if ((address%FLASHPAGESIZE+bytenum) > FLASHPAGESIZE)
        {
                _say_("Exceed limit:address=0x%x,bytenum=0x%x\r\n", address, bytenum);
                return RET_EXCEED_LIMIT;
        }

        memset(g_flagpagebuf, 0xff, FLASHPAGESIZE);
       
        //判断需要写的区域是否已经写过,并更新标志位
        startCheckAddr = address - address % 8;
        endCheckAddr = address + bytenum;
        if (endCheckAddr % 8 != 0)
        {
                endCheckAddr += 8 - (endCheckAddr % 8);
        }
        for (i = startCheckAddr; i < endCheckAddr; i += 2)
        {
                addp = (uint16_t *)i;
                if (*addp != FLASH_DEFAULT_HALFWORD)
                {
                        needErase = 1;
                        _say_("Writed,need to erase!\r\n");
                        break;
                }
        }

        // 如果需要写的区域已经被写过,需要把数据全部读出,更新后重新写入
        if (needErase == 1)
        {
                addp = (uint16_t *)(address-address%FLASHPAGESIZE);// 当前页首地址

                _memcpy(g_flagpagebuf,addp,FLASHPAGESIZE);//读出数据到g_flagpagebuf

                HAL_FLASH_Unlock();
                __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);

                /* Get the 1st page to erase */
                FirstPage = GetPage(address);
                /* Get the number of pages to erase from 1st page */
                NbOfPages = 1;
                /* Get the bank */
                BankNumber = GetBank(address);
                /* Fill EraseInit structure*/
                EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES;
                EraseInitStruct.Banks       = BankNumber;
                EraseInitStruct.Page        = FirstPage;
                EraseInitStruct.NbPages     = NbOfPages;

                status = HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError);
                if (status != HAL_OK)
                {
                        HAL_FLASH_Lock();
                        _say_("Fail to Erase!\r\n");
                        return RET_ERASE_FLASH_ERROR;
                }

                memcpy(g_flagpagebuf+(address%FLASHPAGESIZE)/2,buf,bytenum);// 更新buf数据

                // 将数据写回
                for (i = 0; i < FLASHPAGESIZE/8; tadd += 8, i++)
                {
                        data_64_A = g_flagpagebuf[i*4+0];
                        data_64_B = g_flagpagebuf[i*4+1];
                        data_64_C = g_flagpagebuf[i*4+2];
                        data_64_D = g_flagpagebuf[i*4+3];
                        status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, tadd, data_64_A+(data_64_B<<16)+(data_64_C<<32)+(data_64_D<<48));
                        if (status != HAL_OK)
                        {
                                _say_("FLASH program fail(page),status=%d,i=%d\r\n", status, i);
                                break;
                        }                               
                }               
        }
        else // 如果需要写的区域没有被写过,直接将数据写入
        {
                /*必须保证address是2字节的倍数*/
                HAL_FLASH_Unlock();
                __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
                //SET_BIT(FLASH->CR, 1 << 27);
                memcpy(g_flagpagebuf+(address%8)/2,buf,bytenum);
                tadd = address - address % 8;
                byteNum = bytenum + address % 8;
                if (byteNum % 8 != 0)
                {
                        byteNum += 8 - (byteNum % 8);
                }
                _say_("tadd=0x%x, byteNum=%d\r\n", tadd, byteNum);
                for (i = 0; i < byteNum/8; tadd += 8, i++)
                {
                        data_64_A = g_flagpagebuf[i*4+0];
                        data_64_B = g_flagpagebuf[i*4+1];
                        data_64_C = g_flagpagebuf[i*4+2];
                        data_64_D = g_flagpagebuf[i*4+3];
                        status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, tadd, data_64_A+(data_64_B<<16)+(data_64_C<<32)+(data_64_D<<48));                        
                        if (status != HAL_OK)
                        {
                                uint32_t data1, data2;
                                _say_("Program %d Dword fail,status=%d\r\n", i, status);
                                data1 = *(uint32_t*)tadd;
                                data2 = *(uint32_t*)(tadd+4);
                                _say_("data1=0x%X,data2=0x%X\r\n", data1, data2);
                                break;
                        }
                }
        }

        HAL_FLASH_Lock();
        FeedDog();

        return (status != HAL_OK) ? RET_WRITE_FLASH_ERROR : RET_OK;
}
先对整个页进行擦除,然后进行读写操作,测试中发现:
1、对于已经写过数据的先擦除再写没问题
2、对没有写过的写数据时有时会出错。
出错的地方在FLASH_WaitForLastOperation中,
error = (FLASH->SR & FLASH_FLAG_SR_ERRORS);
状态寄存器被置上了FLASH_SR_PEMPTY
请问各位大大什么情况下状态寄存器会被置上这个值?
有没有解决方案或规避方案?
谢谢

使用特权

评论回复
| 2019-11-29 21:24 | 显示全部楼层
不清楚,不知道这给是什么原因,测试了两块都这样吗

使用特权

评论回复
| 2019-11-29 21:24 | 显示全部楼层
先排除不是硬件的单一问题。然后确认是软件没有搞对的问题。

使用特权

评论回复
 楼主 | 2019-12-2 16:01 | 显示全部楼层
这个问题,我的测试结果是这样的:
uint16_t Flash_Test_buf[1024] = {0};
memcpy(Flash_Test_buf, "kitty123456789hellotes",22);
Flash_ErasePage(0x0803B000, 1);
Flash_AdvantageWrite(0x0803B000,Flash_Test_buf,strlen((char *)Flash_Test_buf));
Flash_AdvantageWrite(0x0803B100,Flash_Test_buf,strlen((char *)Flash_Test_buf));
这样是可以的,但如果在0x0803B000和0x0803B100之间加入对0x0803B008的连续22个字节的写操作
Flash_AdvantageWrite(0x0803B000,Flash_Test_buf,strlen((char *)Flash_Test_buf));
Flash_AdvantageWrite(0x0803B008,Flash_Test_buf,strlen((char *)Flash_Test_buf));
Flash_AdvantageWrite(0x0803B100,Flash_Test_buf,strlen((char *)Flash_Test_buf));
因为对0x0803B008处已经被写过了,所以需要把2KB的整页数据读出来,修改后写回,这时再对0x0803B100进行操作,即使0x0803B100处连续22个字节的Flash的值全为1,也会报问题中描述的现象。
从现象上来看,如果Flash执行过写操作(写为1),再次去写,即使写1也会出错。这个和我理解中的"Flash只能从1变到0"不一致,我一直以为,只要Flash的值为1就可以执行写操作

使用特权

评论回复
扫描二维码,随时随地手机跟帖
您需要登录后才可以回帖 登录 | 注册

本版积分规则

我要发帖 投诉建议 创建版块 申请版主

快速回复

您需要登录后才可以回帖
登录 | 注册
高级模式

论坛热帖

在线客服 快速回复 返回顶部 返回列表