本文章介紹在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;
}


沒有留言:
張貼留言