本文章介紹在Raspberry Pi Pico環境使用兩個以上外加的Flash memory device,並使用FatFs 檔案系統格式化為Fat。
一、使用元件:
- Raspberry Pi Pico -- 1
- Winbond W25Q128FV 16MB -- 2
二、接線:
兩個Winbond W25Q128FV Flash memory 都接在SPI 0,第一個Flash 使用 CS Pin 17,第二個使用CS Pin 20。
三、流程說明:
- 修改前一篇文章[Raspberry Pi Pico (c-sdk)] Storage: Ep1. Builtin XIP flash & external flash device with FatFs filesystem Flash device driver的Function加入w25q_data_t *w25q資料結構的參數,以便能對個別的device操作。
- 修改glue.c function針對每個Volume(Logical driver)呼叫個別的disk_status, disk_initialize, disk_read, disk_write, disk_ioctl。
#define FF_VOLUMES 4
/* Number of volumes (logical drives) to be used. (1-10) */
設定要同時使用的Volumes數
#define FF_FS_LOCK 5
/* The option FF_FS_LOCK switches file lock function to control duplicated file open
/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
/ is 1.
/
/ 0: Disable file lock function. To avoid volume corruption, application program
/ should avoid illegal open, remove and rename to the open objects.
/ >0: Enable file lock function. The value defines how many files/sub-directories
/ can be opened simultaneously under file lock control. Note that the file
/ lock control is independent of re-entrancy. */
設定要同時開啟的檔案數
- 測試流程:
- 個別執行f_mkfs將每個volume(flash device)格式化。
- 建立8KB亂數產生的測試資料。
- 在volume 0 建立test_p01.txt, test_p02.txt與test.txt三個檔案。在volume 1建立pat1_p11.txt, pat1_p12.txt與test.txt。
- 針對volume 0 與volume 1的test.txt寫入不同資料,再讀取以便驗證為確實在不同的volume上。
- 分別列出volume 0 與 volume 1的檔案名稱。
- 測試結果:
五、程式碼:
- W25Q.c
#include "stdio.h" #include "stdlib.h" #include "W25Q.h" #include "FatFs/ff.h" #include "FatFs/ffconf.h" uint8_t rxbuf[10]; uint8_t txbuf[10]; static bool spi_lock=false; static bool w25q_spi_init_flag=false; /*=================*/ const uint8_t i_uniqueid=0x4b; const uint8_t i_page_program=0x02; const uint8_t i_read_data=0x03; const uint8_t i_fast_read_data=0x0b; const uint8_t i_write_disable=0x04; const uint8_t i_read_status_r1=0x05; const uint8_t i_read_status_r2=0x35; const uint8_t i_read_status_r3=0x15; const uint8_t i_write_status_r1=0x01; const uint8_t i_write_status_r2=0x31; const uint8_t i_write_status_r3=0x11; const uint8_t i_sector_erase=0x20; const uint8_t i_block_erase_32k=0x52; const uint8_t i_block_erase_64k=0xd8; const uint8_t i_write_enable=0x06; const uint8_t i_erase_chip=0xc7; const uint8_t i_device_id=0x90; const uint8_t i_JEDEC_ID=0x9f; void w25q_spi_port_init() { gpio_set_dir(PIN_CS_0, GPIO_OUT); gpio_set_dir(PIN_CS_1, GPIO_OUT); gpio_put(PIN_CS_0, 1); gpio_put(PIN_CS_1, 1); gpio_set_function(PIN_MISO, GPIO_FUNC_SPI); gpio_set_function(PIN_SCK, GPIO_FUNC_SPI); gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI); gpio_set_function(PIN_CS_0, GPIO_FUNC_SIO); gpio_set_function(PIN_CS_1, GPIO_FUNC_SIO); spi_init(SPI_PORT, 5000*1000); w25q_spi_init_flag=true; } void w25q_spi_cs_low(w25q_data_t *w25q) { gpio_put(w25q->cs_pin,0); } void w25q_spi_cs_high(w25q_data_t *w25q){ gpio_put(w25q->cs_pin,1); } void w25q_send_cmd_read(uint8_t cmd, uint32_t address, uint8_t *buf, uint32_t len, bool is_fast, w25q_data_t *w25q) { uint8_t addr[4]; int addr_len=3; addr[3] = 0x00; if (is_fast) addr_len=4; addr[0] = (address & 0x00ff0000) >> 16; addr[1] = (address & 0x0000ff00) >> 8; addr[2] = (address & 0x000000ff); w25q_spi_cs_low(w25q); spi_write_blocking(w25q->spi, &cmd, 1); spi_write_blocking(w25q->spi, addr, addr_len); spi_read_blocking(w25q->spi, 0x00, buf, len); w25q_spi_cs_high(w25q); } void w25q_send_cmd_write(uint8_t cmd, uint32_t address, uint8_t *buf, uint32_t len, w25q_data_t *w25q) { uint8_t addr[3]; addr[0] = (address & 0x00ff0000) >> 16; addr[1] = (address & 0x0000ff00) >> 8; addr[2] = (address & 0x000000ff); w25q_write_enable(w25q); w25q_spi_cs_low(w25q); spi_write_blocking(w25q->spi, &cmd, 1); spi_write_blocking(w25q->spi, addr, 3); spi_write_blocking(w25q->spi, buf, len); w25q_spi_cs_high(w25q); } void w25q_send_cmd_addr(uint8_t cmd, uint32_t address, w25q_data_t *w25q) { uint8_t addr[3]; addr[0] = (address & 0x00ff0000) >> 16; addr[1] = (address & 0x0000ff00) >> 8; addr[2] = (address & 0x000000ff); w25q_spi_cs_low(w25q); spi_write_blocking(w25q->spi, &cmd, 1); spi_write_blocking(w25q->spi, addr, 3); w25q_spi_cs_high(w25q); } void w25q_send_cmd(uint8_t cmd, uint8_t *buf, uint32_t len, w25q_data_t *w25q) { w25q_spi_cs_low(w25q); spi_write_blocking(w25q->spi, &cmd, 1); spi_read_blocking(w25q->spi, 0x00, buf, len); w25q_spi_cs_high(w25q); } void w25q_send_simple_cmd(uint8_t cmd, w25q_data_t *w25q) { w25q_spi_cs_low(w25q); spi_write_blocking(w25q->spi, &cmd, 1); w25q_spi_cs_high(w25q); } void w25q_write_enable(w25q_data_t *w25q) { w25q_send_simple_cmd(i_write_enable, w25q); sleep_ms(1); } void w25q_write_disable(w25q_data_t *w25q) { w25q_send_simple_cmd(i_write_disable, w25q); sleep_ms(1); } /*==================*/ bool w25q_spi_init(spi_inst_t *spi, uint cs_pin, w25q_data_t *w25q) { w25q->spi = spi; w25q->cs_pin = cs_pin; if (!w25q_spi_init_flag) w25q_spi_port_init(); w25q_get_JEDEC_ID(w25q); w25q->lock = 1; sleep_ms(100); switch (w25q->jedec_id & 0x000000FF) { case 0x20: // w25q512 w25q->blockCount = 1024; break; case 0x19: // w25q256 w25q->blockCount = 512; break; case 0x18: // w25q128 w25q->blockCount = 256; break; case 0x17: // w25q64 w25q->blockCount = 128; break; case 0x16: // w25q32 w25q->blockCount = 64; break; case 0x15: // w25q16 w25q->blockCount = 32; break; case 0x14: // w25q80 w25q->blockCount = 16; break; case 0x13: // w25q40 w25q->blockCount = 8; case 0x12: // w25q20 w25q->blockCount = 4; break; case 0x11: // w25q10 w25q->blockCount = 2; break; default: w25q->lock = 0; return false; } w25q->pageSize = 256; w25q->sectorSize = 0x1000; w25q->sectorCount = w25q->blockCount * 16; w25q->pageCount = (w25q->sectorCount * w25q->sectorSize) / w25q->pageSize; w25q->blockSize = w25q->sectorSize * 16; w25q->capacityKB = (w25q->sectorCount * w25q->sectorSize) / 1024; w25q_get_uid(w25q); w25q_read_status_register_1(w25q); w25q_read_status_register_2(w25q); w25q_read_status_register_3(w25q); w25q->lock = 0; return true; } void w25q_read_status_register_1(w25q_data_t *w25q){ w25q_send_cmd(i_read_status_r1, &w25q->statusRegister1, 1, w25q); } void w25q_read_status_register_2(w25q_data_t *w25q){ w25q_send_cmd(i_read_status_r2, &w25q->statusRegister2, 1, w25q); } void w25q_read_status_register_3(w25q_data_t *w25q){ w25q_send_cmd(i_read_status_r3, &w25q->statusRegister3, 1, w25q); } void w25q_write_status_register_1(w25q_data_t *w25q){ w25q_send_cmd(i_write_status_r1, &w25q->statusRegister1, 1, w25q); } void w25q_write_status_register_2(w25q_data_t *w25q){ w25q_send_cmd(i_write_status_r2, &w25q->statusRegister2, 1, w25q); } void w25q_write_status_register_3(w25q_data_t *w25q){ w25q_send_cmd(i_write_status_r3, &w25q->statusRegister3, 1, w25q); } void w25q_wait_for_write_end(w25q_data_t *w25q) { sleep_ms(1); w25q_spi_cs_low(w25q); spi_write_blocking(w25q->spi, &i_read_status_r1,1); do { spi_read_blocking(w25q->spi, 0x00, &w25q->statusRegister1,1); sleep_ms(1); } while ((w25q->statusRegister1 & 0x01) == 0x01); w25q_spi_cs_high(w25q); } void w25q_erase_chip(w25q_data_t *w25q) { while (w25q->lock) sleep_ms(1); w25q->lock=1; w25q_write_enable(w25q); w25q_send_simple_cmd(i_erase_chip, w25q); w25q_wait_for_write_end(w25q); sleep_ms(10); w25q->lock=0; } void w25q_page_program(uint32_t page_addr, uint16_t offset, uint8_t *buf, uint32_t len, w25q_data_t *w25q) { while (w25q->lock) sleep_ms(1); w25q->lock=1; if (offset + len > w25q->pageSize) { len = w25q->pageSize - offset; } page_addr = (page_addr * w25q->pageSize) + offset; w25q_wait_for_write_end(w25q); w25q_write_enable(w25q); w25q_send_cmd_write(i_page_program, page_addr, buf, len, w25q); w25q_wait_for_write_end(w25q); sleep_ms(1); w25q->lock=0; } /*===========================*/ uint32_t w25_page_to_sector_address(uint32_t pageAddress, w25q_data_t *w25q) { return ((pageAddress * w25q->pageSize) / w25q->sectorSize); } uint32_t w25q_page_to_block_address(uint32_t pageAddress, w25q_data_t *w25q) { return ((pageAddress * w25q->pageSize) / w25q->blockSize); } uint32_t w25q_data_sector_to_block_address(uint32_t sectorAddress, w25q_data_t *w25q) { return ((sectorAddress * w25q->sectorSize) / w25q->blockSize); } uint32_t w25q_sector_to_page_address(uint32_t sectorAddress, w25q_data_t *w25q) { return (sectorAddress * w25q->sectorSize) / w25q->pageSize; } uint32_t w25q_block_to_page_address(uint32_t blockAddress, w25q_data_t *w25q) { return (blockAddress * w25q->blockSize) / w25q->pageSize; } /*============================*/ void w25q_write_sector(uint32_t sect_addr, uint32_t offset, uint8_t *buf, uint32_t len, w25q_data_t *w25q) { if (offset >= w25q->sectorSize) return; if (offset + len > w25q->sectorSize) len = w25q->sectorSize - offset; uint32_t startPage; int32_t bytesToWrite; uint32_t localOffset; startPage = w25q_sector_to_page_address(sect_addr, w25q) + (offset / w25q->pageSize); localOffset = offset % w25q->pageSize; bytesToWrite = len; do { w25q_page_program(startPage, localOffset, buf, bytesToWrite, w25q); startPage++; bytesToWrite -= w25q->pageSize - localOffset; buf += w25q->pageSize - localOffset; localOffset = 0; } while (bytesToWrite > 0); } void w25q_write_block_64k(uint32_t blk_addr, uint32_t offset, uint8_t *buf, uint32_t len, w25q_data_t *w25q) { if ((len > w25q->blockSize) || (len == 0)) len = w25q->blockSize; if (offset >= w25q->blockSize) return; uint32_t startPage; int32_t bytesToWrite; uint32_t localOffset; if ((offset + len) > w25q->blockSize) bytesToWrite = w25q->blockSize - offset; else bytesToWrite = len; startPage = w25q_block_to_page_address(blk_addr, w25q) + (offset / w25q->pageSize); localOffset = offset % w25q->pageSize; do { w25q_page_program(startPage, localOffset, buf, len, w25q); startPage++; bytesToWrite -= w25q->pageSize - localOffset; buf += w25q->pageSize - localOffset; localOffset = 0; } while (bytesToWrite > 0); } void w25q_read_bytes(uint32_t address, uint8_t *buf, uint32_t len, w25q_data_t *w25q) { while (w25q->lock == 1) sleep_ms(1); w25q->lock = 1; w25q_send_cmd_read(i_fast_read_data, address, buf, len, true, w25q); sleep_ms(1); w25q->lock = 0; } void w25q_read_page(uint32_t page_addr, uint32_t offset, uint8_t *buf, uint32_t len, w25q_data_t *w25q) { while (w25q->lock == 1) sleep_ms(1); w25q->lock = 1; if (offset >= w25q->pageSize) return; if ((offset + len) >= w25q->pageSize) len = w25q->pageSize - offset; page_addr = page_addr * w25q->pageSize + offset; w25q_send_cmd_read(i_fast_read_data, page_addr, buf, len, true, w25q); sleep_ms(1); w25q->lock = 0; } void w25q_read_sector(uint32_t sect_addr, uint32_t offset, uint8_t *buf, uint32_t len, w25q_data_t *w25q) { if (offset >= w25q->sectorSize) return; if (offset + len > w25q->sectorSize) len = w25q->sectorSize - offset; uint32_t startPage; int32_t bytesToRead; uint32_t localOffset; bytesToRead = len; startPage = w25q_sector_to_page_address(sect_addr, w25q) + (offset / w25q->pageSize); localOffset = offset % w25q->pageSize; do { w25q_read_page(startPage, localOffset, buf, bytesToRead, w25q); startPage++; bytesToRead -= w25q->pageSize - localOffset; buf += w25q->pageSize - localOffset; localOffset = 0; } while (bytesToRead > 0); } void w25q_read_block(uint32_t blk_addr, uint32_t offset, uint8_t *buf, uint32_t len, w25q_data_t *w25q) { if (offset+len > w25q->blockSize) len = w25q->blockSize-offset; uint32_t startPage; int32_t bytesToRead; uint32_t localOffset; bytesToRead = len; startPage = w25q_block_to_page_address(blk_addr, w25q) + (offset / w25q->pageSize); localOffset = offset % w25q->pageSize; do { w25q_read_page(startPage, localOffset, buf, bytesToRead, w25q); startPage++; bytesToRead -= w25q->pageSize - localOffset; buf += w25q->pageSize - localOffset; localOffset = 0; } while (bytesToRead > 0); } void w25q_sector_erase(uint32_t sect_addr, w25q_data_t *w25q) { while(w25q->lock) sleep_ms(1); w25q->lock=1; sect_addr = sect_addr * w25q->sectorSize; w25q_wait_for_write_end(w25q); w25q_write_enable(w25q); w25q_send_cmd_addr(i_sector_erase, sect_addr, w25q); w25q_wait_for_write_end(w25q); sleep_ms(1); w25q->lock=0; } void w25q_block_erase_32k(uint32_t blk_addr, w25q_data_t *w25q) { while(w25q->lock) sleep_ms(1); w25q->lock=1; blk_addr = blk_addr * w25q->sectorSize * 8; w25q_wait_for_write_end(w25q); w25q_write_enable(w25q); w25q_send_cmd_addr(i_block_erase_32k, blk_addr, w25q); w25q_wait_for_write_end(w25q); sleep_ms(1); w25q->lock=0; } void w25q_block_erase_64k(uint32_t blk_addr, w25q_data_t *w25q) { while(w25q->lock) sleep_ms(1); w25q->lock=1; blk_addr = blk_addr * w25q->sectorSize * 16; w25q_wait_for_write_end(w25q); w25q_write_enable(w25q); w25q_send_cmd_addr(i_block_erase_64k, blk_addr, w25q); w25q_wait_for_write_end(w25q); sleep_ms(1); w25q->lock=0; } void w25q_get_manufacter_device_id(uint8_t *mid, w25q_data_t *w25q){ assert(w25q->spi); w25q_send_cmd_read(i_device_id, 0x000000, mid, 2, false, w25q); } void w25q_get_JEDEC_ID(w25q_data_t *w25q) { uint8_t temp[3]; w25q_send_cmd(i_JEDEC_ID, temp, 3, w25q); w25q->jedec_id = ((uint32_t)temp[0] << 16) | ((uint32_t)temp[1] << 8) | (uint32_t)temp[2]; } void w25q_get_uid(w25q_data_t *w25q) { assert(w25q->spi); txbuf[0]= 0x4b; txbuf[1] = 0x00; txbuf[2] = 0x00; txbuf[3] = 0x00;txbuf[4]=0x00; w25q_spi_cs_low(w25q); spi_write_blocking(w25q->spi, txbuf, 5); spi_read_blocking(w25q->spi, 0x00, w25q->uuid, 8); w25q_spi_cs_high(w25q); }
- w25Q.h
#ifndef W25Q_H #define W25Q_H #include "stdio.h" #include "stdlib.h" #include "pico/stdlib.h" #include "hardware/spi.h" #define SPI_PORT spi0 #define PIN_MISO 16 #define PIN_SCK 18 #define PIN_MOSI 19 #define PIN_CS_0 17 #define PIN_CS_1 20 typedef struct{ spi_inst_t *spi; uint cs_pin; uint8_t uuid[8]; uint32_t jedec_id; uint32_t blockCount; uint32_t pageCount; uint32_t sectorCount; uint16_t pageSize; uint32_t sectorSize; uint32_t blockSize; uint8_t statusRegister1; uint8_t statusRegister2; uint8_t statusRegister3; uint32_t capacityKB; uint8_t lock; uint8_t Stat; }w25q_data_t; bool w25q_spi_init(spi_inst_t *spi, uint cs_pin, w25q_data_t *w25q); void w25q_get_manufacter_device_id(uint8_t *mid, w25q_data_t *w25q); void w25q_get_JEDEC_ID(w25q_data_t *w25q); void w25q_erase_chip(w25q_data_t *w25q); void w25q_page_program(uint32_t page_addr, uint16_t offset, uint8_t *buf, uint32_t len, w25q_data_t *w25q); void w25q_write_sector(uint32_t sect_addr, uint32_t offset, uint8_t *buf, uint32_t len, w25q_data_t *w25q); void w25q_write_block_64k(uint32_t blk_addr, uint32_t offset, uint8_t *buf, uint32_t len, w25q_data_t *w25q); void w25q_read_bytes(uint32_t address, uint8_t *buf, uint32_t len, w25q_data_t *w25q); void w25q_read_page(uint32_t page_addr, uint32_t offset, uint8_t *buf, uint32_t len, w25q_data_t *w25q); void w25q_read_sector(uint32_t sect_addr, uint32_t offset, uint8_t *buf, uint32_t len, w25q_data_t *w25q); void w25q_read_block(uint32_t blk_addr, uint32_t offset, uint8_t *buf, uint32_t len, w25q_data_t *w25q); //void w25q_read_data(uint32_t address, uint8_t *buf, uint32_t len); //void w25q_fast_read_data(uint32_t address, uint8_t *buf, uint32_t len); void w25q_read_status_register_1(w25q_data_t *w25q); void w25q_read_status_register_2(w25q_data_t *w25q); void w25q_read_status_register_3(w25q_data_t *w25q); void w25q_write_status_register_1(w25q_data_t *w25q); void w25q_write_status_register_2(w25q_data_t *w25q); void w25q_write_status_register_3(w25q_data_t *w25q); void w25q_sector_erase(uint32_t sect_addr, w25q_data_t *w25q); void w25q_block_erase_32k(uint32_t blk_addr,w25q_data_t *w25q); void w25q_block_erase_64k(uint32_t blk_addr, w25q_data_t *w25q); void w25q_get_uid(w25q_data_t *w25q); void w25q_write_enable(w25q_data_t *w25q); void w25q_write_diable(w25q_data_t *w25q); #endif
- glue.c
#include "stdio.h"
#include "ff.h" /* Obtains integer types */
#include "diskio.h" /* Declarations of disk functions */
#include "W25Q.h"
#include "hardware/rtc.h"
#include "pico/util/datetime.h"
#include "stdlib.h"
#define SPI_FLASH_0 0
#define SPI_FLASH_1 1
w25q_data_t *w25q_spi_cs0=NULL;
w25q_data_t *w25q_spi_cs1=NULL;
DSTATUS Stat = STA_NOINIT;
/*-----------------------------------------------------------------------*/
/* Get Drive Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
DSTATUS stat;
stat = STA_NOINIT;
if (pdrv == SPI_FLASH_0 && w25q_spi_cs0) {
stat = w25q_spi_cs0->Stat;
}
if (pdrv == SPI_FLASH_1 && w25q_spi_cs1) {
stat = w25q_spi_cs0->Stat;
}
return stat;
}
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
DSTATUS stat;
stat=STA_NOINIT;
switch (pdrv) {
case SPI_FLASH_0:
if (w25q_spi_cs0 == NULL) w25q_spi_cs0 = (w25q_data_t*)malloc(sizeof(w25q_data_t));
if (w25q_spi_init(SPI_PORT, PIN_CS_0, w25q_spi_cs0)) {
w25q_spi_cs0->Stat = RES_OK;
stat = w25q_spi_cs0->Stat;
}
break;
case SPI_FLASH_1:
if (w25q_spi_cs1 == NULL) w25q_spi_cs1 = (w25q_data_t*)malloc(sizeof(w25q_data_t));
if (w25q_spi_init(SPI_PORT, PIN_CS_1, w25q_spi_cs1)) {
w25q_spi_cs1->Stat = RES_OK;
stat = w25q_spi_cs1->Stat;
}
break;
}
return stat;
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to read */
)
{
DRESULT res;
switch (pdrv) {
case SPI_FLASH_0:
if (w25q_spi_cs0->Stat & STA_NOINIT) return RES_NOTRDY;
w25q_read_sector(sector, 0, buff, count*w25q_spi_cs0->sectorSize, w25q_spi_cs0);
break;
case SPI_FLASH_1:
if (w25q_spi_cs1->Stat & STA_NOINIT) return RES_NOTRDY;
w25q_read_sector(sector, 0, buff, count*w25q_spi_cs1->sectorSize, w25q_spi_cs1);
break;
}
return RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
#if FF_FS_READONLY == 0
DRESULT disk_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to write */
)
{
BYTE *tbuf=(BYTE*)buff;
w25q_data_t * tmp_w25q=NULL;
switch (pdrv) {
case SPI_FLASH_0:
if (w25q_spi_cs0 == NULL) return RES_NOTRDY;
tmp_w25q = w25q_spi_cs0;
break;
case SPI_FLASH_1:
if (w25q_spi_cs1 == NULL) return RES_NOTRDY;
tmp_w25q = w25q_spi_cs1;
break;
}
if (pdrv == SPI_FLASH_0 || pdrv == SPI_FLASH_1) {
while(count > 1)
{
w25q_sector_erase(sector, tmp_w25q);
w25q_write_sector(sector, 0, tbuf, tmp_w25q->sectorSize, tmp_w25q);
count--;
tbuf += tmp_w25q->sectorSize;
sector++;
}
if (count == 1)
{
w25q_sector_erase(sector, tmp_w25q);
w25q_write_sector(sector, 0, tbuf, tmp_w25q->sectorSize, tmp_w25q);
count--;
}
}
return count ? RES_ERROR : RES_OK;
}
#endif
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
DRESULT res = RES_ERROR;
switch(cmd) {
case CTRL_SYNC:
res = RES_OK;
break;
case GET_SECTOR_SIZE:
if (pdrv == SPI_FLASH_0) {
*(WORD*)buff = (WORD)w25q_spi_cs0->sectorSize; // in f_mkfs() [WORD ss]
res = RES_OK;
}
if (pdrv == SPI_FLASH_1) {
*(WORD*)buff = (WORD)w25q_spi_cs1->sectorSize; // in f_mkfs() [WORD ss]
res = RES_OK;
}
break;
case GET_BLOCK_SIZE:
if (pdrv == SPI_FLASH_0) {
*(DWORD*)buff = w25q_spi_cs0->blockSize;
res = RES_OK;
}
if (pdrv == SPI_FLASH_1) {
*(DWORD*)buff = w25q_spi_cs1->blockSize;
res = RES_OK;
}
break;
case GET_SECTOR_COUNT:
if (pdrv == SPI_FLASH_0) {
*(DWORD*)buff = w25q_spi_cs0->sectorCount;
res = RES_OK;
}
if (pdrv == SPI_FLASH_1) {
*(DWORD*)buff = w25q_spi_cs1->sectorCount;
res = RES_OK;
}
break;
default:
res = RES_PARERR;
break;
}
return res;
}
DWORD get_fattime(void) {
datetime_t t = {0, 0, 0, 0, 0, 0, 0};
bool rc = rtc_get_datetime(&t);
if (!rc) return 0;
DWORD fattime = 0;
// bit31:25
// Year origin from the 1980 (0..127, e.g. 37 for 2017)
uint8_t yr = t.year - 1980;
fattime |= (0b01111111 & yr) << 25;
// bit24:21
// Month (1..12)
uint8_t mo = t.month;
fattime |= (0b00001111 & mo) << 21;
// bit20:16
// Day of the month (1..31)
uint8_t da = t.day;
fattime |= (0b00011111 & da) << 16;
// bit15:11
// Hour (0..23)
uint8_t hr = t.hour;
fattime |= (0b00011111 & hr) << 11;
// bit10:5
// Minute (0..59)
uint8_t mi = t.min;
fattime |= (0b00111111 & mi) << 5;
// bit4:0
// Second / 2 (0..29, e.g. 25 for 50)
uint8_t sd = t.sec / 2;
fattime |= (0b00011111 & sd);
return fattime;
}
- spi_multi_flash_fatfs.c
#include <stdio.h> #include "pico/stdlib.h" #include "hardware/spi.h" #include "W25Q.h" #include "ff.h" #include "diskio.h" #include "hardware/flash.h" #include "string.h" #define FLASH_PATH_0 "0:" #define FLASH_PATH_1 "1:" FRESULT scan_files ( char* path /* Start node to be scanned (***also used as work area***) */ ) { FRESULT res; DIR dir; UINT i; static FILINFO fno; res = f_opendir(&dir, path); /* Open the directory */ if (res == FR_OK) { for (;;) { res = f_readdir(&dir, &fno); /* Read a directory item */ if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ if (fno.fattrib & AM_DIR) { /* It is a directory */ i = strlen(path); sprintf(&path[i], "/%s", fno.fname); res = scan_files(path); /* Enter the directory */ if (res != FR_OK) break; path[i] = 0; } else { /* It is a file. */ printf("%s/%s\n", path, fno.fname); } } f_closedir(&dir); } return res; } int main() { stdio_init_all(); FRESULT res; FATFS fs0, fs1; FIL fil0, fil1; UINT br; uint8_t work[4096*2]; uint8_t readbuff[4096*2]; res = f_mkfs(FLASH_PATH_0, 0, work, 4096); if(res != FR_OK) { printf("mkfs 0 error\n"); return 1; } printf("\n\nmkfs 0 successfully(run only once): remove all data\n"); res = f_mkfs(FLASH_PATH_1, 0, work, 4096); if(res != FR_OK) { printf("mkfs 1 error\n"); return 1; } printf("mkfs 1 successfully(run only once): remove all data\n"); // generator test data for (uint32_t i=0; i < 4096*2; i++) { uint8_t c = rand() % 127; if (c < 31) c+=31; work[i] = c; } printf("\nwrite buffer:200-300\n"); for (uint32_t i = 200; i <= 300; i++) { printf("%c",work[i]); } printf("\n"); //mount fs0 res = f_mount(&fs0, FLASH_PATH_0, 1); if (res != FR_OK) { printf("mount 0 error\n"); return 1; } // mount fs1 res = f_mount(&fs1, FLASH_PATH_1, 1); if (res != FR_OK) { printf("mount drv 1 error\n"); return 1; } res = f_open(&fil0, FLASH_PATH_0"/test_p01.txt", FA_CREATE_ALWAYS|FA_WRITE); f_write(&fil0, "01abc", 5, &br); f_close(&fil0); res = f_open(&fil0, FLASH_PATH_0"/test_p02.txt", FA_CREATE_ALWAYS|FA_WRITE); f_write(&fil0, "02abc", 5, &br); f_close(&fil0); res = f_open(&fil0, FLASH_PATH_0"/test.txt", FA_CREATE_ALWAYS|FA_WRITE); if (res != FR_OK) { printf("open error\n"); return 1; } res = f_write(&fil0, work, 4096*2, &br); f_close(&fil0); res = f_open(&fil1, FLASH_PATH_1"/pat1_p11.txt", FA_CREATE_ALWAYS|FA_WRITE); f_close(&fil1); res = f_open(&fil1, FLASH_PATH_1"/pat1_p12.txt", FA_CREATE_ALWAYS|FA_WRITE); f_close(&fil1); res = f_open(&fil1, FLASH_PATH_1"/test.txt", FA_CREATE_ALWAYS|FA_WRITE); if (res != FR_OK) { printf("open 1 error\n"); return 1; } // change some data and write into FLASH_PATH_1 memcpy(work+200, "FLASH_1", 7); f_write(&fil1, work, 4096*2, &br); f_close(&fil1); res = f_open(&fil0, FLASH_PATH_0"/test.txt", FA_READ); if (res != FR_OK) { printf("read open error\n"); return 1; } f_read(&fil0, readbuff, 4096*2, &br); printf("\nread FLASH_PATH_0/test.txt buff:200-300\n"); for (uint32_t i = 200; i <= 300; i++) { printf("%c",readbuff[i]); } printf("\n"); res = f_open(&fil1, FLASH_PATH_1"/test.txt", FA_READ); if (res != FR_OK) { printf("read open 1 error\n"); return 1; } f_read(&fil1, readbuff, 4096*2, &br); printf("\nread FLASH_PATH_1/test.txt buff:200-300\n"); for (uint32_t i = 200; i <= 300; i++) { printf("%c",readbuff[i]); } printf("\n"); f_close(&fil0); f_close(&fil1); printf("\nlist all files in FLASH_PATH_0\n"); scan_files(FLASH_PATH_0); printf("\nlist all files in FLASH_PATH_1\n"); scan_files(FLASH_PATH_1); f_unmount(FLASH_PATH_0); f_unmount(FLASH_PATH_1); return 0; }
沒有留言:
張貼留言