本實驗 整合SD/MMC Card與Flash Memory device一起使用。
- 測試項目:
- SD/MMC Low Level Disk I/O,沒有使用檔案系統,直接進行Sector Address I/O
- SD/MMC 在有沒有使用DMA時,I/O效能比較。
- 同時掛載SD/MMC Card與Flash memory device進行I/O測試。
SD/MMC driver程式寫作參考網址 FatFs - Generic FAT Filesystem Module,Flash memory device driver程式碼請參閱上篇文章:[Raspberry Pi Pico (c-sdk)] Storage: Ep 2. Multi external flash memory devices with FatFs filesystem
- SPI 腳位連接:
- ffconf.h修改項目:
展示影片:
程式碼:
- glue.c
#include "stdio.h"
#include "stdlib.h"
#include "ff.h"
#include "diskio.h"
#include "spi_sdmmc.h"
#include "W25Q.h"
#include "hardware/rtc.h"
#include "inttypes.h"
#define SDMMC_DRV_0 0
#define W25Q_DRV_1 1
sdmmc_data_t *pSDMMC=NULL;
w25q_data_t *pW25Q = NULL;
//==================//
DSTATUS disk_initialize (BYTE drv){
DSTATUS stat;
switch (drv) {
case SDMMC_DRV_0:
if (pSDMMC == NULL) {
pSDMMC = (sdmmc_data_t*)malloc(sizeof(sdmmc_data_t));
pSDMMC->csPin = SDMMC_PIN_CS;
pSDMMC->spiPort = SDMMC_SPI_PORT;
pSDMMC->spiInit=false;
pSDMMC->sectSize=512;
#ifdef __SPI_SDMMC_DMA
pSDMMC->dmaInit=false;
#endif
}
stat = sdmmc_disk_initialize(pSDMMC);
return stat;
break;
case W25Q_DRV_1:
if (pW25Q == NULL) {
pW25Q = (w25q_data_t*)malloc(sizeof(w25q_data_t));
pW25Q->spiInit=false;
pW25Q->Stat=STA_NOINIT;
}
stat = w25q_disk_initialize(W25Q_SPI_PORT, W25Q_PIN_CS, pW25Q);
return stat;
break;
}
return STA_NOINIT;
}
/*-----------------------------------------------------------------------*/
/* Get disk status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (BYTE drv) {
DSTATUS stat;
switch (drv) {
case SDMMC_DRV_0:
stat= sdmmc_disk_status(pSDMMC); /* Return disk status */
return stat;
break;
case W25Q_DRV_1:
stat = pW25Q->Stat;
return stat;
break;
}
return RES_PARERR;
}
/*-----------------------------------------------------------------------*/
/* Read sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE drv, /* Physical drive number (0) */
BYTE *buff, /* Pointer to the data buffer to store read data */
LBA_t sector, /* Start sector number (LBA) */
UINT count /* Number of sectors to read (1..128) */
)
{
DSTATUS stat;
switch (drv) {
case SDMMC_DRV_0:
stat = sdmmc_disk_read(buff, sector, count, pSDMMC);
return stat;
break;
case W25Q_DRV_1:
if (pW25Q->Stat & STA_NOINIT) return RES_NOTRDY;
w25q_read_sector((uint32_t)sector, 0, buff, count*pW25Q->sectorSize, pW25Q);
return pW25Q->Stat;
break;
}
return RES_PARERR;
}
/*-----------------------------------------------------------------------*/
/* Write sector(s) */
/*-----------------------------------------------------------------------*/
#if FF_FS_READONLY == 0
DRESULT disk_write (
BYTE drv, /* Physical drive number (0) */
const BYTE *buff, /* Ponter to the data to write */
LBA_t sector, /* Start sector number (LBA) */
UINT count /* Number of sectors to write (1..128) */
)
{
DSTATUS stat = STA_NODISK;
switch (drv) {
case SDMMC_DRV_0:
stat = sdmmc_disk_write(buff, sector, count, pSDMMC);
return stat;
break;
case W25Q_DRV_1:
stat = w25q_disk_write(buff, sector, count, pW25Q);
return stat;
break;
}
return RES_PARERR;
}
#endif
/*-----------------------------------------------------------------------*/
/* Miscellaneous drive controls other than data read/write */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (
BYTE drv, /* Physical drive number (0) */
BYTE cmd, /* Control command code */
void *buff /* Pointer to the conrtol data */
)
{
DSTATUS stat;
switch (drv) {
case SDMMC_DRV_0:
stat = sdmmc_disk_ioctl(cmd, buff, pSDMMC);
return stat;
break;
case W25Q_DRV_1:
stat = w25q_disk_ioctl(cmd, buff, pW25Q);
return stat;
break;
}
return RES_PARERR;
}
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;
}- W25Q.c
#include "stdio.h"
#include "stdlib.h"
#include "W25Q.h"
uint8_t rxbuf[10];
uint8_t txbuf[10];
/*=================*/
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(w25q_data_t *w25q) {
gpio_set_dir(w25q->cs_pin, GPIO_OUT);
gpio_put(w25q->cs_pin, 1);
gpio_set_function(w25q->cs_pin, GPIO_FUNC_SIO);
gpio_set_function(W25Q_PIN_MISO, GPIO_FUNC_SPI);
gpio_set_function(W25Q_PIN_SCK, GPIO_FUNC_SPI);
gpio_set_function(W25Q_PIN_MOSI, GPIO_FUNC_SPI);
printf("\nThe actual baudrate(W25Q):%d\n",spi_init(w25q->spi, SPI_BAUDRATE_HIGH));
w25q->spiInit=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);
}
/*==================*/
DRESULT w25q_disk_initialize(spi_inst_t *spi, uint cs_pin, w25q_data_t *w25q) {
w25q->spi = spi;
w25q->cs_pin = cs_pin;
if (!w25q->spiInit) w25q_spi_port_init(w25q);
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;
w25q->Stat &= ~STA_NOINIT;
return w25q->Stat;
}
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);
}
DRESULT w25q_disk_write(
const BYTE *buff, /* Ponter to the data to write */
LBA_t sector, /* Start sector number (LBA) */
UINT count, /* Number of sectors to write (1..128) */
w25q_data_t *w25q
)
{
BYTE *tbuf=(BYTE*)buff;
while(count > 1)
{
w25q_sector_erase(sector, w25q);
w25q_write_sector(sector, 0, tbuf, w25q->sectorSize, w25q);
count--;
tbuf += w25q->sectorSize;
sector++;
}
if (count == 1)
{
w25q_sector_erase(sector, w25q);
w25q_write_sector(sector, 0, tbuf, w25q->sectorSize, w25q);
count--;
}
return count? RES_ERROR: RES_OK;
}
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;
}
DRESULT 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 RES_ERROR;
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);
return RES_OK;
}
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);
}
DRESULT w25q_disk_ioctl (
BYTE cmd, /* Control code */
void *buff, /* Buffer to send/receive control data */
w25q_data_t *w25q
)
{
DRESULT res = RES_ERROR;
switch(cmd) {
case CTRL_SYNC:
res = RES_OK;
break;
case GET_SECTOR_SIZE:
*(WORD*)buff = (WORD)w25q->sectorSize; // in f_mkfs() [WORD ss]
res = RES_OK;
break;
case GET_BLOCK_SIZE:
*(DWORD*)buff = w25q->blockSize;
res = RES_OK;
break;
case GET_SECTOR_COUNT:
*(DWORD*)buff = w25q->sectorCount;
res = RES_OK;
break;
default:
res = RES_PARERR;
break;
}
return res;
}- W25q.h
#ifndef W25Q_H
#define W25Q_H
#include "stdio.h"
#include "stdlib.h"
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "ff.h"
#include "ffconf.h"
#include "diskio.h"
/* W25Q SPI pins*/
#define W25Q_SPI_PORT spi0
#define W25Q_PIN_MISO 16
#define W25Q_PIN_SCK 18
#define W25Q_PIN_MOSI 19
#define W25Q_PIN_CS 17
/* ====================== */
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;
bool spiInit;
DRESULT Stat;
}w25q_data_t;
DRESULT w25q_disk_initialize(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);
DRESULT 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);
DRESULT w25q_disk_write(const BYTE *buff, LBA_t sector, UINT count,w25q_data_t *w25q);
DRESULT w25q_disk_ioctl(BYTE cmd, void *buff, w25q_data_t *w25q);
#endif- spi_sdmmc.c
/*
This library is derived from ChaN's FatFs - Generic FAT Filesystem Module.
*/
#include "stdio.h"
#include "stdlib.h"
#include "pico/stdlib.h"
#include "spi_sdmmc.h"
#define SDMMC_CD 0 // card detect
#define SDMMC_WP 0 // write protected
static uint8_t dummy_block[512];
/* sdmmc spi port initialize*/
void sdmmc_spi_port_init(sdmmc_data_t *sdmmc)
{
spi_init(sdmmc->spiPort, SPI_BAUDRATE_LOW);
gpio_set_function(SDMMC_PIN_MISO, GPIO_FUNC_SPI);
gpio_set_function(sdmmc->csPin, GPIO_FUNC_SIO);
gpio_set_function(SDMMC_PIN_SCK, GPIO_FUNC_SPI);
gpio_set_function(SDMMC_PIN_MOSI, GPIO_FUNC_SPI);
gpio_set_dir(sdmmc->csPin, GPIO_OUT);
gpio_put(sdmmc->csPin, 1); // deselect
sdmmc->spiInit = true; // alreadily initialized
}
/* config spi dma*/
#ifdef __SPI_SDMMC_DMA
void config_spi_dma(sdmmc_data_t *sdmmc)
{
sdmmc->read_dma_ch = dma_claim_unused_channel(true);
sdmmc->write_dma_ch = dma_claim_unused_channel(true);
sdmmc->dma_rc = dma_channel_get_default_config(sdmmc->read_dma_ch);
sdmmc->dma_wc = dma_channel_get_default_config(sdmmc->write_dma_ch);
channel_config_set_transfer_data_size(&(sdmmc->dma_rc), DMA_SIZE_8);
channel_config_set_transfer_data_size(&(sdmmc->dma_wc), DMA_SIZE_8);
channel_config_set_read_increment(&(sdmmc->dma_rc), false);
channel_config_set_write_increment(&(sdmmc->dma_rc), true);
channel_config_set_read_increment(&(sdmmc->dma_wc), true);
channel_config_set_write_increment(&(sdmmc->dma_wc), false);
channel_config_set_dreq(&(sdmmc->dma_rc), spi_get_dreq(sdmmc->spiPort, false));
channel_config_set_dreq(&(sdmmc->dma_wc), spi_get_dreq(sdmmc->spiPort, true));
for (int i = 0; i < 512; i++)
dummy_block[i] = 0xFF;
dma_channel_configure(sdmmc->read_dma_ch,
&(sdmmc->dma_rc),
NULL,
&spi_get_hw(sdmmc->spiPort)->dr,
sdmmc->sectSize, false);
dma_channel_configure(sdmmc->write_dma_ch,
&(sdmmc->dma_wc),
&spi_get_hw(sdmmc->spiPort)->dr,
NULL,
sdmmc->sectSize, false);
sdmmc->dmaInit = true;
}
#endif
/* set spi cs low (select)*/
void sdmmc_spi_cs_low(sdmmc_data_t *sdmmc)
{
gpio_put(sdmmc->csPin, 0);
}
/* set spi cs high (deselect)*/
void sdmmc_spi_cs_high(sdmmc_data_t *sdmmc)
{
gpio_put(sdmmc->csPin, 1);
}
/* Initialize SDMMC SPI interface */
static void sdmmc_init_spi(sdmmc_data_t *sdmmc)
{
sdmmc_spi_port_init(sdmmc); // if not initialized, init it
#ifdef __SPI_SDMMC_DMA
if (!sdmmc->dmaInit)
config_spi_dma(sdmmc);
#endif
sleep_ms(10);
}
/* Receive a sector data (512 bytes) */
static void sdmmc_read_spi_dma(
BYTE *buff, /* Pointer to data buffer */
UINT btr, /* Number of bytes to receive (even number) */
sdmmc_data_t *sdmmc)
{
#ifdef __SPI_SDMMC_DMA
dma_channel_set_read_addr(sdmmc->write_dma_ch, dummy_block, false);
dma_channel_set_trans_count(sdmmc->write_dma_ch, btr, false);
dma_channel_set_write_addr(sdmmc->read_dma_ch, buff, false);
dma_channel_set_trans_count(sdmmc->read_dma_ch, btr, false);
dma_start_channel_mask((1u << (sdmmc->read_dma_ch)) | (1u << (sdmmc->write_dma_ch)));
dma_channel_wait_for_finish_blocking(sdmmc->read_dma_ch);
#else
spi_read_blocking(sdmmc->spiPort, 0xFF, buff, btr);
#endif
}
#if FF_FS_READONLY == 0
/* Send a sector data (512 bytes) */
static void sdmmc_write_spi_dma(
const BYTE *buff, /* Pointer to the data */
UINT btx, /* Number of bytes to send (even number) */
sdmmc_data_t *sdmmc)
{
#ifdef __SPI_SDMMC_DMA
dma_channel_set_read_addr(sdmmc->write_dma_ch, buff, false);
dma_channel_set_trans_count(sdmmc->write_dma_ch, btx, false);
dma_channel_start(sdmmc->write_dma_ch);
dma_channel_wait_for_finish_blocking(sdmmc->write_dma_ch);
#else
spi_write_blocking(sdmmc->spiPort, buff, btx);
#endif
}
#endif
/*-----------------------------------------------------------------------*/
/* Wait for card ready */
/*-----------------------------------------------------------------------*/
static int sdmmc_wait_ready(uint timeout, sdmmc_data_t *sdmmc)
{
uint8_t dst;
absolute_time_t timeout_time = make_timeout_time_ms(timeout);
do
{
spi_read_blocking(sdmmc->spiPort, 0xFF, &dst, 1);
} while (dst != 0xFF && 0 < absolute_time_diff_us(get_absolute_time(), timeout_time)); /* Wait for card goes ready or timeout */
return (dst == 0xFF) ? 1 : 0;
}
/*-----------------------------------------------------------------------*/
/* Deselect card and release SPI */
/*-----------------------------------------------------------------------*/
static void sdmmc_deselect(sdmmc_data_t *sdmmc)
{
uint8_t src = 0xFF;
sdmmc_spi_cs_high(sdmmc);
spi_write_blocking(sdmmc->spiPort, &src, 1);
}
/*-----------------------------------------------------------------------*/
/* Select card and wait for ready */
/*-----------------------------------------------------------------------*/
static int sdmmc_select(sdmmc_data_t *sdmmc) /* 1:OK, 0:Timeout */
{
uint8_t src = 0xFF;
sdmmc_spi_cs_low(sdmmc);
spi_write_blocking(sdmmc->spiPort, &src, 1);
if (sdmmc_wait_ready(500, sdmmc))
return 1; /* Wait for card ready */
sdmmc_deselect(sdmmc);
return 0; /* Timeout */
}
/*-----------------------------------------------------------------------*/
/* Receive a data packet from the MMC */
/*-----------------------------------------------------------------------*/
// static
int sdmmc_read_datablock( /* 1:OK, 0:Error */
BYTE *buff, /* Data buffer */
UINT btr, /* Data block length (byte) */
sdmmc_data_t *sdmmc)
{
BYTE token;
absolute_time_t timeout_time = make_timeout_time_ms(200);
do
{ /* Wait for DataStart token in timeout of 200ms */
spi_read_blocking(sdmmc->spiPort, 0xFF, &token, 1);
} while ((token == 0xFF) && 0 < absolute_time_diff_us(get_absolute_time(), timeout_time));
if (token != 0xFE)
return 0; /* Function fails if invalid DataStart token or timeout */
sdmmc_read_spi_dma(buff, btr, sdmmc);
// Discard CRC
spi_read_blocking(sdmmc->spiPort, 0xFF, &token, 1);
spi_read_blocking(sdmmc->spiPort, 0xFF, &token, 1);
return 1; // Function succeeded
}
/*-----------------------------------------------------------------------*/
/* Send a data packet to the MMC */
/*-----------------------------------------------------------------------*/
#if FF_FS_READONLY == 0
// static
int sdmmc_write_datablock( /* 1:OK, 0:Failed */
const BYTE *buff, /* Ponter to 512 byte data to be sent */
BYTE token, /* Token */
sdmmc_data_t *sdmmc)
{
BYTE resp;
if (!sdmmc_wait_ready(500, sdmmc))
return 0; /* Wait for card ready */
// Send token : 0xFE--single block, 0xFC -- multiple block write start, 0xFD -- StopTrans
spi_write_blocking(sdmmc->spiPort, &token, 1);
if (token != 0xFD)
{ /* Send data if token is other than StopTran */
sdmmc_write_spi_dma(buff, 512, sdmmc); /* Data */
token = 0xFF;
spi_write_blocking(sdmmc->spiPort, &token, 1); // Dummy CRC
spi_write_blocking(sdmmc->spiPort, &token, 1);
spi_read_blocking(sdmmc->spiPort, 0xFF, &resp, 1);
// receive response token: 0x05 -- accepted, 0x0B -- CRC error, 0x0C -- Write Error
if ((resp & 0x1F) != 0x05)
return 0; /* Function fails if the data packet was not accepted */
}
return 1;
}
#endif
/*-----------------------------------------------------------------------*/
/* Send a command packet to the MMC */
/*-----------------------------------------------------------------------*/
//static
BYTE sdmmc_send_cmd( /* Return value: R1 resp (bit7==1:Failed to send) */
BYTE cmd, /* Command index */
DWORD arg, /* Argument */
sdmmc_data_t *sdmmc)
{
BYTE n, res;
BYTE tcmd[5];
if (cmd & 0x80)
{ /* Send a CMD55 prior to ACMD<n> */
cmd &= 0x7F;
res = sdmmc_send_cmd(CMD55, 0, sdmmc);
if (res > 1)
return res;
}
/* Select the card and wait for ready except to stop multiple block read */
if (cmd != CMD12)
{
sdmmc_deselect(sdmmc);
if (!sdmmc_select(sdmmc))
return 0xFF;
}
/* Send command packet */
tcmd[0] = 0x40 | cmd; // 0 1 cmd-index(6) --> 01xxxxxx(b)
tcmd[1] = (BYTE)(arg >> 24); // 32 bits argument
tcmd[2] = (BYTE)(arg >> 16);
tcmd[3] = (BYTE)(arg >> 8);
tcmd[4] = (BYTE)arg;
spi_write_blocking(sdmmc->spiPort, tcmd, 5);
n = 0x01; /* Dummy CRC + Stop */
if (cmd == CMD0)
n = 0x95; /* Valid CRC for CMD0(0) */
if (cmd == CMD8)
n = 0x87; /* Valid CRC for CMD8(0x1AA) */
spi_write_blocking(sdmmc->spiPort, &n, 1);
/* Receive command resp */
if (cmd == CMD12)
spi_read_blocking(sdmmc->spiPort, 0xFF, &res, 1); /* Diacard following one byte when CMD12 */
n = 10; /* Wait for response (10 bytes max) */
do
{
spi_read_blocking(sdmmc->spiPort, 0xFF, &res, 1);
} while ((res & 0x80) && --n);
return res; /* Return received response */
}
/*-----------------------------------------------------------------------*/
/* Initialize disk drive */
/*-----------------------------------------------------------------------*/
DSTATUS sdmmc_init(sdmmc_data_t *sdmmc)
{
BYTE n, cmd, ty, src, ocr[4];
sdmmc->Stat = STA_NOINIT;
// low baudrate
spi_set_baudrate(sdmmc->spiPort, SPI_BAUDRATE_LOW);
src = 0xFF;
sdmmc_spi_cs_low(sdmmc);
for (n = 10; n; n--)
spi_write_blocking(sdmmc->spiPort, &src, 1); // Send 80 dummy clocks
sdmmc_spi_cs_high(sdmmc);
ty = 0;
if (sdmmc_send_cmd(CMD0, 0, sdmmc) == 1)
{ /* Put the card SPI/Idle state, R1 bit0=1*/
absolute_time_t timeout_time = make_timeout_time_ms(1000);
if (sdmmc_send_cmd(CMD8, 0x1AA, sdmmc) == 1)
{ /* SDv2? */
spi_read_blocking(sdmmc->spiPort, 0xFF, ocr, 4); // R7(5 bytes): R1 read by sdmmc_send_cmd, Get the other 32 bit return value of R7 resp
if (ocr[2] == 0x01 && ocr[3] == 0xAA)
{ /* Is the card supports vcc of 2.7-3.6V? */
while ((0 < absolute_time_diff_us(get_absolute_time(), timeout_time)) && sdmmc_send_cmd(ACMD41, 1UL << 30, sdmmc))
; /* Wait for end of initialization with ACMD41(HCS) */
if ((0 < absolute_time_diff_us(get_absolute_time(), timeout_time)) && sdmmc_send_cmd(CMD58, 0, sdmmc) == 0)
{ /* Check CCS bit in the OCR */
spi_read_blocking(sdmmc->spiPort, 0xFF, ocr, 4);
ty = (ocr[0] & 0x40) ? CT_SDC2 | CT_BLOCK : CT_SDC2; /* Card id SDv2 */
}
}
}
else
{ /* Not SDv2 card */
if (sdmmc_send_cmd(ACMD41, 0, sdmmc) <= 1)
{ /* SDv1 or MMC? */
ty = CT_SDC1;
cmd = ACMD41; /* SDv1 (ACMD41(0)) */
}
else
{
ty = CT_MMC3;
cmd = CMD1; /* MMCv3 (CMD1(0)) */
}
while ((0 < absolute_time_diff_us(get_absolute_time(), timeout_time)) && sdmmc_send_cmd(cmd, 0, sdmmc))
; /* Wait for end of initialization */
if (!(0 < absolute_time_diff_us(get_absolute_time(), timeout_time)) || sdmmc_send_cmd(CMD16, 512, sdmmc) != 0) /* Set block length: 512 */
ty = 0;
}
}
sdmmc->cardType = ty; /* Card type */
sdmmc_deselect(sdmmc);
if (ty)
{ /* OK */
// high baudrate
printf("\nThe actual baudrate(SD/MMC):%d\n",spi_set_baudrate(sdmmc->spiPort, SPI_BAUDRATE_HIGH)); // speed high
sdmmc->sectSize = 512;
sdmmc->Stat &= ~STA_NOINIT; /* Clear STA_NOINIT flag */
}
else
{ /* Failed */
sdmmc->Stat = STA_NOINIT;
}
return sdmmc->Stat;
}
/* sdmmc_disk_initizlize, sdmmc_disk_read, sdmmc_disk_write, sdmmc_disk_status, sdmmc_disk_ioctl*/
/* sdmmc_disk_initialize*/
DSTATUS sdmmc_disk_initialize(sdmmc_data_t *sdmmc)
{
if (!sdmmc->spiInit)
sdmmc_init_spi(sdmmc); /* Initialize SPI */
DSTATUS stat = sdmmc_init(sdmmc);
return stat;
}
/* sdmmc disk status*/
DSTATUS sdmmc_disk_status(sdmmc_data_t *sdmmc)
{
return sdmmc->Stat;
}
/* sdmmc disk read*/
DSTATUS sdmmc_disk_read(
BYTE *buff, /* Pointer to the data buffer to store read data */
LBA_t sector, /* Start sector number (LBA) */
UINT count, /* Number of sectors to read (1..128) */
sdmmc_data_t *sdmmc)
{
DWORD sect = (DWORD)(sector);
if (!count)
return RES_PARERR; /* Check parameter */
if (sdmmc->Stat & STA_NOINIT)
return RES_NOTRDY; /* Check if drive is ready */
if (!(sdmmc->cardType & CT_BLOCK))
sect *= 512; /* LBA ot BA conversion (byte addressing cards) */
if (count == 1)
{ /* Single sector read */
if ((sdmmc_send_cmd(CMD17, sect, sdmmc) == 0) /* READ_SINGLE_BLOCK */
&& sdmmc_read_datablock(buff, 512, sdmmc))
{
count = 0;
}
}
else
{ /* Multiple sector read */
if (sdmmc_send_cmd(CMD18, sect, sdmmc) == 0)
{ /* READ_MULTIPLE_BLOCK */
do
{
if (!sdmmc_read_datablock(buff, 512, sdmmc))
break;
buff += 512;
} while (--count);
sdmmc_send_cmd(CMD12, 0, sdmmc); /* STOP_TRANSMISSION */
}
}
sdmmc_deselect(sdmmc); // sdmmc_select() is called in function sdmmc_send_cmd()
return count ? RES_ERROR : RES_OK; /* Return result */
}
DSTATUS sdmmc_disk_write(
const BYTE *buff, /* Ponter to the data to write */
LBA_t sector, /* Start sector number (LBA) */
UINT count, /* Number of sectors to write (1..128) */
sdmmc_data_t *sdmmc)
{
DWORD sect = (DWORD)sector;
if (!count)
return RES_PARERR; /* Check parameter */
if (sdmmc->Stat & STA_NOINIT)
return RES_NOTRDY; /* Check drive status */
if (sdmmc->Stat & STA_PROTECT)
return RES_WRPRT; /* Check write protect */
if (!(sdmmc->cardType & CT_BLOCK))
sect *= 512; /* LBA ==> BA conversion (byte addressing cards) */
if (count == 1)
{ /* Single sector write */
if ((sdmmc_send_cmd(CMD24, sect, sdmmc) == 0) /* WRITE_BLOCK */
&& sdmmc_write_datablock(buff, 0xFE, sdmmc))
{
count = 0;
}
}
else
{ /* Multiple sector write */
if (sdmmc->cardType & CT_SDC)
sdmmc_send_cmd(ACMD23, count, sdmmc); /* Predefine number of sectors */
if (sdmmc_send_cmd(CMD25, sect, sdmmc) == 0)
{ /* WRITE_MULTIPLE_BLOCK */
do
{
if (!sdmmc_write_datablock(buff, 0xFC, sdmmc))
break;
buff += 512;
} while (--count);
if (!sdmmc_write_datablock(0, 0xFD, sdmmc))
count = 1; /* STOP_TRAN token */
}
}
sdmmc_deselect(sdmmc); // sdmmc_select() is called in function sdmmc_send_cmd
return count ? RES_ERROR : RES_OK; /* Return result */
}
/* sdmmc disk ioctl*/
DSTATUS sdmmc_disk_ioctl(
BYTE cmd, /* Control command code */
void *buff, /* Pointer to the conrtol data */
sdmmc_data_t *sdmmc)
{
DRESULT res;
BYTE n, csd[16];
DWORD st, ed, csize;
LBA_t *dp;
BYTE src = 0xFF;
if (sdmmc->Stat & STA_NOINIT)
return RES_NOTRDY; /* Check if drive is ready */
res = RES_ERROR;
switch (cmd)
{
case CTRL_SYNC: /* Wait for end of internal write process of the drive */
if (sdmmc_select(sdmmc))
res = RES_OK;
break;
case GET_SECTOR_COUNT: /* Get drive capacity in unit of sector (DWORD) */
if ((sdmmc_send_cmd(CMD9, 0, sdmmc) == 0) && sdmmc_read_datablock(csd, 16, sdmmc))
{
if ((csd[0] >> 6) == 1)
{ /* SDC CSD ver 2 */
csize = csd[9] + ((WORD)csd[8] << 8) + ((DWORD)(csd[7] & 63) << 16) + 1;
*(LBA_t *)buff = csize << 10;
}
else
{ /* SDC CSD ver 1 or MMC */
n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
*(LBA_t *)buff = csize << (n - 9);
}
res = RES_OK;
}
break;
case GET_SECTOR_SIZE: // FF_MAX_SS != FX_MIN_SS
//*(WORD*)buff=512; // SDHC, SDXC sector size is 512
*(WORD *)buff = sdmmc->sectSize;
res = RES_OK;
break;
case GET_BLOCK_SIZE: /* Get erase block size in unit of sector (DWORD) */
if (sdmmc->cardType & CT_SDC2)
{ /* SDC ver 2+ */
if (sdmmc_send_cmd(ACMD13, 0, sdmmc) == 0)
{ /* Read SD status */
spi_write_blocking(sdmmc->spiPort, &src, 1);
if (sdmmc_read_datablock(csd, 16, sdmmc))
{ /* Read partial block */
for (n = 64 - 16; n; n--)
spi_write_blocking(sdmmc->spiPort, &src, 1); // xchg_spi(0xFF); /* Purge trailing data */
*(DWORD *)buff = 16UL << (csd[10] >> 4);
res = RES_OK;
}
}
}
else
{ /* SDC ver 1 or MMC */
if ((sdmmc_send_cmd(CMD9, 0, sdmmc) == 0) && sdmmc_read_datablock(csd, 16, sdmmc))
{ /* Read CSD */
if (sdmmc->cardType & CT_SDC1)
{ /* SDC ver 1.XX */
*(DWORD *)buff = (((csd[10] & 63) << 1) + ((WORD)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1);
}
else
{ /* MMC */
*(DWORD *)buff = ((WORD)((csd[10] & 124) >> 2) + 1) * (((csd[11] & 3) << 3) + ((csd[11] & 224) >> 5) + 1);
}
res = RES_OK;
}
}
break;
case CTRL_TRIM: /* Erase a block of sectors (used when _USE_ERASE == 1) */
if (!(sdmmc->cardType & CT_SDC))
break; /* Check if the card is SDC */
if (sdmmc_disk_ioctl(MMC_GET_CSD, csd, sdmmc))
break; /* Get CSD */
if (!(csd[10] & 0x40))
break; /* Check if ERASE_BLK_EN = 1 */
dp = buff;
st = (DWORD)dp[0];
ed = (DWORD)dp[1]; /* Load sector block */
if (!(sdmmc->cardType & CT_BLOCK))
{
st *= 512;
ed *= 512;
}
if (sdmmc_send_cmd(CMD32, st, sdmmc) == 0 && sdmmc_send_cmd(CMD33, ed, sdmmc) == 0 && sdmmc_send_cmd(CMD38, 0, sdmmc) == 0 && sdmmc_wait_ready(30000, sdmmc))
{ /* Erase sector block */
res = RES_OK; /* FatFs does not check result of this command */
}
break;
/* Following commands are never used by FatFs module */
case MMC_GET_TYPE: /* Get MMC/SDC type (BYTE) */
*(BYTE *)buff = sdmmc->cardType;
res = RES_OK;
break;
case MMC_GET_CSD: /* Read CSD (16 bytes) */
if (sdmmc_send_cmd(CMD9, 0, sdmmc) == 0 && sdmmc_read_datablock((BYTE *)buff, 16, sdmmc))
{ /* READ_CSD */
res = RES_OK;
}
break;
case MMC_GET_CID: /* Read CID (16 bytes) */
if (sdmmc_send_cmd(CMD10, 0, sdmmc) == 0 && sdmmc_read_datablock((BYTE *)buff, 16, sdmmc))
{ /* READ_CID */
res = RES_OK;
}
break;
case MMC_GET_OCR: /* Read OCR (4 bytes) */
if (sdmmc_send_cmd(CMD58, 0, sdmmc) == 0)
{ /* READ_OCR */
for (n = 0; n < 4; n++)
*(((BYTE *)buff) + n) = spi_write_blocking(sdmmc->spiPort, &src, 1); // xchg_spi(0xFF);
res = RES_OK;
}
break;
case MMC_GET_SDSTAT: /* Read SD status (64 bytes) */
if (sdmmc_send_cmd(ACMD13, 0, sdmmc) == 0)
{ /* SD_STATUS */
spi_write_blocking(sdmmc->spiPort, &src, 1);
if (sdmmc_read_datablock((BYTE *)buff, 64, sdmmc))
res = RES_OK;
}
break;
default:
res = RES_PARERR;
}
sdmmc_deselect(sdmmc); // sdmmc_select() is called in function sdmmc_send_cmd()
return res;
}
- spi_sdmmc.h
/*
This library is derived from ChaN's FatFs - Generic FAT Filesystem Module.
*/
#ifndef SPI_SDMMC_H
#define SPI_SDMMC_H
#include "hardware/spi.h"
#include "hardware/dma.h"
#include "ff.h"
#include "diskio.h"
//#define __SPI_SDMMC_DMA
/* SDMMC SPI pins*/
#define SDMMC_SPI_PORT spi1
#define SDMMC_PIN_MISO 12
#define SDMMC_PIN_CS 13
#define SDMMC_PIN_SCK 14
#define SDMMC_PIN_MOSI 15
/* ====================== */
/* MMC/SD command */
#define CMD0 (0) /* GO_IDLE_STATE */
#define CMD1 (1) /* SEND_OP_COND (MMC) */
#define ACMD41 (0x80 + 41) /* SEND_OP_COND (SDC) */
#define CMD8 (8) /* SEND_IF_COND */
#define CMD9 (9) /* SEND_CSD */
#define CMD10 (10) /* SEND_CID */
#define CMD12 (12) /* STOP_TRANSMISSION */
#define ACMD13 (0x80 + 13) /* SD_STATUS (SDC) */
#define CMD16 (16) /* SET_BLOCKLEN */
#define CMD17 (17) /* READ_SINGLE_BLOCK */
#define CMD18 (18) /* READ_MULTIPLE_BLOCK */
#define CMD23 (23) /* SET_BLOCK_COUNT (MMC) */
#define ACMD23 (0x80 + 23) /* SET_WR_BLK_ERASE_COUNT (SDC) */
#define CMD24 (24) /* WRITE_BLOCK */
#define CMD25 (25) /* WRITE_MULTIPLE_BLOCK */
#define CMD32 (32) /* ERASE_ER_BLK_START */
#define CMD33 (33) /* ERASE_ER_BLK_END */
#define CMD38 (38) /* ERASE */
#define CMD55 (55) /* APP_CMD */
#define CMD58 (58) /* READ_OCR */
typedef struct {
spi_inst_t *spiPort;
bool spiInit;
uint csPin;
BYTE cardType;
uint16_t sectSize;
#ifdef __SPI_SDMMC_DMA
uint read_dma_ch;
uint write_dma_ch;
dma_channel_config dma_rc;
dma_channel_config dma_wc;
bool dmaInit;
#endif
DRESULT Stat;
}sdmmc_data_t;
//BYTE sdmmc_init(sdmmc_data_t *sdmmc);
DSTATUS sdmmc_disk_initialize(sdmmc_data_t *sdmmc);
DSTATUS sdmmc_disk_status(sdmmc_data_t *sdmmc);
DSTATUS sdmmc_disk_read(BYTE *buff, LBA_t sector, UINT count, sdmmc_data_t *sdmmc);
DSTATUS sdmmc_disk_write(const BYTE *buff, LBA_t sector, UINT count, sdmmc_data_t *sdmmc);
DSTATUS sdmmc_disk_ioctl ( BYTE cmd, void *buff, sdmmc_data_t *sdmmc);
//static
int sdmmc_read_datablock (BYTE *buff, UINT btr, sdmmc_data_t *sdmmc);
//static
int sdmmc_write_datablock (const BYTE *buff, BYTE token, sdmmc_data_t *sdmmc);
//static
BYTE sdmmc_send_cmd(BYTE cmd, DWORD arg, sdmmc_data_t *sdmmc);
/* MMC card type flags (MMC_GET_TYPE) */
#define CT_MMC3 0x01 /* MMC ver 3 */
#define CT_MMC4 0x02 /* MMC ver 4+ */
#define CT_MMC 0x03 /* MMC */
#define CT_SDC1 0x02 /* SDC ver 1 */
#define CT_SDC2 0x04 /* SDC ver 2+ */
#define CT_SDC 0x0C /* SDC */
#define CT_BLOCK 0x10 /* Block addressing */
#endif - 主程式碼:
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "hardware/dma.h"
#include "stdlib.h"
#include "stdio.h"
#include "ff.h"
#include "diskio.h"
#include "string.h"
#include "spi_sdmmc.h"
#include "inttypes.h"
extern sdmmc_data_t *pSDMMC;
void test_sdmmc_raw_rw(uint8_t *buff) {
if (pSDMMC == NULL){
pSDMMC = (sdmmc_data_t*)malloc(sizeof(sdmmc_data_t));
pSDMMC->csPin = SDMMC_PIN_CS;
pSDMMC->spiPort = SDMMC_SPI_PORT;
pSDMMC->spiInit=false;
pSDMMC->sectSize=512;
#ifdef __SPI_SDMMC_DMA
pSDMMC->dmaInit=false;
#endif
printf("sdmmc_init:%0x\n", sdmmc_disk_initialize(pSDMMC));
}
printf("-------------------------------------------------------\n");
printf("Being testing...\n");
// WRITE SINGLE B:OCL
printf("\n===============\n");
printf("Directly write a single block of data to the SDMMC card, the address is 51200. \n");
#ifdef __SPI_SDMMC_DMA
printf("with DMA: ");
#endif
printf("data is written:\n");
printf("\n--------------");
for (int i = 0; i < 512; i++){
if (i % 80==0) printf("\n");
printf("%c", buff[i]);
}
if ((sdmmc_send_cmd(CMD24, 51200, pSDMMC) == 0)) {
sdmmc_write_datablock(buff, 0xFE, pSDMMC);
}
printf("\n===============\n");
memset(buff, 0xFF, 512);
printf("clear buffer\n");
// READ_SINGLE_BLOCK
printf("\n===============\n");
printf("Directly read a single block of data from the SDMMC card at address is 51200\n");
#ifdef __SPI_SDMMC_DMA
printf("with DMA: ");
#endif
printf("data is read:\n");
printf("\n--------------");
if ((sdmmc_send_cmd(CMD17, 51200, pSDMMC) == 0)) {
sdmmc_read_datablock(buff, 512, pSDMMC);
for (int i = 0; i < 512; i++){
if (i % 80==0) printf("\n");
printf("%c", buff[i]);
}
}
printf("\n");
}
void test_sdmmc_flash_rw_fatfs(uint8_t *buff) {
FRESULT res;
FATFS fs0, fs1;
FIL fil0, fil1;
uint br;
uint loop_count=100;
absolute_time_t time1, time2;
printf("-------------------------------------------------------\n");
printf("Begin testing(read data from sdmmc and write to flash memory device)...\n");
res = f_mount(&fs0, SDMMC_PATH, 1);
if (res != FR_OK) {
printf(" mount error\n"); return;
}
printf("mount SDMMC_PATH ok\n");
res = f_mount(&fs1, W25Q_PATH, 1);
if (res != FR_OK) {
printf(" mount error\n"); return;
}
printf("mount W25Q_PATH ok\n");
printf("=================\n");
#ifdef __SPI_SDMMC_DMA
printf("With DMA: ");
#else
printf("Without DMA: ");
#endif
printf(" write 0.8MB data to SD/MMC card.\n");
time1=get_absolute_time();
res= f_open(&fil0, SDMMC_PATH"/tfile1.txt", FA_CREATE_ALWAYS|FA_WRITE);
if (res != FR_OK) {
printf("open write error\n");
return;
}
for (int i = 0; i < loop_count; i++)
res = f_write(&fil0, buff, 8192, &br);
f_close(&fil0);
time2=get_absolute_time();
printf("\nwrite total Time:%"PRId64"(us)\n\n", absolute_time_diff_us(time1,time2));
printf("=================\n");
#ifdef __SPI_SDMMC_DMA
printf("With DMA: ");
#else
printf("Without DMA: ");
#endif
printf(" read 0.8MB data from SD/MMC card and write to Flash memory device.\n");
time2=get_absolute_time();
res = f_open(&fil1, W25Q_PATH"/tfile1.txt", FA_CREATE_ALWAYS|FA_WRITE);
res = f_open(&fil0, SDMMC_PATH"/tfile1.txt",FA_READ );
if (res != FR_OK) {
printf("error\n");
return;
}
for (int i = 0; i < loop_count; i++)
f_read(&fil0, buff, 8192, &br);
f_write(&fil1, buff, 8192, &br);
f_close(&fil0);
f_close(&fil1);
time1=get_absolute_time();
printf("\ntotal Time:%"PRId64"(us)\n\n", absolute_time_diff_us(time2,time1));
f_unmount(SDMMC_PATH);
f_unmount(W25Q_PATH);
}
void test_sdmmc_rw_with_fatfs(uint8_t *buff) {
FRESULT res;
FATFS fs0;
FIL fil0;
uint br;
uint loop_count=4096;
res = f_mount(&fs0, SDMMC_PATH, 1);
if (res != FR_OK) {
printf(" mount error\n"); return;
}
printf("-------------------------------------------------------\n");
printf("Begin testing(SD/MMC card read/write with filesystem support)...\n");
printf("mount SDMMC_PATH ok\n");
absolute_time_t time1, time2;
printf("\n==================\n");
#ifdef __SPI_SDMMC_DMA
printf("With DMA: ");
#else
printf("Without DMA: ");
#endif
printf("Write 32MB data to sd/mmc card.\n");
time1=get_absolute_time();
res= f_open(&fil0, SDMMC_PATH"/testfile.txt", FA_CREATE_ALWAYS|FA_WRITE);
if (res != FR_OK) {
printf("open write error\n");
return;
}
for (int i = 0; i < loop_count; i++) {
if (i%10==0) {
printf(".");
gpio_put(PICO_DEFAULT_LED_PIN, !gpio_get(PICO_DEFAULT_LED_PIN));
}
res = f_write(&fil0, buff, 8192, &br);
}
f_close(&fil0);
time2=get_absolute_time();
printf("\nwrite total Time:%"PRId64"(us)\n", absolute_time_diff_us(time1,time2));
printf("\n==================\n");
#ifdef __SPI_SDMMC_DMA
printf("With DMA: ");
#else
printf("Without DMA: ");
#endif
printf("Read 32MB data from sd/mmc card.\n");
res = f_open(&fil0, SDMMC_PATH"/testfile.txt",FA_READ );
if (res != FR_OK) {
printf("error\n");
return;
}
for (int i = 0; i < loop_count; i++) {
if (i%10==0) {
printf(".");
gpio_put(PICO_DEFAULT_LED_PIN, !gpio_get(PICO_DEFAULT_LED_PIN));
}
f_read(&fil0, buff, 8192, &br);
}
f_close(&fil0);
time1=get_absolute_time();
printf("\nread total Time:%"PRId64"(us)\n\n", absolute_time_diff_us(time2,time1));
f_unmount(SDMMC_PATH);
}
void test_flash_rw_with_fatfs(uint8_t *buff) {
FRESULT res;
FATFS fs0;
FIL fil0;
uint br;
uint loop_count=256;
res = f_mount(&fs0, W25Q_PATH, 1);
if (res != FR_OK) {
printf(" mount error\n"); return;
}
printf("-------------------------------------------------------\n");
printf("Begin testing(Flash memory device read/write with filesystem support)...\n");
printf("mount W25Q_PATH ok\n");
absolute_time_t time1, time2;
printf("\n==================\n");
printf("Write 2MB data to Flash Memory device.\n");
time1=get_absolute_time();
res= f_open(&fil0, W25Q_PATH"/tfile2.txt", FA_CREATE_ALWAYS|FA_WRITE);
if (res != FR_OK) {
printf("open write error\n");
return;
}
for (int i = 0; i < loop_count; i++) {
printf(".");
gpio_put(PICO_DEFAULT_LED_PIN, !gpio_get(PICO_DEFAULT_LED_PIN));
res = f_write(&fil0, buff, 8192, &br);
}
f_close(&fil0);
time2=get_absolute_time();
printf("\nwrite total Time:%"PRId64"(us)\n", absolute_time_diff_us(time1,time2));
printf("\n==================\n");
printf("Read 2MB data from Flash Memory device.\n");
res = f_open(&fil0, W25Q_PATH"/tfile2.txt",FA_READ );
if (res != FR_OK) {
printf("error\n");
return;
}
for (int i = 0; i < loop_count; i++) {
printf(".");
gpio_put(PICO_DEFAULT_LED_PIN, !gpio_get(PICO_DEFAULT_LED_PIN));
f_read(&fil0, buff, 8192, &br);
}
f_close(&fil0);
time1=get_absolute_time();
printf("\nread total Time:%"PRId64"(us)\n\n", absolute_time_diff_us(time2,time1));
f_unmount(W25Q_PATH);
}
int main()
{
stdio_init_all();
uint8_t buff[8192];
uint8_t r;
for (int i=0; i< 8192; i++) {
r = rand()%126;
if (r < 32) r+=32;
buff[i] = r;
}
gpio_init(PICO_DEFAULT_LED_PIN);
gpio_set_dir(PICO_DEFAULT_LED_PIN, true);
//test_sdmmc_raw_rw(buff);
test_sdmmc_rw_with_fatfs(buff);
test_sdmmc_flash_rw_fatfs(buff);
test_flash_rw_with_fatfs(buff);
gpio_put(PICO_DEFAULT_LED_PIN, 0);
//puts("Hello, world!");
return 0;
}
沒有留言:
張貼留言