prettyprint

2022年11月24日 星期四

[Raspberry Pi Pico (c-sdk)] Storage: Ep 2. Multi external flash memory devices with FatFs filesystem

本文章介紹在Raspberry Pi Pico環境使用兩個以上外加的Flash memory device,並使用FatFs 檔案系統格式化為Fat。

一、使用元件:

  1. Raspberry Pi Pico -- 1
  2. Winbond W25Q128FV 16MB -- 2


二、接線:

兩個Winbond W25Q128FV Flash memory 都接在SPI 0,第一個Flash 使用 CS Pin 17,第二個使用CS Pin 20。

三、流程說明:



  • 修改glue.c function針對每個Volume(Logical driver)呼叫個別的disk_status, disk_initialize, disk_read, disk_write, disk_ioctl。

  • 修改ffconf.h中的:

#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. */
設定要同時開啟的檔案數

  • 測試流程
  1. 個別執行f_mkfs將每個volume(flash device)格式化。
  2. 建立8KB亂數產生的測試資料。
  3. 在volume 0 建立test_p01.txt, test_p02.txt與test.txt三個檔案。在volume 1建立pat1_p11.txt, pat1_p12.txt與test.txt。
  4. 針對volume 0 與volume 1的test.txt寫入不同資料,再讀取以便驗證為確實在不同的volume上。
  5. 分別列出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;
}


沒有留言:

張貼留言