本實驗 整合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; }
沒有留言:
張貼留言