本文章介紹Raspberry Pi Pico (RP2040)使用TinyUSB函式庫,建制一個含有兩個LUN(SD and W25Q flash)的USB Mass Storage device(USB Stick)。
有關USB界面與SCSI指令部份由TinyUSB函式庫處理,SD與W25Q Flash 的驅動程式則是修改自前一篇文章的程式碼。
一、TinyUSB
使用usb_device_dual範例程式。
- usb_descriptiors.c: 為根據USB specification所定義,因此直接引用,未修改。
- msc_disk_dual.c: 保留所有callback架構,修改後的檔名msc_device_disk.c,有關
tud_msc_capacity_cb(),則呼叫SD與W25Q driver取得sector count and secotr size。
tud_msc_read10_cb與tud_msc_write10_cb則個別呼叫SD與W25Q driver的sdmmc_read_sector(), sdmmc_write_sector(), w25q_read_serctor() and w25q_write_sector()。 - mian.c:呼叫4個function即可。
storage_driver_init();
board_init();
tud_init(BOARD_TUD_RHPORT);
while (1)
{
tud_task(); // tinyusb device task
}
詳細程式碼附於文末,
- 實際在Debian(Linux), Windows and FreeBSD系統下測試I/O效能、磁碟分割與檔案操作,請參閱下列影片。
二、成果展示
Note:
- 若只使用SD card,則在 msc_device_disk.c 將
// Invoked to determine max LUN uint8_t tud_msc_get_maxlun_cb(void) { return 2; // LUN 0: SDMMC, LUN 1: W25Q Flash }
- 在storage_driver.h檔案中定義SPI速度。
#define SPI_BAUDRATE_LOW (1000*1000) #define SPI_BAUDRATE_HIGH (40*1000*1000)
SPI_BAUDRATE_HIGH降低,例如:
#define SPI_BAUDRATE_HIGH (10*1000*1000)
三、程式碼
- msc_device_disk.c
/* this file was modified from tinyUSB example: msc_disk_dual.c*/ /* * The MIT License (MIT) * * Copyright (c) 2019 Ha Thach (tinyusb.org) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "bsp/board.h" #include "tusb.h" #include "W25Q.h" #include "spi_sdmmc.h" #include "hardware/gpio.h" #include "storage_driver.h" w25q_data_t *pW25Q=NULL; sdmmc_data_t *pSDMMC=NULL; void storage_driver_init() { // W25Q driver initialize pW25Q = (w25q_data_t*)malloc(sizeof(w25q_data_t)); pW25Q->spiInit=false; w25q_disk_initialize(W25Q_SPI_PORT, W25Q_PIN_CS, pW25Q); // SDMMC driver initialize pSDMMC = (sdmmc_data_t*)malloc(sizeof(sdmmc_data_t)); pSDMMC->spiInit=false; #ifdef __SPI_SDMMC_DMA pSDMMC->dmaInit=false; #endif sdmmc_disk_initialize(SDMMC_SPI_PORT, SDMMC_PIN_CS, pSDMMC); // LED blinking when reading/writing gpio_init(LED_BLINKING_PIN); gpio_set_dir(LED_BLINKING_PIN, true); } #if CFG_TUD_MSC // Invoked to determine max LUN uint8_t tud_msc_get_maxlun_cb(void) { return 2; // LUN 0: SDMMC, LUN 1: W25Q Flash } // Invoked when received SCSI_CMD_INQUIRY // Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) { switch (lun) { case SDMMC_LUN: sprintf(vendor_id , "SDMMC"); sprintf(product_id , "Mass Storage"); sprintf(product_rev, "1.0"); break; case W25Q_LUN: sprintf(vendor_id , "Winbond"); sprintf(product_id , "Mass Storage"); sprintf(product_rev, "1.0"); break; } } // Invoked when received Test Unit Ready command. // return true allowing host to read/write this LUN e.g SD card inserted bool tud_msc_test_unit_ready_cb(uint8_t lun) { //if ( lun == 1 && board_button_read() ) return false; return true; // RAM disk is always ready } // Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size // Application update block count and block size void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) { switch(lun) { case SDMMC_LUN: *block_count = pSDMMC->sectCount; *block_size = pSDMMC->sectSize; break; case W25Q_LUN: *block_count = pW25Q->sectorCount; *block_size = pW25Q->sectorSize; break; } } // Invoked when received Start Stop Unit command // - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage // - Start = 1 : active mode, if load_eject = 1 : load disk storage bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) { (void) lun; (void) power_condition; if ( load_eject ) { if (start) { // load disk storage }else { // unload disk storage } } return true; } // Callback invoked when received READ10 command. // Copy disk's data to buffer (up to bufsize) and return number of copied bytes. int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) { switch(lun) { case SDMMC_LUN: if (!sdmmc_read_sector(lba, buffer, bufsize, pSDMMC)) return -1; break; case W25Q_LUN: if (!w25q_read_sector((uint32_t)lba, offset, buffer, bufsize, pW25Q)) return -1; break; } return (int32_t) bufsize; } bool tud_msc_is_writable_cb (uint8_t lun) { (void) lun; #ifdef CFG_EXAMPLE_MSC_READONLY return false; #else return true; #endif } // Callback invoked when received WRITE10 command. // Process data in buffer to disk's storage and return number of written bytes int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) { switch (lun) { case SDMMC_LUN: if (!sdmmc_write_sector(lba, buffer, bufsize, pSDMMC)) return -1; break; case W25Q_LUN: if (offset >= pW25Q->sectorSize) return -1; #ifndef CFG_EXAMPLE_MSC_READONLY w25q_sector_erase(lba, pW25Q); w25q_write_sector(lba, offset, buffer, bufsize, pW25Q); #else (void) lun; (void) lba; (void) offset; (void) buffer; #endif break; } return (int32_t) bufsize; } // Callback invoked when received an SCSI command not in built-in list below // - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE // - READ10 and WRITE10 has their own callbacks int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) { // read10 & write10 has their own callback and MUST not be handled here void const* response = NULL; int32_t resplen = 0; // most scsi handled is input bool in_xfer = true; switch (scsi_cmd[0]) { default: // Set Sense = Invalid Command Operation tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // negative means error -> tinyusb could stall and/or response with failed status resplen = -1; break; } // return resplen must not larger than bufsize if ( resplen > bufsize ) resplen = bufsize; if ( response && (resplen > 0) ) { if(in_xfer) { memcpy(buffer, response, (size_t) resplen); }else { // SCSI output } } return resplen; } #endif void led_blinking_task(void) { static uint32_t start_ms = 0; static bool led_state = false; // Blink every interval ms if ( board_millis() - start_ms < 50) return; // not enough time start_ms += 50; gpio_put(LED_BLINKING_PIN,led_state); led_state = 1 - led_state; // toggle } void led_blinking_task_off(void) { gpio_put(LED_BLINKING_PIN,false); }
- CMakeLists.txt(driver)
add_library(storage_driver INTERFACE) target_sources(storage_driver INTERFACE ${CMAKE_CURRENT_LIST_DIR}/sdmmc/spi_sdmmc.c ${CMAKE_CURRENT_LIST_DIR}/flash/W25Q.c ${CMAKE_CURRENT_LIST_DIR}/msc_device_disk.c ) target_include_directories(storage_driver INTERFACE ${CMAKE_CURRENT_LIST_DIR}/sdmmc ${CMAKE_CURRENT_LIST_DIR}/flash ${CMAKE_CURRENT_LIST_DIR} ) target_link_libraries(storage_driver INTERFACE hardware_spi hardware_dma hardware_rtc pico_stdlib )
- storage_driver.h
#ifndef _STORAGE_DRIVER_H_ #define _STORAGE_DRIVER_H_ /* used by my project */ #define SPI_BAUDRATE_LOW (1000*1000) #define SPI_BAUDRATE_HIGH (40*1000*1000) enum { SDMMC_LUN=0, W25Q_LUN=1, }; /* ================================= */ #define LED_BLINKING_PIN 25 void led_blinking_task(void); void led_blinking_task_off(void); /* ================================= */ void storage_driver_init(void); #endif
- 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" //#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 */ #define SDMMC_SECT_SIZE 512 typedef struct { spi_inst_t *spiPort; bool spiInit; uint csPin; uint8_t cardType; uint16_t sectSize; uint32_t sectCount; #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 uint8_t Stat; }sdmmc_data_t; uint8_t sdmmc_disk_initialize(spi_inst_t *spi, uint cs_pin, sdmmc_data_t *sdmmc); //static int sdmmc_read_datablock (uint8_t *buff, uint btr, sdmmc_data_t *sdmmc); //static int sdmmc_write_datablock (const uint8_t *buff, uint8_t token, sdmmc_data_t *sdmmc); //static uint8_t sdmmc_send_cmd(uint8_t cmd, uint32_t arg, sdmmc_data_t *sdmmc); uint8_t sdmmc_write_sector(uint32_t sector, uint8_t *buff, uint32_t len, sdmmc_data_t *sdmmc); uint8_t sdmmc_read_sector(uint32_t sector, uint8_t* buff, uint32_t len, 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
- 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" #include "storage_driver.h" #define SDMMC_CD 0 // card detect #define SDMMC_WP 0 // write protected static uint8_t dummy_block[SDMMC_SECT_SIZE]; void sdmmc_spi_cs_high(sdmmc_data_t *sdmmc); void sdmmc_spi_cs_low(sdmmc_data_t *sdmmc); static int sdmmc_wait_ready(uint timeout, sdmmc_data_t *sdmmc); static void sdmmc_init_spi(sdmmc_data_t *sdmmc); 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 */ } uint64_t sdmmc_get_sector_count(sdmmc_data_t *sdmmc) { uint8_t n, csd[16]; uint32_t st, ed, csize; uint64_t sectorCounter; uint8_t src = 0xFF; 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] + ((uint16_t)csd[8] << 8) + ((uint32_t)(csd[7] & 63) << 16) + 1; sectorCounter = 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) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) + 1; sectorCounter = csize << (n - 9); } } else { sectorCounter=0; } sdmmc_deselect(sdmmc); return sectorCounter; } uint32_t sdmmc_get_block_count(sdmmc_data_t *sdmmc) { uint8_t n, csd[16]; uint32_t st, ed, csize; uint32_t sectorCounter=0; uint8_t src = 0xFF; 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 */ sectorCounter = 16UL << (csd[10] >> 4); } } } 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 */ sectorCounter = (((csd[10] & 63) << 1) + ((uint16_t)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1); } else { /* MMC */ sectorCounter = ((uint16_t)((csd[10] & 124) >> 2) + 1) * (((csd[11] & 3) << 3) + ((csd[11] & 224) >> 5) + 1); } } } sdmmc_deselect(sdmmc); return sectorCounter; } uint8_t sdmmc_read_sector(uint32_t sector, uint8_t* buff, uint32_t len, sdmmc_data_t *sdmmc) { uint8_t ret=0; uint count; count = (len % sdmmc->sectSize) ? ((len / sdmmc->sectSize) + 1) : (len / sdmmc->sectSize); if (!count) return ret; /* Check parameter */ if (!(sdmmc->cardType & CT_BLOCK)) sector *= sdmmc->sectSize; /* LBA ot BA conversion (byte addressing cards) */ if (count == 1) { /* Single sector read */ if ((sdmmc_send_cmd(CMD17, sector, sdmmc) == 0) /* READ_SINGLE_BLOCK */ && sdmmc_read_datablock(buff, sdmmc->sectSize, sdmmc)) { ret = 1; } led_blinking_task(); //// LED blinking } else { /* Multiple sector read */ if (sdmmc_send_cmd(CMD18, sector, sdmmc) == 0) { /* READ_MULTIPLE_BLOCK */ do { if (!sdmmc_read_datablock(buff, sdmmc->sectSize, sdmmc)) break; buff += sdmmc->sectSize; led_blinking_task(); //// LED blinking } while (--count); sdmmc_send_cmd(CMD12, 0, sdmmc); /* STOP_TRANSMISSION */ ret = 1; } } led_blinking_task_off(); //// LED blinking off sdmmc_deselect(sdmmc); // sdmmc_select() is called in function sdmmc_send_cmd() return ret; } uint8_t sdmmc_write_sector(uint32_t sector, uint8_t *buff, uint32_t len, sdmmc_data_t *sdmmc) { uint8_t ret=0; uint count; count = (len % sdmmc->sectSize) ? ((len / sdmmc->sectSize)+1) : (len / sdmmc->sectSize); if (!count) return ret; /* 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)) sector *= sdmmc->sectSize; /* LBA ==> BA conversion (byte addressing cards) */ if (count == 1) { /* Single sector write */ if ((sdmmc_send_cmd(CMD24, sector, sdmmc) == 0) /* WRITE_BLOCK */ && sdmmc_write_datablock(buff, 0xFE, sdmmc)) { ret = 1; } led_blinking_task(); //// LED_blinking } else { /* Multiple sector write */ if (sdmmc->cardType & CT_SDC) sdmmc_send_cmd(ACMD23, count, sdmmc); /* Predefine number of sectors */ if (sdmmc_send_cmd(CMD25, sector, sdmmc) == 0) { /* WRITE_MULTIPLE_BLOCK */ do { if (!sdmmc_write_datablock(buff, 0xFC, sdmmc)) break; buff += sdmmc->sectSize; led_blinking_task(); //// LED_blinking } while (--count); // LED blinking off if (!sdmmc_write_datablock(0, 0xFD, sdmmc)) count = 1; /* STOP_TRAN token */ ret =1; } } led_blinking_task_off(); sdmmc_deselect(sdmmc); // sdmmc_select() is called in function sdmmc_send_cmd } /* 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 < SDMMC_SECT_SIZE; 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 uint8_ts) */ static void sdmmc_read_spi_dma( uint8_t *buff, /* Pointer to data buffer */ uint btr, /* Number of uint8_ts 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 } /* Send a sector data (512 uint8_ts) */ static void sdmmc_write_spi_dma( const uint8_t *buff, /* Pointer to the data */ uint btx, /* Number of uint8_ts 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 } /*-----------------------------------------------------------------------*/ /* 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 */ /*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/ /* Receive a data packet from the MMC */ /*-----------------------------------------------------------------------*/ // static int sdmmc_read_datablock( /* 1:OK, 0:Error */ uint8_t *buff, /* Data buffer */ uint btr, /* Data block length (uint8_t) */ sdmmc_data_t *sdmmc) { uint8_t 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 uint8_t *buff, /* Ponter to 512 uint8_t data to be sent */ uint8_t token, /* Token */ sdmmc_data_t *sdmmc) { uint8_t 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, sdmmc->sectSize, 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 uint8_t sdmmc_send_cmd( /* Return value: R1 resp (bit7==1:Failed to send) */ uint8_t cmd, /* Command index */ uint32_t arg, /* Argument */ sdmmc_data_t *sdmmc) { uint8_t n, res; uint8_t 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] = (uint8_t)(arg >> 24); // 32 bits argument tcmd[2] = (uint8_t)(arg >> 16); tcmd[3] = (uint8_t)(arg >> 8); tcmd[4] = (uint8_t)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 uint8_t when CMD12 */ n = 10; /* Wait for response (10 uint8_ts max) */ do { spi_read_blocking(sdmmc->spiPort, 0xFF, &res, 1); } while ((res & 0x80) && --n); return res; /* Return received response */ } /*-----------------------------------------------------------------------*/ /* Initialize disk drive */ /*-----------------------------------------------------------------------*/ uint8_t sdmmc_init(sdmmc_data_t *sdmmc) { uint8_t n, cmd, ty, src, ocr[4]; sdmmc->Stat = 0; // 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 uint8_ts): 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, SDMMC_SECT_SIZE, 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 = SDMMC_SECT_SIZE; sdmmc->Stat = 1; /* Clear STA_NOINIT flag */ } else { /* Failed */ sdmmc->Stat = 0; } sdmmc->sectCount = sdmmc_get_sector_count(sdmmc); return sdmmc->Stat; } ///////////////////////////////////////////// uint8_t sdmmc_disk_initialize(spi_inst_t *spi, uint cs_pin, sdmmc_data_t *sdmmc) { sdmmc->spiPort = spi; sdmmc->csPin = cs_pin; if (!sdmmc->spiInit) { sdmmc_init_spi(sdmmc); /* Initialize SPI */ } uint8_t stat = sdmmc_init(sdmmc); return stat; }
- W25Q.h
#ifndef W25Q_H #define W25Q_H #include "stdio.h" #include "stdlib.h" #include "pico/stdlib.h" #include "hardware/spi.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 blockSize; uint32_t sectorCount; uint32_t sectorSize; uint32_t pageCount; uint16_t pageSize; uint8_t statusRegister1; uint8_t statusRegister2; uint8_t statusRegister3; uint32_t capacityKB; uint8_t lock; bool spiInit; uint8_t Stat; }w25q_data_t; uint8_t 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); uint8_t 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
- W25Q.c
#include "stdio.h" #include "stdlib.h" #include "W25Q.h" #include "storage_driver.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); } /*==================*/ uint8_t 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 = 0; 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; led_blinking_task(); } while (bytesToWrite > 0); led_blinking_task_off(); } 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); } uint8_t w25q_disk_write( const uint8_t *buff, /* Ponter to the data to write */ uint64_t sector, /* Start sector number (LBA) */ uint count, /* Number of sectors to write (1..128) */ w25q_data_t *w25q ) { uint8_t *tbuf=(uint8_t*)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? 1: 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; } uint8_t 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 0; 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; led_blinking_task(); } while (bytesToRead > 0); led_blinking_task_off(); return 1; } 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); }
- main.c
#include <stdlib.h> #include <stdio.h> #include <string.h> #include "pico/stdlib.h" #include "bsp/board.h" #include "tusb.h" #include "storage_driver.h" /*------------- MAIN -------------*/ int main(void) { stdio_init_all(); storage_driver_init(); board_init(); // init device stack on configured roothub port tud_init(BOARD_TUD_RHPORT); while (1) { tud_task(); // tinyusb device task } return 0; }
- CMakeLists.txt(root)
# Generated Cmake Pico project file cmake_minimum_required(VERSION 3.13) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) # Initialise pico_sdk from installed location # (note this can come from environment, CMake cache etc) set(PICO_SDK_PATH "/home/duser/pico/pico-sdk") set(PICO_BOARD pico CACHE STRING "Board type") # Pull in Raspberry Pi Pico SDK (must be before project) include(pico_sdk_import.cmake) if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0") message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}") endif() project(pico_usb_msc_device C CXX ASM) # Initialise the Raspberry Pi Pico SDK pico_sdk_init() # Add executable. Default name is the project name, version 0.1 add_executable(pico_usb_msc_device main.c usb_descriptors.c ) pico_set_program_name(pico_usb_msc_device "pico_usb_msc_device") pico_set_program_version(pico_usb_msc_device "0.1") pico_enable_stdio_uart(pico_usb_msc_device 1) pico_enable_stdio_usb(pico_usb_msc_device 0) # Add the standard library to the build target_link_libraries(pico_usb_msc_device pico_stdlib) # Add the standard include files to the build target_include_directories(pico_usb_msc_device PRIVATE ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required ) add_subdirectory(storage_driver) # Add any user requested libraries target_link_libraries(pico_usb_msc_device tinyusb_device tinyusb_board storage_driver ) pico_add_extra_outputs(pico_usb_msc_device)