ESP8266_RTOS_SDK 之spi flash驱动小窥
學習時間到,來看一下人家的flash驅動是咋寫的吧,用文字記錄不容易有所遺漏,也更加方便想起。
先從驅動測試代碼開始,進而切入調用關系底層,以下是sdk中測試flash read的測試代碼:
static void fill(char *dest, int32_t start, int32_t len) {for (int32_t i = 0; i < len; i++) {*(dest + i) = (char) (start + i);} } static void IRAM_ATTR test_read(int src_off, int dst_off, int len) {uint32_t src_buf[16];char dst_buf[64], dst_gold[64];fprintf(stderr, "src=%d dst=%d len=%d\n", src_off, dst_off, len);memset(src_buf, 0xAA, sizeof(src_buf));fill(((char *) src_buf) + src_off, src_off, len);ESP_ERROR_CHECK(spi_flash_erase_sector((start + src_off) / SPI_FLASH_SEC_SIZE));esp_err_t rc = spi_flash_write(start, src_buf, sizeof(src_buf));TEST_ASSERT_EQUAL_INT(rc, ESP_OK);memset(dst_buf, 0x55, sizeof(dst_buf));memset(dst_gold, 0x55, sizeof(dst_gold));fill(dst_gold + dst_off, src_off, len);ESP_ERROR_CHECK(spi_flash_read(start + src_off, dst_buf + dst_off, len));TEST_ASSERT_EQUAL_INT(cmp_or_dump(dst_buf, dst_gold, sizeof(dst_buf)), 0); }這里調用一個簡單的實際用例來步步跟蹤一下,因為直接看進去有點繞,假設現在如此調用:
test_read(0, 0, 4);
執行完第一個fill()函數后,src_buf[16]的內容變成了{0x03020100, 0xaaaaaaaa, ..., 0xaaaaaaaa};
接下來會先執行擦除函數?spi_flash_erase_sector(0x90000/4096),即sector number 是 0x90;
之后我們進到擦除函數中來看,
esp_err_t spi_flash_erase_sector(size_t sec) {FLASH_INTR_DECLARE(c_tmp);esp_err_t ret;if (sec >= (g_rom_flashchip.chip_size / g_rom_flashchip.sector_size)) {return ESP_ERR_FLASH_OP_FAIL;}if (spi_flash_check_wr_protect() == false) {return ESP_ERR_FLASH_OP_FAIL;}FLASH_INTR_LOCK(c_tmp);FlashIsOnGoing = 1;ret = spi_flash_erase_sector_raw(&g_rom_flashchip, sec, g_rom_flashchip.sector_size);FlashIsOnGoing = 0;FLASH_INTR_UNLOCK(c_tmp);return ret; }1,先判斷入口參數是否越界,若超出界限則返回錯誤;
2,再判斷被擦除的flash是否有寫保護,若有寫保護則返回錯誤;
3,進入臨界區(關中斷, vPortEnterCritical());
4,置位一個全局變量FlashIsOnGoing;
5,調用底層擦除函數?spi_flash_erase_sector_raw();
6,清零全局變量 FlashIsOnGoing;
7,離開臨界區(開中斷, vPortExitCritical());
?
接下來再跟進去spi_flash_erase_sector_raw()里面:
esp_err_t spi_flash_erase_sector_raw(esp_rom_spiflash_chip_t *chip, size_t sec, size_t sec_size) {esp_err_t ret = ESP_OK;Cache_Read_Disable_2();if (ESP_OK != SPI_write_enable(chip)) {ret = ESP_ERR_FLASH_OP_FAIL;}if (ESP_OK != SPI_sector_erase(chip, sec * sec_size)) {ret = ESP_ERR_FLASH_OP_FAIL;}Cache_Read_Enable_2();return ret; }這里面步驟不多,先是禁止從flash中向外讀數據,再判斷是否有寫保護,再就是調用底層的擦除函數來擦除指定的sector,之后再使能從flash中向外讀取數據;
?
我們再回到test_read()函數,擦除完sector0x90后,接下來會執行語句?spi_flash_write(start, src_buf, sizeof(src_buf));我們繼續跟進去,先說下這里傳入的參數分別是 (0x90000, src_buf, 64),其中src_buf中的數據上面已經有說明;
esp_err_t spi_flash_write(size_t dest_addr, const void *src, size_t size) { #undef FLASH_WRITE #define FLASH_WRITE(dest, src, size) \ { \ret = __spi_flash_write(dest, src, size); \esp_task_wdt_reset(); \if (ret) { \return ret; \} \ }#undef FLASH_READ #define FLASH_READ(dest, src, size) \ { \ret = spi_flash_read(dest, src, size); \if (ret) { \return ret; \} \ }esp_err_t ret = ESP_ERR_FLASH_OP_FAIL;uint8_t *tmp = (uint8_t *)src;if (!size)return ESP_OK;if (src == NULL) {return ESP_ERR_FLASH_OP_FAIL;}if ((dest_addr + size) > g_rom_flashchip.chip_size) {return ESP_ERR_FLASH_OP_FAIL;}if (spi_flash_check_wr_protect() == false) {return ESP_ERR_FLASH_OP_FAIL;}if (NOT_ALIGN(dest_addr)|| NOT_ALIGN(tmp)|| NOT_ALIGN(size)|| IS_FLASH(src)) {uint8_t buf[SPI_READ_BUF_MAX];if (NOT_ALIGN(dest_addr)) {size_t r_addr = FLASH_ALIGN_BEFORE(dest_addr);size_t c_off = dest_addr - r_addr;size_t wbytes = FLASH_ALIGN_BYTES - c_off;wbytes = wbytes > size ? size : wbytes;FLASH_READ(r_addr, buf, FLASH_ALIGN_BYTES);memcpy(&buf[c_off], tmp, wbytes);FLASH_WRITE(r_addr, buf, FLASH_ALIGN_BYTES);dest_addr += wbytes;tmp += wbytes;size -= wbytes;}while (size > 0) {size_t len = size >= SPI_READ_BUF_MAX ? SPI_READ_BUF_MAX : size;size_t wlen = FLASH_ALIGN(len);if (wlen != len) {size_t l_b = wlen - FLASH_ALIGN_BYTES;FLASH_READ(dest_addr + l_b, &buf[l_b], FLASH_ALIGN_BYTES); }memcpy(buf, tmp, len);FLASH_WRITE(dest_addr, buf, wlen);dest_addr += len;tmp += len;size -= len;}} else {FLASH_WRITE(dest_addr, src, size);}return ret; }1,函數進來首先定義了兩個宏FLASH_WRITE and FLASH_READ,分別用于寫和讀;
2,之后判斷入參是否合法,比如size是否大于0,起始地址是否為空,目的地址是否越界;
3,判斷是否有寫保護;
4,判斷入參是否4字節對齊,判斷起始地址是否合法,若對齊且合法,則調用FLASH_WRITE直接寫flash;否則先進行對齊相關操作;
對齊相關操作:
a,先判斷目標地址是否對齊,這個比較關鍵,若未對齊,則:
if (NOT_ALIGN(dest_addr)) {//計算出前一個對齊的地址,比如 若dests_addr為0x08000223,則r_addr計算出來就是 0x08000220size_t r_addr = FLASH_ALIGN_BEFORE(dest_addr); //c_off是前一個對齊的地址與實際地址的差值,按照上面的假設,這里c_off=3;size_t c_off = dest_addr - r_addr;//wbytes則是 1size_t wbytes = FLASH_ALIGN_BYTES - c_off;wbytes = wbytes > size ? size : wbytes;//將前一個對齊地址起始的4個字節讀取的buf中FLASH_READ(r_addr, buf, FLASH_ALIGN_BYTES);//將待寫入flash中的數據更新到buf的相應位置memcpy(&buf[c_off], tmp, wbytes);//將buf再寫入flash中,從而完成了目的地址對齊前的部分的flash寫操作;FLASH_WRITE(r_addr, buf, FLASH_ALIGN_BYTES);//將目的地址更新成下一個地址對齊的值;dest_addr += wbytes;//將源數據起始地址也相應后移;tmp += wbytes;//將待寫數據的長度相應減少;size -= wbytes;}?
TO BE CONTINUED
?
?
/* 20190506 以下是realtek8710 flash驅動調用示例代碼部分,其寫函數中也只是寫而已,寫之前先判斷將寫的地址是否為FF,若是則直接寫,不是則先調用擦除接口擦除待寫區域*/
flash_read_word(&flash,address,&data);if(data == ~0x0){device_mutex_lock(RT_DEV_LOCK_FLASH);flash_stream_write(&flash, address,sizeof(rtw_wifi_config_t), (uint8_t *)&wifi_config);device_mutex_unlock(RT_DEV_LOCK_FLASH);}else{//flash_EraseSector(sector_nb);device_mutex_lock(RT_DEV_LOCK_FLASH);flash_erase_sector(&flash,BACKUP_SECTOR);for(i = 0; i < 0x1000; i+= 4){flash_read_word(&flash, DATA_SECTOR + i, &data);if(i < sizeof(rtw_wifi_config_t)){memcpy(&data,(char *)(&wifi_config) + i,4);//printf("\n\r Wifi_config + %d = 0x%x",i,(void *)(&wifi_config + i));//printf("\n\r Data = %d",data);}flash_write_word(&flash, BACKUP_SECTOR + i,data);}flash_read_word(&flash,BACKUP_SECTOR + 68,&data);//printf("\n\r Base + BACKUP_SECTOR + 68 wifi channel = %d",data);//erase system dataflash_erase_sector(&flash, DATA_SECTOR);//write data back to system datafor(i = 0; i < 0x1000; i+= 4){flash_read_word(&flash, BACKUP_SECTOR + i, &data);flash_write_word(&flash, DATA_SECTOR + i,data);}//erase backup sectorflash_erase_sector(&flash, BACKUP_SECTOR);}device_mutex_unlock(RT_DEV_LOCK_FLASH);?
/* 寫函數,里面主要也是一些對齊的操作,其它沒什么特別,就是組包來配合其底層flash接口?*/
/*** @brief Write a stream of data to specified address* @param obj: Flash object define in application software.* @param address: Specifies the starting address to write to.* @param len: Specifies the length of the data to write.* @param data: Pointer to a byte array that is to be written.* @retval status: Success:1 or Failure: Others.*/ int flash_stream_write(flash_t *obj, u32 address, u32 len, u8 * data) {// Check address: 4byte aligned & page(256bytes) alignedu32 page_begin = address & (~0xff); u32 page_end = (address + len) & (~0xff);u32 page_cnt = ((page_end - page_begin) >> 8) + 1;u32 addr_begin = address;u32 addr_end = (page_cnt == 1) ? (address + len) : (page_begin + 0x100);u32 size = addr_end - addr_begin;u8 *buffer = data;u8 write_data[12];u32 offset_to_align;u32 read_word;u32 i;flash_write_lock();while(page_cnt){ offset_to_align = addr_begin & 0x3;if(offset_to_align != 0){read_word = HAL_READ32(SPI_FLASH_BASE, addr_begin - offset_to_align);for(i = offset_to_align;i < 4;i++){read_word = (read_word & (~(0xff << (8*i)))) |( (*buffer) <<(8*i));size--;buffer++;if(size == 0)break;}FLASH_TxData12B(addr_begin - offset_to_align, 4, (u8*)&read_word); #ifdef MICRON_N25Q00AAFLASH_ReadFlagStatusReg(); #endif}addr_begin = (((addr_begin-1) >> 2) + 1) << 2;for(;size >= 12 ;size -= 12){_memcpy(write_data, buffer, 12);FLASH_TxData12B(addr_begin, 12, write_data); #ifdef MICRON_N25Q00AAFLASH_ReadFlagStatusReg(); #endifbuffer += 12;addr_begin += 12;}for(;size >= 4; size -=4){_memcpy(write_data, buffer, 4); FLASH_TxData12B(addr_begin, 4, write_data); #ifdef MICRON_N25Q00AAFLASH_ReadFlagStatusReg(); #endifbuffer += 4;addr_begin += 4;}if(size > 0){read_word = HAL_READ32(SPI_FLASH_BASE, addr_begin);for( i = 0;i < size;i++){read_word = (read_word & (~(0xff << (8*i)))) | ((*buffer) <<(8*i));buffer++;}FLASH_TxData12B(addr_begin, 4, (u8*)&read_word); #ifdef MICRON_N25Q00AAFLASH_ReadFlagStatusReg(); #endif}page_cnt--;addr_begin = addr_end;addr_end = (page_cnt == 1) ? (address + len) : (((addr_begin>>8) + 1)<<8);size = addr_end - addr_begin; }Cache_Flush();flash_write_unlock();return 1; }?
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的ESP8266_RTOS_SDK 之spi flash驱动小窥的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: zephyr 测试框架
- 下一篇: STM32单片机如何使用JLINK下载