[Raspberry Pi Pico (c-sdk)] Storage: Ep 3. Using SD/MMC Devices and Flash Devices Together ,再加入USB Mass Storage。
- 分別對USB Mass Storage, SD/MMC and W25Q Flash進行100K data的write/read所需要的時間比較。
- 同時開啟三個檔案分別在USB MSC, SD 與W25Q Flash上,進行讀寫動作。
一、USB MSC host:
- 呼叫:
board_init();
tuh_init(BOARD_TUH_RHPORT); - 在main loop 程式中呼叫
tuh_task():
處理USB queue message
- 至少重寫4個
TU_ATTR_WEAK __attribute__ ((weak))
的callback function:
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len);
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len);
void tuh_msc_mount_cb(uint8_t dev_addr);
void tuh_msc_umount_cb(uint8_t dev_addr);
DSTATUS disk_status (BYTE drv);
void tuh_msc_mount_cb(uint8_t dev_addr)
三、成果影片
#include "pico/stdlib.h"
#include "usb_msc.h"
#include "bsp/board.h"
static usb_msc_t *_pUSBMSC;
static scsi_inquiry_resp_t inquiry_resp;
bool usb_msc_mounted() {
return _pUSBMSC->usb_mounted;
}
bool inquiry_complete_cb(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data)
{
msc_cbw_t const* cbw = cb_data->cbw;
msc_csw_t const* csw = cb_data->csw;
if (csw->status != 0)
{
printf("Inquiry failed\r\n");
return false;
}
// Print out Vendor ID, Product ID and Rev
printf("%.8s %.16s rev %.4s\r\n", inquiry_resp.vendor_id, inquiry_resp.product_id, inquiry_resp.product_rev);
// Get capacity of device
_pUSBMSC->block_count = tuh_msc_get_block_count(dev_addr, cbw->lun);
_pUSBMSC->block_size = tuh_msc_get_block_size(dev_addr, cbw->lun);
printf("Disk Size: %lu MB\r\n", _pUSBMSC->block_count / ((1024*1024)/_pUSBMSC->block_size));
_pUSBMSC->disk_busy=false;
_pUSBMSC->usb_mounted=true;
return true;
}
//------------- IMPLEMENTATION -------------//
void tuh_msc_mount_cb(uint8_t dev_addr)
{
printf("A MassStorage device is mounted\r\n");
_pUSBMSC->dev_addr = dev_addr;
_pUSBMSC->lun = 0;
tuh_msc_inquiry(_pUSBMSC->dev_addr, _pUSBMSC->lun, &inquiry_resp, inquiry_complete_cb, 0);
}
void tuh_msc_umount_cb(uint8_t dev_addr)
{
printf("A MassStorage device is unmounted\r\n");
_pUSBMSC->usb_mounted=false;
f_unmount(USB_MSC_PATH);
}
/////////////
DRESULT usb_msc_initialize(usb_msc_t* pusb_msc) {
//printf("usb_msc disk initialize\r\n");
if (!tuh_inited()) {
board_init();
tuh_init(BOARD_TUH_RHPORT);
_pUSBMSC=pusb_msc;
_pUSBMSC->disk_busy = true;
_pUSBMSC->usb_mounted = false;
absolute_time_t t1=get_absolute_time();
absolute_time_t t2;
while (1) {
tuh_task();
if (_pUSBMSC->usb_mounted) {
_pUSBMSC->Stat &= ~STA_NOINIT;
break;
}
t2 = get_absolute_time();
if (absolute_time_diff_us(t1, t2) > 10000000) {
_pUSBMSC->Stat = STA_NOINIT;
break;
}
}
}
_pUSBMSC->disk_busy = false;
return _pUSBMSC->Stat;
}
void wait_for_disk_io(usb_msc_t *pusb_msc)
{
while(pusb_msc->disk_busy)
{
tuh_task();
led_blinking();
}
led_blinking_off();
}
bool disk_io_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data)
{
(void) dev_addr; (void) cb_data;
_pUSBMSC->disk_busy = false; //////
_pUSBMSC->Stat = RES_OK;
return true;
}
DRESULT usb_msc_ioctl(
BYTE cmd, /* Control code */
void *buff, /* Buffer to send/receive control data */
usb_msc_t* pusb_msc
)
{
DRESULT res = RES_ERROR;
switch ( cmd )
{
case CTRL_SYNC:
// nothing to do since we do blocking
return RES_OK;
case GET_SECTOR_COUNT:
//*((DWORD*) buff) = (DWORD) tuh_msc_get_block_count(pusb_msc->dev_addr, pusb_msc->lun);
*((DWORD*) buff) = pusb_msc->block_count;
return RES_OK;
case GET_SECTOR_SIZE:
//*((WORD*) buff) = (WORD) tuh_msc_get_block_size(pusb_msc->dev_addr, pusb_msc->lun);
*((WORD*) buff) = (WORD) pusb_msc->block_size;
return RES_OK;
case GET_BLOCK_SIZE:
*((DWORD*) buff) = 1; // erase block size in units of sector size
return RES_OK;
default:
return RES_PARERR;
}
}
DRESULT usb_msc_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) */
usb_msc_t *pusb_msc
)
{
pusb_msc->disk_busy = true;
tuh_msc_write10(pusb_msc->dev_addr, pusb_msc->lun, buff, sector, (uint16_t) count, disk_io_complete, 0);
wait_for_disk_io(pusb_msc);
return pusb_msc->Stat;
}
DRESULT usb_msc_disk_read(
BYTE *buff, /* Ponter to the data to write */
LBA_t sector, /* Start sector number (LBA) */
UINT count, /* Number of sectors to write (1..128) */
usb_msc_t *pusb_msc
)
{
pusb_msc->disk_busy = true;
tuh_msc_read10(pusb_msc->dev_addr, pusb_msc->lun, buff, sector, (uint16_t) count, disk_io_complete, 0);
wait_for_disk_io(pusb_msc);
return pusb_msc->Stat;
}
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
{
}
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
{
}
- usb_msc.h
#ifndef _USB_MSC_H_
#define _USB_MSC_H_
#include "tusb.h"
#include "ff.h"
#include "diskio.h"
#include "pico_storage.h"
typedef struct {
uint8_t dev_addr;
volatile bool disk_busy;
uint8_t lun;
uint32_t block_count;
uint32_t block_size;
bool usb_mounted;
DRESULT Stat;
} usb_msc_t;
bool usb_msc_mounted();
DRESULT usb_msc_initialize(usb_msc_t* pusb_msc);
void wait_for_disk_io(usb_msc_t *pusb_msc);
bool disk_io_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data);
DRESULT usb_msc_ioctl(BYTE cmd, void *buff, usb_msc_t* pusb_msc);
DRESULT usb_msc_disk_write(const BYTE *buff, LBA_t sector, UINT count, usb_msc_t *pusb_msc);
DRESULT usb_msc_disk_read(BYTE *buff, LBA_t sector, UINT count, usb_msc_t *pusb_msc);
#endif
- glue.c
#include "stdio.h"
#include "stdlib.h"
#include "ff.h"
#include "diskio.h"
#include "spi_sdmmc.h"
#include "W25Q.h"
#include "usb_msc.h"
#include "hardware/rtc.h"
#include "inttypes.h"
#include "hardware/gpio.h"
#define SDMMC_DRV_0 0
#define W25Q_DRV_1 1
#define USB_MSC_DRV_2 2
sdmmc_data_t *pSDMMC=NULL;
w25q_data_t *pW25Q = NULL;
usb_msc_t *pUSB_MSC=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;
case USB_MSC_DRV_2:
if (pUSB_MSC == NULL) {
pUSB_MSC = (usb_msc_t*)malloc(sizeof(usb_msc_t));
}
stat = usb_msc_initialize(pUSB_MSC);
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;
case USB_MSC_DRV_2:
//uint8_t dev_addr = pdrv + 1;
return tuh_msc_mounted(pUSB_MSC->dev_addr) ? 0 : STA_NODISK;
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;
case USB_MSC_DRV_2:
stat = usb_msc_disk_read(buff, sector, count, pUSB_MSC);
return 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;
case USB_MSC_DRV_2:
stat = usb_msc_disk_write(buff, sector, count, pUSB_MSC);
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;
case USB_MSC_DRV_2:
stat = usb_msc_ioctl(cmd, buff, pUSB_MSC);
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;
}
void led_blinking(void)
{
static absolute_time_t t1;
static bool state=false;
// Blink every interval ms
if ( absolute_time_diff_us(t1, get_absolute_time()) < 100000) return; // not enough time
t1 = get_absolute_time();
gpio_put(LED_BLINKING_PIN, state);
state = !state;
}
void led_blinking_off(void) {
gpio_put(LED_BLINKING_PIN, false);
}
- pic_storage.h
#ifndef _PICO_STORAGE_H_
#define _PICO_STORAGE_H_
/* used by my project */
#define SDMMC_PATH "0:"
#define W25Q_PATH "1:"
#define USB_MSC_PATH "2:"
#define SPI_BAUDRATE_LOW (1000*1000)
#define SPI_BAUDRATE_HIGH (50*1000*1000)
/* ================================= */
#define LED_BLINKING_PIN 25
void led_blinking(void);
void led_blinking_off(void);
/* ================================= */
#endif
- CMakeLists.txt
add_library(pico_storage_drv INTERFACE)
target_sources(pico_storage_drv INTERFACE
${CMAKE_CURRENT_LIST_DIR}/glue.c
${CMAKE_CURRENT_LIST_DIR}/FatFs/ff.c
${CMAKE_CURRENT_LIST_DIR}/FatFs/ffunicode.c
${CMAKE_CURRENT_LIST_DIR}/FatFs/ffsystem.c
${CMAKE_CURRENT_LIST_DIR}/sdmmc/spi_sdmmc.c
${CMAKE_CURRENT_LIST_DIR}/flash/W25Q.c
${CMAKE_CURRENT_LIST_DIR}/usb_msc/usb_msc.c
)
target_include_directories(pico_storage_drv INTERFACE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/FatFs
${CMAKE_CURRENT_LIST_DIR}/flash
${CMAKE_CURRENT_LIST_DIR}/sdmmc
${CMAKE_CURRENT_LIST_DIR}/usb_msc
)
target_link_libraries(pico_storage_drv INTERFACE
hardware_spi
hardware_dma
hardware_rtc
pico_stdlib
tinyusb_board
tinyusb_host
)