[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:
target_link_libraries(pico_storage
tinyusb_host
tinyusb_board
)
- 呼叫:
board_init();
tuh_init(BOARD_TUH_RHPORT); - 在main loop 程式中呼叫
tuh_task():
處理USB queue message
基本結構如下
#include "tusb.h"
void main(void) {
board_init();
tuh_init(BOARD_TUH_RHPORT);
while(1) {
tuh_task():
}
}
- 至少重寫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);
二、USB Mass Storage driver說明:
將上述TinyUSB與FatFS檔案系統整合。
在FatFs有五個主要程式:
DSTATUS disk_initialize (BYTE drv);
DSTATUS disk_status (BYTE drv);
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) */
);
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) */
);
DRESULT disk_ioctl (
BYTE drv, /* Physical drive number (0) */
BYTE cmd, /* Control command code */
void *buff /* Pointer to the conrtol data */
);
DSTATUS disk_status (BYTE drv);
DSTATUS disk_status (BYTE drv);
在
FRESULT f_mount(FATFS *fs, const TCHAR *path, BYTE opt);
會呼叫
disk_initialize();
因此在此函式中加入
if (!tuh_inited()) {
board_init();
tuh_init(BOARD_TUH_RHPORT);
}
使用時不需要在主程式呼叫borad_init() and tuh_init();
在init USB driver時主動呼叫callback function:
void tuh_msc_mount_cb(uint8_t dev_addr)
void tuh_msc_mount_cb(uint8_t dev_addr)
在此function 中呼叫
tuh_msc_inquiry(dev_addr, lun, &inquiry_resp, inquiry_complete_cb, 0);
取得device sector count與sector size以便在disk_ioctl中傳回此參數。
在disk_read()中呼叫:
tuh_msc_read10(pusb_msc->dev_addr, pusb_msc->lun, buff, sector, (uint16_t) count, disk_io_complete, 0);
在disk_write呼叫
tuh_msc_write10(pusb_msc->dev_addr, pusb_msc->lun, buff, sector, (uint16_t) count, disk_io_complete, 0);
其他詳細程式內容附於文章末尾。
三、成果影片
四、程式碼
#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 )
沒有留言:
張貼留言