//flash 1.0V /********************************************************************* * INCLUDES */ #include "bsp_flash.h" static void fstorageCallbackFunc(nrf_fstorage_evt_t *pFstorageEvent); static uint32_t getflashEndAddress(void); static void waitForFlashReady(nrf_fstorage_t const *pFstorageHandle); /********************************************************************* * LOCAL VARIABLES */ static uint32_t flash_PageBuff[PAGE_INT_SIZE]; //页缓冲区 NRF_FSTORAGE_DEF(nrf_fstorage_t s_fstorageHandle) = { /* Set a handler for fstorage events. */ .evt_handler = fstorageCallbackFunc, /* These below are the boundaries of the flash space assigned to this instance of fstorage. * You must set these manually, even at runtime, before nrf_fstorage_init() is called. * The function nrf5_flash_end_addr_get() can be used to retrieve the last address on the * last page of flash available to write data. */ .start_addr = START_FSTORAGE_ADDR, .end_addr = END_FSTORAGE_ADDR, }; /********************************************************************* * LOCAL FUNCTIONS */ /** @brief Fstorage读写内存操作 @param flashAddr -[in] 闪存地址 @param readWriteFlag -[in] 读写操作标志 @param pData -[in&out] 指向需要操作的数据 @param dataLen -[in] 数据长度 @return 错误代码 */ static uint32_t Fstorage_FlashRW(uint32_t flashAddr, uint8_t readWriteFlag, uint32_t *pData, uint32_t dataLen) { ret_code_t errCode; if(flashAddr%4 != 0 || flashAddr == 0)return FLASH_ERROR_ADDRALIGN; if((flashAddr + dataLen) > END_FSTORAGE_ADDR){ return FLASH_ERROR_ADDROVERBOUNDS; //是否越界 } if(readWriteFlag == FSTORAGE_READ) // 读取数据 { errCode = nrf_fstorage_read(&s_fstorageHandle, flashAddr, pData, dataLen); APP_ERROR_CHECK(errCode); } else // 写入数据 { if(dataLen %4 != 0 || dataLen == 0)return FLASH_ERROR_DATAALIGN; if( FLASH_ZONEADDR_CHECK(flashAddr) )return FLASH_ERROR_ZONEADDR; errCode = nrf_fstorage_erase(&s_fstorageHandle, flashAddr, 1, NULL); // 只能写入位值1,不能写入位值0,所以先擦后写 errCode = nrf_fstorage_write(&s_fstorageHandle, flashAddr, pData, dataLen, NULL); APP_ERROR_CHECK(errCode); waitForFlashReady(&s_fstorageHandle); // 等待写完 } return FLASH_OP_SUCCESS; } /** @brief Fstorage写内存操作,调用前FLASH预先已擦除 @param flashAddr -[in] 闪存地址 @param pData -[in&out] 指向需要操作的数据 @param dataLen -[in] 数据长度 @param is_check - [in] 是否检测数据存不存在 @return 0:写入成功 1:4字节不对齐 2:已有数据存在 3:访问越界 */ static uint32_t Fstorage_FlashOnlyWrite(uint32_t flashAddr, uint32_t *pData, uint32_t dataLen, bool is_check) { ret_code_t errCode; uint32_t check,i,len; if(flashAddr%4 != 0 || flashAddr == 0)return FLASH_ERROR_ADDRALIGN; if(dataLen %4 != 0 || dataLen == 0)return FLASH_ERROR_DATAALIGN; if((flashAddr + dataLen) > END_FSTORAGE_ADDR){ return FLASH_ERROR_ADDROVERBOUNDS; } if(is_check) { len = dataLen / 4; for(i=0;iresult != NRF_SUCCESS) { SEGGER_RTT_printf(0,"--> Event received: ERROR while executing an fstorage operation.\n"); return ; } switch(pFstorageEvent->id) { case NRF_FSTORAGE_EVT_WRITE_RESULT: //SEGGER_RTT_printf(0,"--> Event received: wrote %d bytes at address 0x%x.\n", pFstorageEvent->len, pFstorageEvent->addr); break; case NRF_FSTORAGE_EVT_ERASE_RESULT: //SEGGER_RTT_printf(0,"--> Event received: erased %d page from address 0x%x.\n", pFstorageEvent->len, pFstorageEvent->addr); break; default: break; } } /** @brief 检索Flash上可用于写入数据的地址 @param 无 @return 无 */ static uint32_t getflashEndAddress(void) { uint32_t const bootloaderAddr = NRF_UICR->NRFFW[0]; uint32_t const pageSz = NRF_FICR->CODEPAGESIZE; uint32_t const codeSz = NRF_FICR->CODESIZE; return (bootloaderAddr != 0xFFFFFFFF ? bootloaderAddr : (codeSz * pageSz)); } /** @brief 等待写入完成 @param pFstorageHandle -[in] Fstorage句柄 @return 无 */ static void waitForFlashReady(nrf_fstorage_t const *pFstorageHandle) { while(nrf_fstorage_is_busy(pFstorageHandle)) // While fstorage is busy, sleep and wait for an event. { nrf_pwr_mgmt_run(); } } /** @brief 从flash中连续读取N个4字节数据 @param addr-[in] 读取的首地址 @param pData -[in&out] 指向需要操作的数据 @param dataLen -[in] 数据长度 @return 错误代码 */ static uint32_t Read_N_4Byte_flash(uint32_t addr , uint32_t *pData, uint32_t dataLen) { return Fstorage_FlashRW(addr, FSTORAGE_READ, pData, dataLen); } /** @brief 获取页号所在的首地址 @param PageNum-[in] 需要查找的页号 @param PageAddr-[in/out] 返回的页号首地址 @return 错误代码 */ static uint32_t GetPageAddr(uint16_t PageNum, uint32_t *PageAddr) { if(PageNum >= FLASH_PAGE_NUM)return FLASH_ERROR_PAGENUM; *PageAddr = FLASH_ZONE_ADDR_1 + PageNum * FLASH_PAGE_SIZE; return FLASH_OP_SUCCESS; } /** @brief 获取页号所在的尾地址(该页号下一页的首地址) @param PageNum-[in] 需要查找的页号 @param PageAddr-[in/out] 返回的页号首地址 @return 错误代码 */ static uint32_t GetPageEndAddr(uint16_t PageNum, uint32_t *PageAddr) { if(PageNum >= FLASH_PAGE_NUM)return FLASH_ERROR_PAGENUM; *PageAddr = FLASH_ZONE_ADDR_1 + (PageNum+1) * FLASH_PAGE_SIZE; return FLASH_OP_SUCCESS; } /** @brief 获取地址距离所在页的偏移量 @param addr-[in] 需要查找的flash地址 @param _offset-[in/out] 返回的偏移量 @return 错误代码 */ static uint32_t GetDistanceFromPage(uint32_t addr, uint32_t *_offset) { if(addr %4 != 0 || addr == 0)return FLASH_ERROR_ADDRALIGN; if(addr > END_FSTORAGE_ADDR || addr < START_FSTORAGE_ADDR){ return FLASH_ERROR_ADDROVERBOUNDS; //是否越界 } //获取偏移量 *_offset = ((addr - START_FSTORAGE_ADDR)%FLASH_PAGE_SIZE)/4; return FLASH_OP_SUCCESS; } /** @brief 判断是否需要擦除 @param start_addr-[in] flash首地址 @param len -[in] 长度 @param is_erase-[in/out] 1:需要擦除,0:不需要擦除 @return 错误代码 */ static uint32_t __NeedErase(uint32_t start_addr , uint32_t len , uint8_t *is_erase) { uint32_t err_code; uint32_t i = 0, data; if((start_addr + len) > END_FSTORAGE_ADDR)return FLASH_ERROR_ADDROVERBOUNDS; if(start_addr%4 != 0 || start_addr == 0)return FLASH_ERROR_ADDRALIGN; if(len %4 != 0 || len == 0)return FLASH_ERROR_DATAALIGN; len /=4; for(;i END_FSTORAGE_ADDR || addr < START_FSTORAGE_ADDR){ return FLASH_ERROR_ADDROVERBOUNDS; //是否越界 } memcpy(pData, (uint32_t*)addr, dataLen); return FLASH_OP_SUCCESS; } /** @brief 获取flash地址所在的页号 @param addr-[in] 需要查找的flash地址 @param page_num-[in/out] 返回的页号 @return 错误代码 */ uint32_t GetPageNum(uint32_t addr, uint16_t *PageNum) { uint16_t page_num = 0; if(addr > END_FSTORAGE_ADDR || addr < START_FSTORAGE_ADDR){ return FLASH_ERROR_ADDROVERBOUNDS; //是否越界 } // if(addr %4 != 0 || addr == 0)return FLASH_ERROR_ADDRALIGN; page_num = (addr - START_FSTORAGE_ADDR)/FLASH_PAGE_SIZE; if(page_num >= FLASH_PAGE_NUM)return FLASH_ERROR_PAGENUM; *PageNum = page_num; return FLASH_OP_SUCCESS; } /** @brief 擦除1页 @param PageNum-[in] 页号 @return 错误代码 */ uint32_t Erase_OnePage(uint16_t PageNum) { uint32_t PageAddr; uint32_t err_code; err_code = GetPageAddr(PageNum, &PageAddr); if(err_code != FLASH_OP_SUCCESS)return err_code; return Fstorage_FlashOnlyErasePage(PageAddr); } /** @brief 写N个字到flash中 @param addr-[in] 写入的flash首地址 @param pdata-[in] 需要写入的数据 @param len-[in] 数据长度 @return 错误代码 */ uint32_t Write_N_4Byte_flash(uint32_t addr , uint32_t *pdata, uint32_t len) { uint8_t is_erase; uint16_t start_page = 0; //起始页号 uint32_t page_size = 0; //需要操作页的大小 uint32_t _offset = 0; //页操作页码的起始地址 uint32_t page_offset = 0; //页偏移 uint32_t data_offset = 0; //数据偏移 uint32_t remain_size = 0; //本次操作页可以使用的剩余大小 uint32_t base_addr = 0; //本次操作页的基地址 uint32_t surplus = 0; uint32_t err_code; //是否越界 if((addr + len) > END_FSTORAGE_ADDR){ return FLASH_ERROR_ADDROVERBOUNDS; } //获取起始页号 err_code = GetPageNum(addr, &start_page); if(err_code != FLASH_OP_SUCCESS)return err_code; //获取偏移量 err_code = GetDistanceFromPage(addr,&_offset); if(err_code != FLASH_OP_SUCCESS)return err_code; //计算需要写入的页数 if(((PAGE_INT_SIZE - _offset)*4) >= len) { page_size = 1; surplus = 0; } else { page_size = 1; surplus = len - ((PAGE_INT_SIZE - _offset)*4); page_size += ((surplus/FLASH_PAGE_SIZE) + (surplus%FLASH_PAGE_SIZE>0?1:0)); } //写入 surplus = len; for(page_offset = 0; page_offset < page_size; page_offset++) { //获取页首地址 err_code = GetPageAddr(start_page + page_offset, &base_addr); if(err_code != FLASH_OP_SUCCESS)return err_code; if(page_offset == 0) { remain_size = (PAGE_INT_SIZE - _offset)*4 FLASH_PAGE_SIZE ? FLASH_PAGE_SIZE : surplus; surplus = surplus - remain_size; } //判断是否要擦除 err_code = __NeedErase(base_addr + _offset*4 , remain_size, &is_erase); if(err_code != FLASH_OP_SUCCESS)return err_code; //先擦除后写 if(is_erase) { //读取 err_code = Read_N_4Byte_flash(base_addr , flash_PageBuff, FLASH_PAGE_SIZE); if(err_code != FLASH_OP_SUCCESS)return err_code; //修改 memcpy(&flash_PageBuff[_offset], pdata + data_offset, remain_size); //写回 err_code = Fstorage_FlashRW(base_addr, FSTORAGE_WRITE, flash_PageBuff, FLASH_PAGE_SIZE); if(err_code != FLASH_OP_SUCCESS)return err_code; } else { err_code = Fstorage_FlashOnlyWrite(base_addr + _offset*4, pdata + data_offset, remain_size, false); //直接写 if(err_code != FLASH_OP_SUCCESS)return err_code; } data_offset += remain_size/4; } return FLASH_OP_SUCCESS; } /** @brief 只写N个字到flash中,调用前FLASH预先已擦除 @param addr-[in] 写入的flash首地址 @param pdata-[in] 需要写入的数据 @param len-[in] 数据长度 @return 错误代码 */ uint32_t Only_Write_N_4Byte_flash(uint32_t addr , uint32_t *pdata, uint32_t len, bool is_check) { return Fstorage_FlashOnlyWrite(addr, pdata, len, is_check); } /** @brief 查找一页中可写的空间(0xFF) @param PageNum-[in] 输入的页号 @param start_addr-[in/out] 返回的空闲空间的起始地址 @param len-[in/out] 返回的空间的长度 @return 错误代码 */ uint32_t Find_FreeSpace_In_Page(uint16_t PageNum, uint32_t *start_addr, uint32_t *len) { uint32_t PageEndAddr, RetrieveAddr, i; uint32_t flash_data; if(PageNum >= FLASH_PAGE_NUM)return FLASH_ERROR_PAGENUM; //获取该页的结尾地址 GetPageEndAddr(PageNum, &PageEndAddr); //从页尾开始检索空闲空间 RetrieveAddr = PageEndAddr; for(i = 0;i