本文章介紹使用Raspberry Pi Pico PIO程式製作SD memory Card 4-bit wide bus and 1-bit data bus驅動程式。並與FatFs檔案系統結合,測試在SD card上進行檔案讀寫。
SD memory card主要分為Command, data read and data write三個部份。每個部份我們製作一個相對應的State machine:CMD, READ and WRITE。
一、CMD PIO state machine:
command為48bits:第一個byte:01開頭為HOST to Card, 00開頭為Card to Host, 其餘6bits為Command ID。接下來4 bytes為parameters。最後一個byte為CRC7and 1 bit(stop bit)。response分成0,48 and 136 bits三種。
CMD state machine使用1 CMD line and 1 CLK line:
CMD pin:IN/OUT/SET/JMP。 CLK pin:side-set
詳細程式碼附於文章末尾。
二、READ State machine:
Card output(HOST read): 每個data block為512 bytes後面跟著CRC16。4-bit wide bus DAT line分別需要個別的CRC16 data。
READ state machine: 1 or 4 DAT line, 1 CLK line。
DAT pin(s): IN/SET/JMP, CLK pin:side-set
三、WRITE state machine:
Card input(HOST output):host寫入一個data block後面跟CRC16。Card於3 clocks後在DAT0 (DAT1~3 don't care) 送出CRC status(010為positive, 101為negative)。
WRITE state machine: 1~4 DAT line, 1 CLK line。
DAT pin(s): IN/OUT/SET/JMP, CLK pin:side-set
四、HOST read data:
- 啟動CMD state machine, host向card 送出data read command(CMD17 or CMD18),收到rsponse後,停止state machine(disable)。
- 啟動READ state machine讀取資料。
- CMD與READ state machine使用自己的CLK pin。因此在CMD state machine換成READ state machine時,clock會先暫停。根據SD specification "clock control"章節說明是允許的。
五、HOST DATA write:
- 啟動CMD state machine, host向card 送出data write command(CMD24 or CMD25),收到rsponse後,停止state machine(disable)。
- 啟動WRITE state machine write data block and crc。接著讀取crc status。
參考資料為:
SD Specifications Part 1 Physical Layer Specification(https://www.sdcard.org/)
成果展示:
程式碼:
- sdio_mem_card.c
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/dma.h"
#include "hardware/pio.h"
#include "sdio_mem_card.h"
#include "sdio_mem_card.pio.h"
#include "hardware/clocks.h"
#include "crc7.h"
#include "crc-itu-t.h"
#include "string.h"
#include "inttypes.h"
/*
void pio_irq_read_data() {
if (pio_interrupt_get(SDIO_MEM_PIO, 0)) {
pio_interrupt_clear(SDIO_MEM_PIO, 0);
printf("irq 0:\n");
}
if (pio_interrupt_get(SDIO_MEM_PIO, 1)) {
pio_interrupt_clear(SDIO_MEM_PIO, 1);
printf("irq:1\n");
}
}
*/
/*!
* \brief sdio memory card pio initialize
* \param pio: pio number
* \param sm state machine
* \param cmd_pin command pin
* \param clk_pin CLK pin
* \param data_pin_base data 0~4 pins(D0~D3)
* \param clk_int Integer part of the divisor
* \param clk_frac – Fractional part in 1/256ths
*/
void sdio_pio_init(sd_memory_card_t* pSD, PIO pio, uint cmd_pin, uint clk_pin, uint data_pin_base, uint16_t clk_int, uint8_t clk_frac) {
uint offset=0;
pio_sm_config c;
static uint8_t data_width;
data_width = (pSD->is_wide_bus ? 4:1);
pio_gpio_init(pio, cmd_pin);
pio_gpio_init(pio, clk_pin);
gpio_pull_up(cmd_pin);
for (int i=0; i < 4; i++) {
pio_gpio_init(pio, data_pin_base+i);
gpio_pull_up(data_pin_base+i);
}
pio_enable_sm_mask_in_sync(pio, (1<<SDIO_CMD_SM)|(1<<SDIO_DATA_READ_SM)|(1<<SDIO_DATA_WRITE_SM));
//== SDIO READ SM ==
pio_sm_config cr;
if (data_width == 1) {
pSD->sd_data_rx_offset = pio_add_program(pio, &sdio_1_bit_rx_program);
cr = sdio_1_bit_rx_program_get_default_config(pSD->sd_data_rx_offset);
}
else {
pSD->sd_data_rx_offset = pio_add_program(pio, &sdio_4_bit_rx_program);
cr = sdio_4_bit_rx_program_get_default_config(pSD->sd_data_rx_offset);
}
pio_sm_set_consecutive_pindirs(pio, SDIO_DATA_READ_SM, data_pin_base, 4, false);
pio_sm_set_consecutive_pindirs(pio, SDIO_DATA_READ_SM, clk_pin, 1, true);
//pio_sm_set_consecutive_pindirs(pio, SDIO_DATA_READ_SM, cmd_pin, 1, false);
sm_config_set_in_pins(&cr, data_pin_base);
sm_config_set_sideset_pins(&cr, clk_pin);
sm_config_set_set_pins(&cr, data_pin_base,data_width);
sm_config_set_jmp_pin(&cr, data_pin_base);
sm_config_set_out_shift(&cr, false, true, 32);
sm_config_set_in_shift(&cr, false, true, 32);
sm_config_set_clkdiv_int_frac(&cr, clk_int,clk_frac);
pio_sm_init(pio, SDIO_DATA_READ_SM, pSD->sd_data_rx_offset, &cr);
//== SDIO WRITE SM ==
pio_sm_config cw;
if (data_width == 1) {
pSD->sd_data_tx_offset = pio_add_program(pio, &sdio_1_bit_tx_program);
cw = sdio_1_bit_rx_program_get_default_config(pSD->sd_data_tx_offset);
}
else {
pSD->sd_data_tx_offset = pio_add_program(pio, &sdio_4_bit_tx_program);
cw = sdio_4_bit_rx_program_get_default_config(pSD->sd_data_tx_offset);
}
pio_sm_set_consecutive_pindirs(pio, SDIO_DATA_WRITE_SM, data_pin_base, 4, true);
pio_sm_set_consecutive_pindirs(pio, SDIO_DATA_WRITE_SM, clk_pin, 1, true);
//pio_sm_set_consecutive_pindirs(pio, SDIO_DATA_WRITE_SM, cmd_pin, 1, false);
sm_config_set_in_pins(&cw, data_pin_base);
sm_config_set_out_pins(&cw, data_pin_base, data_width);
sm_config_set_sideset_pins(&cw, clk_pin);
sm_config_set_set_pins(&cw, data_pin_base,data_width);
sm_config_set_jmp_pin(&cw, data_pin_base);
sm_config_set_out_shift(&cw, false, true, 32);
sm_config_set_in_shift(&cw, false, true, 32);
sm_config_set_clkdiv_int_frac(&cw, clk_int,clk_frac);
pio_sm_init(pio, SDIO_DATA_WRITE_SM, pSD->sd_data_tx_offset, &cw);
//== SDIO command SM ==
offset = pio_add_program(pio, &sdio_mem_cmd_program);
c = sdio_mem_cmd_program_get_default_config(offset);
pio_sm_set_consecutive_pindirs(pio, SDIO_CMD_SM, cmd_pin, 1, false);
pio_sm_set_consecutive_pindirs(pio, SDIO_CMD_SM, clk_pin, 1, true);
pio_sm_set_consecutive_pindirs(pio, SDIO_CMD_SM, data_pin_base, 4, false);
sm_config_set_in_pins(&c, cmd_pin);
sm_config_set_out_pins(&c, cmd_pin, 1);
sm_config_set_set_pins(&c, cmd_pin,1);
sm_config_set_sideset_pins(&c, clk_pin);
sm_config_set_jmp_pin(&c, cmd_pin);
sm_config_set_out_shift(&c, false, true, 32);
sm_config_set_in_shift(&c, false, true, 32);
sm_config_set_clkdiv_int_frac(&c, clk_int,clk_frac);
pio_sm_init(pio, SDIO_CMD_SM, offset, &c);
// initial state machine but not run
pio_sm_set_enabled(pio, SDIO_CMD_SM, false);
pio_sm_set_enabled(pio, SDIO_DATA_READ_SM, false);
pio_sm_set_enabled(pio, SDIO_DATA_WRITE_SM, false);
//** for test only
//uint pio_irq = pio_get_index(pio)? PIO1_IRQ_0:PIO0_IRQ_0;
//pio_set_irq0_source_enabled(pio, pis_interrupt0, true);
//pio_set_irq0_source_enabled(pio, pis_interrupt1, true);
//irq_add_shared_handler(pio_irq, pio_irq_read_data, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
//irq_set_enabled(pio_irq, true);
#if _DMA_TRANS_
/* DMA */
uint pio_base = (pio==pio0)?PIO0_BASE:PIO1_BASE;
// ==== Write DMA ===
pSD->dma_write_channel = dma_claim_unused_channel(true);
dma_channel_config dcw = dma_channel_get_default_config(pSD->dma_write_channel);
channel_config_set_write_increment(&dcw, false);
channel_config_set_read_increment(&dcw, true);
channel_config_set_bswap(&dcw, true);
channel_config_set_dreq(&dcw, pio_get_dreq(pio, SDIO_DATA_WRITE_SM, true));
channel_config_set_transfer_data_size(&dcw, DMA_SIZE_32); //DMA_SIZE_8,16,32
dma_channel_configure(pSD->dma_write_channel,
&dcw, (void*) (pio_base+PIO_TXF2_OFFSET), // SDIO_DATA_WRITE_SM = 2
NULL, MAX_RW_BLOCKS_TRANS>> DMA_SIZE_32, false); //DMA_SIZE_8 or 16 or 32
//==== READ DMA ===
pSD->dma_read_channel = dma_claim_unused_channel(true);
dma_channel_config dcr = dma_channel_get_default_config(pSD->dma_read_channel);
channel_config_set_write_increment(&dcr, true);
channel_config_set_read_increment(&dcr, false);
channel_config_set_bswap(&dcr, true);
channel_config_set_dreq(&dcr, pio_get_dreq(pio, SDIO_DATA_READ_SM, false));
channel_config_set_transfer_data_size(&dcr, DMA_SIZE_32); //DMA_SIZE_8,16,32
dma_channel_configure(pSD->dma_read_channel,
&dcr, NULL, (void*) (pio_base+PIO_RXF1_OFFSET), // SDIO_DATA_READ_SM = 1
MAX_RW_BLOCKS_TRANS>> DMA_SIZE_32, false); //DMA_SIZE_8 or 16 or 32
/* DMA */
#endif
}
/*!
* \brief send SDIO CMD and recv RESPONSE
* \param cmd_id command ID
* \param arg args size 4 bytes(32bits)
* \param resp RESPONSE Register (48 bits / 136 bites)
* \param resp_bits response register length
*/
uint8_t sdio_send_recv_cmd(uint8_t cmd_id, uint8_t *arg, uint32_t *resp, uint8_t resp_bits) {
uint8_t cmd;
uint32_t cmd_buf[2];
uint8_t crc = 0;
cmd = cmd_id | 0x40; // start 0b01 send from host to card
// crc7
crc = crc7_table[crc ^ cmd];
for (int i=0; i < count_of(arg); i++)
crc = crc7_table[crc ^ arg[i]];
// crc7
if (resp_bits > 0) resp_bits--;
// put to fifo: 1byte command len,1byte command ID, 4bytes arg ,1byte crc ,1bytes RESP
cmd_buf[0] = 47 << 24 | cmd << 16 | arg[0] << 8 | arg[1];
cmd_buf[1] = arg[2] << 24 | arg[3] << 16 | (crc | 0x01) << 8 | (resp_bits);
memset(resp, 0, sizeof(resp));
pio_sm_clear_fifos(SDIO_MEM_PIO, SDIO_CMD_SM);
pio_sm_exec(SDIO_MEM_PIO, SDIO_CMD_SM,pio_encode_set(pio_pins,1));
pio_sm_exec(SDIO_MEM_PIO, SDIO_CMD_SM,pio_encode_set(pio_pindirs,1));
//Output shift counter is reset to 0 by this operation, i.e. full)
pio_sm_exec(SDIO_MEM_PIO, SDIO_CMD_SM, pio_encode_mov(pio_osr, pio_null));
for (int i = 0; i < count_of(cmd_buf); i++) {
pio_sm_put_blocking(SDIO_MEM_PIO, SDIO_CMD_SM, cmd_buf[i]);
}
pio_sm_exec(SDIO_MEM_PIO, SDIO_CMD_SM, pio_encode_jmp(sdio_mem_cmd_offset_sdio_cmd_start));
pio_sm_set_enabled(SDIO_MEM_PIO, SDIO_CMD_SM, true);
switch (resp_bits) {
case 47:
resp[0] = pio_sm_get_blocking(SDIO_MEM_PIO, SDIO_CMD_SM);
resp[1] = pio_sm_get_blocking(SDIO_MEM_PIO, SDIO_CMD_SM);
break;
case 135:
for (int i=0; i < 5; i++) {
resp[i] = pio_sm_get_blocking(SDIO_MEM_PIO, SDIO_CMD_SM);
}
break;
case 0:
resp[0] = pio_sm_get_blocking(SDIO_MEM_PIO, SDIO_CMD_SM); //dummy
break;
}
pio_sm_set_enabled(SDIO_MEM_PIO, SDIO_CMD_SM, false);
return SD_OK;
}
bool format_sdio_response_136(uint32_t *resp_32, uint32_t *content) {
if ((resp_32[0]>>24) != 0x3f){ printf("get CID/CSD error\n"); return false;}
for (int i=0; i < 3;i++) {
content[i] = resp_32[i] << 8 | resp_32[i+1] >> 24;
}
content[3] = (resp_32[3] >> 24) << 8 | resp_32[4]&0xff;
}
bool format_sdio_response_48(uint32_t *resp_32, uint8_t *resp_8) {
resp_8[0] = resp_32[0] >> 24;
resp_8[1] = (resp_32[0]&0x00ff0000) >> 16;
resp_8[2] = (resp_32[0]&0x0000ff00) >> 8;
resp_8[3] = resp_32[0] &0x000000ff;
resp_8[4] = (resp_32[1]&0x0000ff00) >> 8;
resp_8[5] = (resp_32[1] &0x000000ff) ;
uint8_t crc = 0;
for (int i=0; i < 5; i++)
crc = crc7_table[crc ^ resp_8[i]];
if (crc != (resp_8[5]&0xfe)) return false;
return true;
}
uint32_t get_sd_card_status(sd_memory_card_t* pSD) {
uint8_t arg[4];
uint32_t resp[2];
arg[0] = pSD->rca[0];
arg[1] = pSD->rca[1];
arg[2] = 0;
arg[3] = 0;
sdio_send_recv_cmd(CMD13, arg, resp, 48);
return (resp[0])<<8 | (resp[1]>>8);
}
uint32_t format_R1_response(uint32_t *resp) {
return (resp[0]&0x00ffffff)<<8 | (resp[1]>>8);
}
/*!
* \brief initialize sd card
* \param wide_bus true:4bit, false 1bit
*/
uint8_t sd_memory_card_init(sd_memory_card_t* pSD, uint8_t wide_bus) {
uint8_t arg[4];
uint32_t resp[5];
uint8_t resp_bytes[17];
uint8_t ret_val;
uint32_t CSID[5];
uint8_t ret = SD_OK;
pSD->SD_initialized=false;
pio_clear_instruction_memory(SDIO_MEM_PIO);
pSD->is_wide_bus = (wide_bus)? true:false;
sdio_pio_init(pSD, SDIO_MEM_PIO, SDIO_CMD_PIN, SDIO_CLK_PIN, SDIO_D0_PIN, 1, 0);
sleep_ms(2);
memset(arg, 0, sizeof(arg));
sdio_send_recv_cmd(CMD0, arg, resp, 0); // CMD0: REEST
for (int i=0; i < 10; i++) {
memset(arg, 0, sizeof(arg));
arg[2]=1;
arg[3]=0xaa;
sdio_send_recv_cmd(CMD8, arg, resp, 48); //CMD8: SEND_IF_CONF
if(format_sdio_response_48(resp, resp_bytes)) {
if (resp_bytes[4]== arg[3])
printf("cmd8 ok:%d\n",i);
ret = SD_OK;
break;
} else {
ret = SD_INIT_FAILURE;
}
}
absolute_time_t timeout = make_timeout_time_ms(1000);
do {
memset(arg, 0, sizeof(arg));
sdio_send_recv_cmd(CMD55, arg, resp, 48); // next is APP_CMD
arg[0] = 0x40; // HCS, XPC, S18R
arg[1] = 0x10;
sdio_send_recv_cmd(ACMD41, arg, resp, 48); // R3
format_sdio_response_48(resp, resp_bytes); // crc is always 1111111
//for (int i=0; i < 6; i++) { printf("%02x ",resp_bytes[i]);} printf("\n");
if (resp_bytes[0]==0x3f && (resp_bytes[1] & 0x80)) {
ret = SD_OK;
break;
}
else {
ret = SD_TIME_OUT;
}
} while (absolute_time_diff_us(get_absolute_time(), timeout) > 0);
// 0x80 initialize completely. R3[1]: Busy CCS UHS-II Reserved(0000) S18A
if(ret != SD_OK) return ret;
memset(arg, 0, sizeof(arg));
if (resp_bytes[1] & 0x01) {
sdio_send_recv_cmd(CMD11, arg, resp, 0); // voltage switch
}
sdio_send_recv_cmd(CMD2, arg, resp, 136); // CID(R2)
sdio_send_recv_cmd(CMD3, arg, resp, 48);
if (format_sdio_response_48(resp, resp_bytes)) {
pSD->rca[0] = resp_bytes[1];
pSD->rca[1] = resp_bytes[2];
printf("rca received:%02x, %02x\n", pSD->rca[0], pSD->rca[1]);
} else {
printf("CMD3 response crc error\n");
for (int i=0; i < 6; i++) {
printf("%02x ",resp_bytes[i]);
}
printf("\n");
return SD_CRC_ERROR;
}
//get CID regisger ===
memset(arg,0,sizeof(arg));
arg[0] = pSD->rca[0];
arg[1] = pSD->rca[1];
for (int i=0; i<10; i++) {
if (sdio_send_recv_cmd(CMD10, arg, resp, 136)== SD_OK) {
ret = SD_OK;
break; // CID (R2)
}
else {
ret = SD_INIT_FAILURE;
}
}
format_sdio_response_136(resp, CSID);
pSD->pnm[0]= (uint8_t)(CSID[0]);
pSD->pnm[1]= (uint8_t)((CSID[1]>>24));
pSD->pnm[2]= (uint8_t)((CSID[1]>>16));
pSD->pnm[3]= (uint8_t)((CSID[1])>>8);
pSD->pnm[4]= (uint8_t)(CSID[1]);
pSD->pnm[5]= '\0';
pSD->prv = (uint8_t)(CSID[2]>>24);
pSD->psn = (CSID[2]) << 8 | CSID[3]>>24;
// === get CID end
//===get CSD register ===
memset(arg,0,sizeof(arg));
arg[0] = pSD->rca[0];
arg[1] = pSD->rca[1];
for (int i=0; i<10; i++) {
if (sdio_send_recv_cmd(CMD9, arg, resp, 136)== SD_OK) {
ret = SD_OK;
break; // CSD (R2)
}else {
ret = SD_INIT_FAILURE;
}
sleep_ms(2);
}
format_sdio_response_136(resp, CSID);
printf("CSD:%08x,%08x,%08x,%08x\n", CSID[0],CSID[1],CSID[2],CSID[3]);
pSD->block_size = 1<<((CSID[1]>>16) & 0xf) ;
pSD->total_blocks = (CSID[1]&0x1f) << 16 |CSID[2] >> 16;
printf("block_size:%d, total_blocks:%u, capacity:%d KB\n",
pSD->block_size,
pSD->total_blocks,
(pSD->total_blocks/(1024/pSD->block_size))
);
// ==get CSD end===
memset(arg,0,sizeof(arg));
arg[0] = pSD->rca[0];
arg[1] = pSD->rca[1];
sdio_send_recv_cmd(CMD7, arg, resp, 48); // R1b, SELECT_CARD
//sdio_send_recv_cmd(CMD42, arg, resp, 48); //R1
//format_sdio_response_48(resp, resp_bytes);
arg[0] = pSD->rca[0];
arg[1] = pSD->rca[1];
sdio_send_recv_cmd(CMD55, arg, resp, 48); // next is APP_CMD
if (wide_bus) arg[3]=0x02; // 4-bit
sdio_send_recv_cmd(ACMD6, arg, resp, 48); //R1, set bus width
//format_sdio_response_48(resp, resp_bytes);
//printf("status:%0x\n", resp_bytes[4]);
sdio_send_recv_cmd(CMD13, arg, resp, 48);
format_sdio_response_48(resp, resp_bytes);
if (resp_bytes[3] == 0x09 && resp_bytes[4]==0x00)
{
uint8_t test[512];
sd_read_single_block(pSD, (uint32_t*)test, pSD->total_blocks);
if (get_sd_card_status(pSD)==0x0900) {
pSD->SD_initialized=true;
printf("SD card initialize successfully\n");
}
}
return ret;
}
uint8_t sd_stop_trans() {
uint8_t arg[4];
uint32_t resp[2];
uint8_t resp_bytes[5];
memset(arg, 0, sizeof(arg));
sdio_send_recv_cmd(CMD12, arg, resp, 48); //R1b,
return SD_OK;
}
uint16_t calc_1bit_crc16(uint32_t *buff, uint16_t words_per_block){
uint16_t crc =0;
uint32_t temp;
for (int i= 0; i< words_per_block; i++) {
temp = __builtin_bswap32(buff[i]);
crc = crc_itu_t_table[(crc >> 8) ^ (uint8_t)(temp>>24)] ^ (crc << 8);
crc = crc_itu_t_table[(crc >> 8) ^ (uint8_t)(temp>>16)] ^ (crc << 8);
crc = crc_itu_t_table[(crc >> 8) ^ (uint8_t)(temp>>8)] ^ (crc << 8);
crc = crc_itu_t_table[(crc >> 8) ^ (uint8_t)(temp)] ^ (crc << 8);
}
return crc;
}
uint64_t calc_4bit_crc16(uint32_t *data, uint32_t num_words)
{
uint64_t crc = 0;
uint32_t *end = data + num_words;
while (data < end)
{
for (int unroll = 0; unroll < 4; unroll++)
{
// Each 32-bit word contains 8 bits per line.
// Reverse the bytes because SDIO protocol is big-endian.
uint32_t data_in = __builtin_bswap32(*data++);
// Shift out 8 bits for each line
uint32_t data_out = crc >> 32;
crc <<= 32;
// XOR outgoing data to itself with 4 bit delay
data_out ^= (data_out >> 16);
// XOR incoming data to outgoing data with 4 bit delay
data_out ^= (data_in >> 16);
// XOR outgoing and incoming data to accumulator at each tap
uint64_t xorred = data_out ^ data_in;
crc ^= xorred;
crc ^= xorred << (5 * 4);
crc ^= xorred << (12 * 4);
}
}
return crc;
}
uint8_t pio_sd_read_blocks(sd_memory_card_t* pSD, uint32_t *buff, uint32_t count, uint32_t* crc) {
uint32_t read_cycles;
uint read_start_addr;
uint8_t ret=SD_OK;
uint16_t block_data_bytes;
uint8_t crc_len;
if (pSD->is_wide_bus) {
block_data_bytes=(512+2*4);
read_cycles=block_data_bytes*2-1;
read_start_addr = pSD->sd_data_rx_offset+sdio_4_bit_rx_offset_rx_start;
crc_len=2;
}
else
{
block_data_bytes = (512+2);
read_cycles = block_data_bytes*8-1;
read_start_addr = pSD->sd_data_rx_offset+sdio_1_bit_rx_offset_rx_start;
crc_len=1;
}
pio_sm_clear_fifos(SDIO_MEM_PIO, SDIO_DATA_READ_SM);
pio_sm_restart(SDIO_MEM_PIO, SDIO_DATA_READ_SM);
pio_sm_exec(SDIO_MEM_PIO, SDIO_DATA_READ_SM,pio_encode_set(pio_pindirs, 0));
//pio_sm_exec(SDIO_MEM_PIO, SDIO_DATA_READ_SM, pio_encode_mov(pio_isr, pio_null));
pio_sm_put_blocking(SDIO_MEM_PIO, SDIO_DATA_READ_SM, read_cycles);
pio_sm_exec(SDIO_MEM_PIO, SDIO_DATA_READ_SM, pio_encode_out(pio_x, 32));
pio_sm_exec(SDIO_MEM_PIO, SDIO_DATA_READ_SM, pio_encode_jmp(read_start_addr));
pio_sm_set_enabled(SDIO_MEM_PIO, SDIO_DATA_READ_SM, true);
for (int block_idx=0; block_idx < count; block_idx++) {
#if _DMA_TRANS_
dma_channel_set_trans_count(pSD->dma_read_channel, SDIO_WORDS_PER_BLOCK, false);
dma_channel_set_write_addr(pSD->dma_read_channel, buff+block_idx*SDIO_WORDS_PER_BLOCK, false);
dma_channel_start(pSD->dma_read_channel);
dma_channel_wait_for_finish_blocking(pSD->dma_read_channel);
dma_channel_set_trans_count(pSD->dma_read_channel, crc_len, false);
dma_channel_set_write_addr(pSD->dma_read_channel, crc+crc_len*block_idx, false);
dma_channel_start(pSD->dma_read_channel);
dma_channel_wait_for_finish_blocking(pSD->dma_read_channel);
#else
for (int i=0; i < SDIO_WORDS_PER_BLOCK; i++) {
buff[i+SDIO_WORDS_PER_BLOCK*block_idx] = __builtin_bswap32(pio_sm_get_blocking(SDIO_MEM_PIO, SDIO_DATA_READ_SM));
}
for (int c=0; c < crc_len; c++) {
crc[c+crc_len*block_idx] = __builtin_bswap32(pio_sm_get_blocking(SDIO_MEM_PIO, SDIO_DATA_READ_SM));
}
#endif
}
pio_sm_set_enabled(SDIO_MEM_PIO, SDIO_DATA_READ_SM, false);
return SD_OK;
}
uint8_t pio_sd_write_blocks(sd_memory_card_t* pSD, uint32_t *buff, uint32_t count) {
uint32_t write_cycles;
uint write_start_addr;
uint8_t ret=SD_OK;
uint16_t block_data_bytes;
uint16_t crc;
uint32_t start, end;
uint32_t lines=1;
if (pSD->is_wide_bus) {
block_data_bytes=(4+512+2*4); // start 0xfffffff0, end 0xffffffff
write_cycles=block_data_bytes*2;
write_start_addr = pSD->sd_data_tx_offset+sdio_4_bit_tx_offset_tx_start;
start=0xfffffff0;
end = 0xffffffff;
lines=4;
}
else
{
block_data_bytes = (4+512+2); // start 0xfffffffe,
write_cycles = block_data_bytes*8;
write_start_addr = pSD->sd_data_tx_offset+sdio_1_bit_tx_offset_tx_start;
start = 0xfffffffe;
end =0xffffffff;
lines=1;
}
pio_sm_clear_fifos(SDIO_MEM_PIO, SDIO_DATA_WRITE_SM);
pio_sm_restart(SDIO_MEM_PIO, SDIO_DATA_WRITE_SM);
// set pins output
uint pindirs;
if (pSD->is_wide_bus){
pio_sm_exec(SDIO_MEM_PIO, SDIO_DATA_WRITE_SM, pio_encode_set(pio_pins, 15));
pio_sm_exec(SDIO_MEM_PIO, SDIO_DATA_WRITE_SM, pio_encode_set(pio_pindirs, 15));
} else {
pio_sm_exec(SDIO_MEM_PIO, SDIO_DATA_WRITE_SM, pio_encode_set(pio_pins, 1));
pio_sm_exec(SDIO_MEM_PIO, SDIO_DATA_WRITE_SM, pio_encode_set(pio_pindirs, 1));
}
pio_sm_put_blocking(SDIO_MEM_PIO, SDIO_DATA_WRITE_SM, write_cycles);
pio_sm_exec(SDIO_MEM_PIO, SDIO_DATA_WRITE_SM, pio_encode_out(pio_x, 32));
pio_sm_put_blocking(SDIO_MEM_PIO, SDIO_DATA_WRITE_SM, 31);//
pio_sm_exec(SDIO_MEM_PIO, SDIO_DATA_WRITE_SM, pio_encode_out(pio_y, 32));
pio_sm_exec(SDIO_MEM_PIO, SDIO_DATA_WRITE_SM, pio_encode_jmp(write_start_addr));
uint32_t crc_and_stop_bit;
uint32_t crc32_h, crc32_l;
if (pSD->is_wide_bus) {
uint64_t crc64 = calc_4bit_crc16(buff, SDIO_WORDS_PER_BLOCK);
crc32_h = (uint32_t)(crc64>>32);
crc32_l = (uint32_t)crc64;
} else {
crc = calc_1bit_crc16(buff, SDIO_WORDS_PER_BLOCK);
crc_and_stop_bit = crc <<16 | 0xffff;
}
pio_sm_put_blocking(SDIO_MEM_PIO, SDIO_DATA_WRITE_SM, start); //start bit
pio_sm_set_enabled(SDIO_MEM_PIO, SDIO_DATA_WRITE_SM, true);
#if _DMA_TRANS_
dma_channel_set_trans_count(pSD->dma_write_channel, SDIO_WORDS_PER_BLOCK, false);
dma_channel_set_read_addr(pSD->dma_write_channel, buff, false);
dma_channel_start(pSD->dma_write_channel);
dma_channel_wait_for_finish_blocking(pSD->dma_write_channel);
#else
for (int i=0; i < SDIO_WORDS_PER_BLOCK; i++) {
pio_sm_put_blocking(SDIO_MEM_PIO, SDIO_DATA_WRITE_SM, __builtin_bswap32(buff[i]));
}
#endif
if (pSD->is_wide_bus) {
pio_sm_put_blocking(SDIO_MEM_PIO, SDIO_DATA_WRITE_SM,crc32_h);
pio_sm_put_blocking(SDIO_MEM_PIO, SDIO_DATA_WRITE_SM,crc32_l);
pio_sm_put_blocking(SDIO_MEM_PIO, SDIO_DATA_WRITE_SM,end);
} else {
pio_sm_put_blocking(SDIO_MEM_PIO, SDIO_DATA_WRITE_SM,crc_and_stop_bit);
}
uint32_t crc_status;
crc_status= pio_sm_get_blocking(SDIO_MEM_PIO, SDIO_DATA_WRITE_SM);
pio_sm_set_enabled(SDIO_MEM_PIO, SDIO_DATA_WRITE_SM, false);
// card report postive crc:010, neg:101(card status)
if (crc_status>>24 != 0xe5){ // EZZS...E(pos:e5, net:e9)
//printf("write CRC Error\n");
ret = SD_CRC_ERROR;
}
return ret;
}
uint8_t sd_read_single_block(sd_memory_card_t *pSD, uint32_t *buff, uint32_t address) {
uint8_t arg[4];
uint32_t resp[2];
uint32_t read_crc[2];
uint8_t ret = SD_OK;
arg[0] = address >> 24;
arg[1] = (address & 0x00ff0000) >> 16;
arg[2] = (address & 0x0000ff00) >> 8;
arg[3] = (address & 0x000000ff);
sdio_send_recv_cmd(CMD17, arg, resp, 48); //R1,
if (format_R1_response(resp) == 0x0900)
{
ret = pio_sd_read_blocks(pSD, buff, 1, read_crc);
} else {
printf("read single bolck error:%08x\n", format_R1_response(resp));
return SD_STATE_ERROR;
}
// check CRC;
if (pSD->is_wide_bus) {
uint64_t data_crc;
uint64_t crc;
data_crc = (uint64_t)(__builtin_bswap32(read_crc[0]))<<32|(__builtin_bswap32(read_crc[1]));
crc = calc_4bit_crc16(buff,SDIO_WORDS_PER_BLOCK);
//printf("data_crc:%016llu\n crc:%016llu\n\n", data_crc, crc);
//printf("data_crc:%08x,%08x\n crc:%08x,%08x\n\n", read_crc[0],read_crc[1], (uint32_t)(crc>>32),(uint32_t)crc) ;
if (data_crc != crc) {
printf("crc error:wide bus\n");
return SD_CRC_ERROR;
}
} else {
uint16_t data_crc;
uint16_t crc;
data_crc = (uint16_t)(__builtin_bswap32(read_crc[0])>>16);
crc = calc_1bit_crc16(buff,SDIO_WORDS_PER_BLOCK);
//printf("data_crc:%08x crc:%08x, dd:%08x\n", data_crc, crc, read_crc[0]);
if (data_crc!=crc){
printf("crc error:1 bit\n");
return SD_CRC_ERROR;
}
}
return ret;
}
uint8_t sd_read_multiple_block(sd_memory_card_t* pSD, uint32_t *buff, uint32_t address, uint32_t count) {
uint8_t arg[4];
uint32_t resp[2];
uint32_t read_crc[2*count];
uint8_t ret = SD_OK;
uint32_t crc_len=(pSD->is_wide_bus)?2:1;
arg[0] = address >> 24;
arg[1] = (address & 0x00ff0000) >> 16;
arg[2] = (address & 0x0000ff00) >> 8;
arg[3] = (address & 0x000000ff);
sdio_send_recv_cmd(CMD18, arg, resp, 48); //R1,
if (format_R1_response(resp) == 0x0900)
{
ret = pio_sd_read_blocks(pSD, buff, count, read_crc);
} else {
printf("read multiple error:%08x\n", format_R1_response(resp));
return SD_STATE_ERROR;
}
sd_stop_trans();
// check CRC;
if (pSD->is_wide_bus) {
uint64_t data_crc;
uint64_t crc;
for (int i=0; i < count; i++) {
data_crc = (uint64_t)(__builtin_bswap32(read_crc[2*i]))<<32|(__builtin_bswap32(read_crc[2*i+1]));
crc = calc_4bit_crc16(buff+(i*SDIO_WORDS_PER_BLOCK),SDIO_WORDS_PER_BLOCK);
//printf("data_crc:%016llu\n crc:%016llu\n", data_crc, crc);
if (data_crc != crc) {
printf("crc error: wide bus\n\n");
return SD_CRC_ERROR;
}
}
} else {
uint16_t data_crc;
uint16_t crc;
for (int i=0; i < count; i++) {
data_crc = (uint16_t)(__builtin_bswap32(read_crc[i])>>16);
crc = calc_1bit_crc16(buff+(i*(SDIO_WORDS_PER_BLOCK)),SDIO_WORDS_PER_BLOCK);
//printf("data_crc:%08x crc:%08x\n", data_crc, crc);
if (data_crc!=crc){
printf("crc error: 1bit\n");
return SD_CRC_ERROR;
}
}
}
return ret ;
}
uint8_t sd_write_single_block(sd_memory_card_t* pSD, uint32_t *buff, uint32_t address) {
uint8_t arg[4];
uint32_t resp[2];
uint8_t resp_bytes[6];
uint8_t ret=SD_ERROR_CODE;
//CMD16 SET_BLOCKLEN
memset(arg,0,sizeof(arg));
arg[2]=0x02;
sdio_send_recv_cmd(CMD16, arg, resp, 48); //R1,
arg[0] = address >> 24;
arg[1] = (address & 0x00ff0000) >> 16;
arg[2] = (address & 0x0000ff00) >> 8;
arg[3] = (address & 0x000000ff);
sdio_send_recv_cmd(CMD24, arg, resp, 48); //R1,
// uint32_t count=0;
if (format_R1_response(resp) == 0x0900) {
ret = pio_sd_write_blocks(pSD, buff, 1);
for (int idx=0; idx< 10000;idx++) {
uint32_t ss= get_sd_card_status(pSD);
if (ss == 0x0900) break;
sleep_us(10);
// count++;
};
}
else {
printf("write single error :%08x\n",format_R1_response(resp) );
return SD_STATE_ERROR;
}
//printf("error count:%d\n", count);
return ret;
}
uint8_t sd_write_multiple_block(sd_memory_card_t* pSD, uint32_t *buff, uint32_t address, uint32_t count) {
uint8_t arg[4];
uint32_t resp[2];
uint8_t resp_bytes[6];
uint8_t ret=SD_OK;
//CMD16 SET_BLOCKLEN
memset(arg,0,sizeof(arg));
arg[2]=0x02;
sdio_send_recv_cmd(CMD16, arg, resp, 48); //R1,
// pre-erase
arg[0] = pSD->rca[0];
arg[1] = pSD->rca[1];
sdio_send_recv_cmd(CMD55, arg, resp, 48); // next is APP_CMD
arg[0] = count >> 24;
arg[1] = (count & 0x00ff0000) >> 16;
arg[2] = (count & 0x0000ff00) >> 8;
arg[3] = (count & 0x000000ff);
sdio_send_recv_cmd(ACMD23, arg, resp, 48); //R1, pre-erase blocks
arg[0] = address >> 24;
arg[1] = (address & 0x00ff0000) >> 16;
arg[2] = (address & 0x0000ff00) >> 8;
arg[3] = (address & 0x000000ff);
sdio_send_recv_cmd(CMD25, arg, resp, 48); //R1,
// uint32_t errors=0;
for (int i=0; i < count; i++) {
for (int idx=0; idx < 100000; idx++) { // max 100ms wait for status : READY_FOR_DATA
if (get_sd_card_status(pSD) & 0x0100) {ret=SD_OK;break;}
sleep_us(1);
ret = SD_TIME_OUT;
// errors++;
}
if (ret == SD_OK) {
ret = pio_sd_write_blocks(pSD, buff+i*SDIO_WORDS_PER_BLOCK, 1);
if (ret != SD_OK) break;
} else {
return ret;
}
}
sd_stop_trans();
if (ret != SD_OK) { sleep_ms(1); return ret;}
uint32_t cardStatus;
// errors=0;
for (int timeout=0; timeout<10000;timeout++) { // wait for card ready
cardStatus = get_sd_card_status(pSD);
if (cardStatus == 0x0900) break;
// errors++;
sleep_us(10);
}
//printf("not finish count:%d\n", errors);
// send ACMD22 to get how many blocks well writen, no arg, R1 32bit+CRC
return ret;
}
uint32_t sd_get_total_blocks(sd_memory_card_t* pSD) {
return pSD->total_blocks;
}
uint16_t sd_get_block_size(sd_memory_card_t* pSD) {
return pSD->block_size;
}
/* sdio_memory_disk_initizlize, sdio_memory_disk_read, sdio_memory_disk_write, sdio_memory_disk_status, sdio_memory_disk_ioctl*/
/* sdmmc_disk_initialize*/
DSTATUS sdio_memory_disk_initialize(sd_memory_card_t* pSD, uint8_t wide_bus)
{
DSTATUS stat = sd_memory_card_init(pSD, wide_bus);
return RES_OK;
}
/* sdmmc disk status*/
DSTATUS sdio_memory_disk_status(sd_memory_card_t* pSD)
{
if (pSD->SD_initialized)
return RES_OK;
else
return RES_PARERR;
}
/* sdmmc disk read*/
DSTATUS sdio_memory_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) */
sd_memory_card_t* pSD)
{
DSTATUS ret=RES_OK;
DWORD sect = (DWORD)(sector);
if (!count)
return RES_PARERR; /* Check parameter */
if (!pSD->SD_initialized)
return RES_NOTRDY; /* Check if drive is ready */
if (count == 1)
{
for (int retry=0; retry < CRC_ERROR_RETRY_COUNT; retry++) {
ret = sd_read_single_block(pSD, (uint32_t*)buff, sector);
if (ret == SD_OK) break;//crc error try again
if (ret == SD_CRC_ERROR) continue; else break;
//led_blinking(); //// LED blinking
}
}
else
{ /* Multiple sector read */
for (int retry=0; retry < CRC_ERROR_RETRY_COUNT; retry++) {
ret = sd_read_multiple_block(pSD, (uint32_t*)buff, sect, count);
if (ret == SD_OK) break;//crc error try again
if (ret == SD_CRC_ERROR) continue; else break;
//led_blinking(); //// LED blinking
}
}
//led_blinking_off(); //// LED blinking off
return ret; /* Return result */
}
DSTATUS sdio_memory_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) */
sd_memory_card_t *pSD)
{
DWORD sect = (DWORD)sector;
if (!count)
return RES_PARERR; /* Check parameter */
if (!pSD->SD_initialized)
return RES_NOTRDY; /* Check if drive is ready */
if (count == 1)
{ /* Single block write */
return sd_write_single_block(pSD, (uint32_t*)buff, sect);
//led_blinking(); //// LED_blinking
}
else
{ /* Multiple sector write */
return sd_write_multiple_block(pSD, (uint32_t*)buff, sect, count);
}
return RES_OK; /* Return result */
}
/* sdmmc disk ioctl*/
DSTATUS sdio_memory_disk_ioctl(
BYTE cmd, /* Control command code */
void *buff, /* Pointer to the conrtol data */
sd_memory_card_t* pSD)
{
DRESULT res;
BYTE n, csd[16];
DWORD st, ed, csize;
LBA_t *dp;
BYTE src = 0xFF;
if (!pSD->SD_initialized)
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 (get_sd_card_status(pSD) == 0x0900)
res = RES_OK;
break;
case GET_SECTOR_COUNT: /* Get drive capacity in unit of sector (DWORD) */
*(LBA_t *)buff = *(LBA_t *)sd_get_total_blocks(pSD);
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 = pSD->block_size;
res = RES_OK;
break;
case GET_BLOCK_SIZE: /* Get erase block size in unit of sector (DWORD) */
//printf("get_block_size\n");
/* ACMD13,CMD9 */
res = RES_OK;
break;
case CTRL_TRIM: /* Erase a block of sectors (used when _USE_ERASE == 1) */
//printf("CTL_TRIM\n");
/* CMD32 */
res = RES_OK;
/* Following commands are never used by FatFs module */
break;
case MMC_GET_TYPE: /* Get MMC/SDC type (BYTE) */
//printf("MMC_GET_TYPE\n");
res = RES_OK;
break;
case MMC_GET_CSD: /* Read CSD (16 bytes) CMD9*/
//printf("MMC_GET_CSD\n");
res = RES_OK;
break;
case MMC_GET_CID: /* Read CID (16 bytes) CMD10*/
//printf("MMC_GET_CID\n");
res = RES_OK;
break;
case MMC_GET_OCR: /* Read OCR (4 bytes) CMD58*/
//printf("MMC_GET_OCR\n");
res = RES_OK;
break;
case MMC_GET_SDSTAT: /* Read SD status (64 bytes) ACMD13*/
//printf("MMC_GET_SDSTAT\n");
res = RES_OK;
break;
default:
//printf("default:%d\n",cmd);
res = RES_PARERR;
}
return res;
}
- sdio_mem_card.h
#ifndef __SDIO_MEM_H_
#define __SDIO_MEM_H_
#include "ff.h"
#include "diskio.h"
#include "pico_storage.h"
#define SDIO_CLK_PIN 16 // PIO sideset pin
#define SDIO_CMD_PIN 17 //
#define SDIO_D0_PIN 18 // D0~D3 must consecutive and D0 PIN number is minium
#define SDIO_D1_PIN 19
#define SDIO_D2_PIN 20
#define SDIO_D3_PIN 21
#define SDIO_MEM_PIO pio0
#define SDIO_CMD_SM 0
#define SDIO_DATA_READ_SM 1
#define SDIO_DATA_WRITE_SM 2
#define SDIO_CLK_SM 3
#define _DMA_TRANS_ 1
#define SDIO_WORDS_PER_BLOCK 128
#define SDIO_RW_WORDS_PER_BLOCK 130 //128+2
#define MAX_RW_BLOCKS_TRANS 16
#define CRC_ERROR_RETRY_COUNT 3
typedef struct {
uint8_t rca[2];
uint8_t SD_initialized;
int sd_data_rx_offset;
int sd_data_tx_offset;
int sd_data_cmd_offset;
uint8_t is_wide_bus;
uint8_t pnm[6]; // product name
uint8_t prv; // product revision BCD(4.4)
uint32_t psn; //product serial number
uint16_t block_size;
uint32_t total_blocks;
int dma_write_channel;
int dma_read_channel;
} sd_memory_card_t;
enum {
CLK_FREQ_400K=0,
CLK_FREQ_1M,
CLK_FREQ_12_5M,
CLK_FREQ_25M,
CLK_FREQ_50M,
} SDIO_CLK_FREQ;
enum {
CMD0 = 0,
CMD2 = 2,
CMD3 = 3, //SEND_RELATIVE_ADDR
CMD4 = 4, //SET_DSR
CMD7 = 7, //SELECT_CARD
CMD8 = 8,
CMD9 = 9, //SEND_CSD
CMD10 = 10, //SEND_CID
CMD11 = 11,
CMD12 = 12, //STOP_TRANSMISSION
CMD13 = 13, //SEND_STATUS
CMD16 = 16, // BLOCK LENGTH
CMD17 = 17, // read single block
CMD18 = 18, // read mutiple block
CMD24 = 24, // write single block
CMD25 = 25, // write mutiple block
CMD42 = 42,
CMD55 = 55, // APP_CMD, next is app command
ACMD22 = 22, // SEND_NUM_WR_BLOCKS
ACMD23 = 23, //SET_WR_BLK_ERASE_COUNT
ACMD6 = 6, // SET_BUS_WIDTH, param:00b-1 bit, 10b-4 bits
ACMD41 = 41, //SD_SEND_OP_COND
} SDIO_CMD_LIST;
enum {
IDLE_STATE= 0x01,
ERASE_RESET= 0x02,
ILLEAGAL_COMMAND= 0x04,
CRC_ERROR= 0x08,
ERASE_SEQUENCE_ERROR= 0x10,
ADDRESS_ERROR= 0x20,
PARAMETER_ERROR= 0x40,
} SDIO_R1;
enum {
SD_OK=0,
SD_INIT_FAILURE,
SD_CRC_ERROR,
SD_TIME_OUT,
SD_STATE_ERROR,
SD_READ_ERROR,
SD_WRITE_ERROR,
} SD_ERROR_CODE;
//void sdio_pio_init(PIO pio, uint cmd_pin, uint clk_pin, uint data_pin_base, uint16_t clk_int, uint8_t clk_frac);
uint8_t sd_memory_card_init(sd_memory_card_t* sd_mem_card, uint8_t wide_bus);
uint8_t sd_read_single_block(sd_memory_card_t* pSD,uint32_t *buff, uint32_t address);
uint8_t sd_read_multiple_block(sd_memory_card_t* pSD,uint32_t *buff, uint32_t address, uint32_t count);
uint8_t sd_write_single_block(sd_memory_card_t* pSD,uint32_t *buff, uint32_t address);
uint8_t sd_write_multiple_block(sd_memory_card_t* pSD,uint32_t *buff, uint32_t address, uint32_t count);
uint32_t sd_get_total_blocks(sd_memory_card_t* pSD);
uint16_t sd_get_block_size(sd_memory_card_t* pSD);
DSTATUS sdio_memory_disk_initialize(sd_memory_card_t* pSD, uint8_t wide_bus);
DSTATUS sdio_memory_disk_status(sd_memory_card_t* pSD);
DSTATUS sdio_memory_disk_read(BYTE *buff, LBA_t sector, UINT count, sd_memory_card_t *pSD);
DSTATUS sdio_memory_disk_write(const BYTE *buff, LBA_t sector,UINT count, sd_memory_card_t *pSD);
DSTATUS sdio_memory_disk_ioctl(BYTE cmd, void *buff, sd_memory_card_t* pSD);
#endif
- sdio_mem_card.pio
; SD clock frequency: 125M/(DL+DH+2).
; DL=2, DH=1: 25Mhz
; DL=1, DH=1: 31.25Mhz.
; DL=1, DH=0: 41.67Mhz.
.define DL 1
.define DH 0
; ===cmd state machine===
.program sdio_mem_cmd
.origin 0
.side_set 1
.wrap_target
public sdio_cmd_start:
out null, 32 side 0 [1] ;discard data
out x, 8 side 1 [1] ;command bits
send_cmd_bits:
out pins, 1 side 0 [1]
jmp x--, send_cmd_bits side 1 [1]
set pindirs, 0 side 0 [1]
out x, 8 side 1 [1] ;response bits
jmp !x, sdio_cmd_stop side 0 [1]
wait_recv:
nop side 1 [1]
jmp pin, wait_recv side 0 [1]
recv_resp:
in pins, 1 side 1 [1]
jmp x--, recv_resp side 0 [1]
sdio_cmd_stop:
;nop side 1
set x,7 side 1 [1]
wait_Ncr_Ncc:
nop side 0 [1]
jmp x--, wait_Ncr_Ncc side 1 [1]
push side 0 [1]
wait_shutdown:
jmp wait_shutdown side 0 [1]
;irq nowait 0 side 1
.wrap
;========== 1 bit READ ============
.program sdio_1_bit_rx
.side_set 1
public rx_start:
.wrap_target
wait_start:
mov y,x side 0 [DL]
jmp pin, wait_start side 1 [DH]
nop side 0 [DL]
rx_data_bit:
in PINS, 1 side 1 [DH]
jmp y--, rx_data_bit side 0 [DL]
IN NULL,16 side 0 [DL]
.wrap
;========== 4 bit READ ============
.program sdio_4_bit_rx
.side_set 1
.wrap_target
public rx_start:
wait_start:
mov y,x side 0 [DL]
jmp pin, wait_start side 1 [DH]
nop side 0 [DL]
rx_data_bit:
in PINS, 4 side 1 [DH]
jmp y--, rx_data_bit side 0 [DL]
nop side 0 [DL]
.wrap
;========== 1 bit WRITE ============
.program sdio_1_bit_tx
.side_set 1
.wrap_target
public tx_start:
tx_data_bit:
out PINS, 1 side 0 [DL]
jmp x--, tx_data_bit side 1 [DH]
set pindirs, 0 side 0 [DL]
response_loop:
in PINS, 1 side 1 [DH]
jmp Y--, response_loop side 0 [DL]
nop side 1 [DH]
push side 0 [DL]
.wrap
;========== 4 bit WRITE ============
.program sdio_4_bit_tx
.side_set 1
.wrap_target
public tx_start:
tx_data_bit:
out PINS, 4 side 0 [DL]
jmp x--, tx_data_bit side 1 [DH]
set pindirs, 0 side 0 [DL]
response_loop:
in PINS, 1 side 1 [DH]
jmp Y--, response_loop side 0 [DL]
nop side 1 [DH]
push side 0 [DL]
;nop side 1
.wrap
- CMakeLists.txt(pico_strorage_drv)
add_library(pico_storage_drv INTERFACE)
pico_generate_pio_header(pico_storage_drv ${CMAKE_CURRENT_LIST_DIR}/sdio_mem_card/sdio_mem_card.pio)
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}/spi_sdmmc/spi_sdmmc.c
${CMAKE_CURRENT_LIST_DIR}/flash/W25Q.c
${CMAKE_CURRENT_LIST_DIR}/usb_msc/usb_msc.c
${CMAKE_CURRENT_LIST_DIR}/sdio_mem_card/sdio_mem_card.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}/spi_sdmmc
${CMAKE_CURRENT_LIST_DIR}/usb_msc
${CMAKE_CURRENT_LIST_DIR}/sdio_mem_card
)
target_link_libraries(pico_storage_drv INTERFACE
hardware_spi
hardware_dma
hardware_pio
hardware_rtc
pico_stdlib
tinyusb_board
tinyusb_host
)
- pico_storage.h
#ifndef _PICO_STORAGE_H_
#define _PICO_STORAGE_H_
/* used by my project */
#define SD_4_BIT_PATH "0:"
#define SPI_SDMMC_PATH "1:"
#define SD_1_BIT_PATH "2:"
#define W25Q_PATH "3:"
#define USB_MSC_PATH "4:"
#define SPI_BAUDRATE_LOW (1000*1000)
#define SPI_BAUDRATE_HIGH (10*1000*1000)
/* ================================= */
#define LED_BLINKING_PIN 25
void led_blinking(void);
void led_blinking_off(void);
/* ================================= */
#endif
- glue.c
#include "stdio.h"
#include "stdlib.h"
#include "ff.h"
#include "diskio.h"
#include "sdio_mem_card.h"
#include "spi_sdmmc.h"
#include "W25Q.h"
#include "usb_msc.h"
#include "hardware/rtc.h"
#include "inttypes.h"
#include "hardware/gpio.h"
#define SD_4_BIT_DRV 0
#define SPI_SDMMC_DRV 1
#define SD_1_BIT_DRV 2
#define W25Q_DRV 3
#define USB_MSC_DRV 4
sd_memory_card_t *pSDIO_MEM_CARD_4=NULL;
sd_memory_card_t *pSDIO_MEM_CARD_1=NULL;
spi_sdmmc_data_t *pSPI_SDMMC=NULL;
w25q_data_t *pW25Q = NULL;
usb_msc_t *pUSB_MSC=NULL;
//==================//
DSTATUS disk_initialize (BYTE drv){
DSTATUS stat;
switch (drv) {
case SD_4_BIT_DRV:
if (pSDIO_MEM_CARD_4 == NULL) {
pSDIO_MEM_CARD_4 = (sd_memory_card_t*)malloc(sizeof(sd_memory_card_t));
pSDIO_MEM_CARD_4->SD_initialized=false;
}
stat = sdio_memory_disk_initialize(pSDIO_MEM_CARD_4, true);
return stat;
break;
case SD_1_BIT_DRV:
if (pSDIO_MEM_CARD_1 == NULL) {
pSDIO_MEM_CARD_1 = (sd_memory_card_t*)malloc(sizeof(sd_memory_card_t));
pSDIO_MEM_CARD_1->SD_initialized=false;
}
stat = sdio_memory_disk_initialize(pSDIO_MEM_CARD_1, false);
return stat;
break;
case SPI_SDMMC_DRV:
if (pSPI_SDMMC == NULL) {
pSPI_SDMMC = (spi_sdmmc_data_t*)malloc(sizeof(spi_sdmmc_data_t));
pSPI_SDMMC->csPin = SDMMC_PIN_CS;
pSPI_SDMMC->spiPort = SDMMC_SPI_PORT;
pSPI_SDMMC->spiInit=false;
pSPI_SDMMC->sectSize=512;
#ifdef __SPI_SDMMC_DMA
pSPI_SDMMC->dmaInit=false;
#endif
}
stat = sdmmc_disk_initialize(pSPI_SDMMC);
return stat;
break;
case W25Q_DRV:
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:
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 SD_4_BIT_DRV:
stat = sdio_memory_disk_status(pSDIO_MEM_CARD_4);
return stat;
break;
case SD_1_BIT_DRV:
stat = sdio_memory_disk_status(pSDIO_MEM_CARD_1);
return stat;
break;
case SPI_SDMMC_DRV:
stat= sdmmc_disk_status(pSPI_SDMMC); /* Return disk status */
return stat;
break;
case W25Q_DRV:
stat = pW25Q->Stat;
return stat;
break;
case USB_MSC_DRV:
//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 SD_4_BIT_DRV:
stat = sdio_memory_disk_read(buff, sector, count, pSDIO_MEM_CARD_4);
return stat;
break;
case SD_1_BIT_DRV:
stat = sdio_memory_disk_read(buff, sector, count, pSDIO_MEM_CARD_1);
return stat;
break;
case SPI_SDMMC_DRV:
stat = sdmmc_disk_read(buff, sector, count, pSPI_SDMMC);
return stat;
break;
case W25Q_DRV:
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:
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 SD_4_BIT_DRV:
stat = sdio_memory_disk_write(buff, sector, count, pSDIO_MEM_CARD_4);
return stat;
break;
case SD_1_BIT_DRV:
stat = sdio_memory_disk_write(buff, sector, count, pSDIO_MEM_CARD_1);
return stat;
break;
case SPI_SDMMC_DRV:
stat = sdmmc_disk_write(buff, sector, count, pSPI_SDMMC);
return stat;
break;
case W25Q_DRV:
stat = w25q_disk_write(buff, sector, count, pW25Q);
return stat;
break;
case USB_MSC_DRV:
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 SD_4_BIT_DRV:
stat = sdio_memory_disk_ioctl(cmd, buff, pSDIO_MEM_CARD_4);
return stat;
break;
case SD_1_BIT_DRV:
stat = sdio_memory_disk_ioctl(cmd, buff, pSDIO_MEM_CARD_1);
return stat;
break;
case W25Q_DRV:
stat = w25q_disk_ioctl(cmd, buff, pW25Q);
return stat;
break;
case USB_MSC_DRV:
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)
uint32_t yr = t.year - 1980;
fattime |= (0b01111111 & yr) << 25;
// bit24:21
// Month (1..12)
uint32_t mo = t.month;
fattime |= (0b00001111 & mo) << 21;
// bit20:16
// Day of the month (1..31)
uint32_t da = t.day;
fattime |= (0b00011111 & da) << 16;
// bit15:11
// Hour (0..23)
uint32_t hr = t.hour;
fattime |= (0b00011111 & hr) << 11;
// bit10:5
// Minute (0..59)
uint32_t mi = t.min;
fattime |= (0b00111111 & mi) << 5;
// bit4:0
// Second / 2 (0..29, e.g. 25 for 50)
uint32_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);
}
- main.c
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/dma.h"
#include "hardware/pio.h"
//#include "sdio_mem_card/sdio_mem_card.h"
#include "string.h"
#include "ff.h"
#include "diskio.h"
#include "pico_storage.h"
#include "hardware/rtc.h"
//sd_memory_card_t sd;
void test_data(uint8_t* buff) {
// test write data
uint8_t ch='A';
for (int i=0; i < 512;i++){
buff[i] = ch;
ch++;
if (ch > 'Z') ch='A';
}
memcpy(buff+512, buff, 512);
buff[511]='\n';
buff[0+512] = 'a';
buff[26+512] = 'b';
buff[52+512] = 'c';
buff[512+511]='\n';
memcpy(buff+1024, buff, 512);
buff[0+1024] = '1';
buff[26+1024] = '2';
buff[52+1024] = '3';
buff[1024+511]='\n';
for (uint32_t i = 1536; i< 8192; i+=512)
memcpy(buff+i, buff, 512);
}
uint8_t test_4_bit() {
FATFS fs;
FIL fil_r, fil_w;
FRESULT res;
uint bw, br;
uint8_t buff[16384];
if (f_mount(&fs, SD_4_BIT_PATH, 1) != FR_OK) {
printf("mount error\n");
return 0;
}
printf("mount ok\n");
printf("\n ==============================\ntest under 41.66Mhz SD clock frequency\n");
res = f_open(&fil_r, SD_4_BIT_PATH"/pico_storage.mp4", FA_READ);
if (res != FR_OK) {
printf("open read error:\n");
f_unmount(SD_4_BIT_PATH);
return 0;
}
uint64_t r_out=0;
uint64_t fsize=f_size(&fil_r);
uint32_t i=0;
f_close(&fil_r);
printf("file size:%llu\n",fsize);
uint16_t buff_size[4] = {1024, 4096, 8192, 12288};
for (int r=0; r<4; r++) {
res = f_open(&fil_r, SD_4_BIT_PATH"/pico_storage.mp4", FA_READ);
if (res != FR_OK) {
printf("open read error:\n");
f_unmount(SD_4_BIT_PATH);
return 0;
}
r_out=0;
printf("Test file read speed(SD-4bit): using %dK buffer\n", buff_size[r]/1024);
absolute_time_t tt=get_absolute_time();
do {
res = f_read(&fil_r, buff, buff_size[r], &br);
if (res != FR_OK) {
printf("read error:%d\n", res);
break;
}
r_out += br;
} while(r_out < fsize);
int64_t et=absolute_time_diff_us(tt, get_absolute_time())/1000;
double dd = (double)(r_out)/et*1000/1024/1024;
printf("total time:%llu ms, total_read:%d bytes, = %0.2lfMB/s\n", et, r_out, dd);
printf("--------------\n\n");
f_close(&fil_r);
}
printf("\n======================\n");
printf("Test file write speed(SD-4bit): using 8K buffer\n");
test_data(buff);
res = f_open(&fil_w, SD_4_BIT_PATH"/8k_4_buff.txt", FA_CREATE_ALWAYS|FA_WRITE);
if (res != FR_OK) {
printf("open file error:\n");
f_unmount(SD_4_BIT_PATH);
return 0;
}
absolute_time_t tw=get_absolute_time();
for (int s=0; s < 8192;s++) { // 64MB
f_write(&fil_w, buff, 8192, &bw);
//printf("write:%d\n", bw);
}
int64_t et=absolute_time_diff_us(tw, get_absolute_time())/1000;
double dd = (double)(8192*8192)/et*1000/1024/1024;
printf("total time:%llu ms, total_write:%d bytes, = %0.2lfMB/s\n", et, 8192*8192, dd);
f_close(&fil_w);
printf("\n======================\n");
printf("Test file read in and write out speed(SD-4bit): using 8K buffer\n");
res = f_open(&fil_r, SD_4_BIT_PATH"/pico_storage.mp4", FA_READ);
if (res != FR_OK) {
printf("open read error:\n");
f_unmount(SD_4_BIT_PATH);
return 0;
}
res = f_open(&fil_w, SD_4_BIT_PATH"/test_out_4.mp4", FA_CREATE_ALWAYS|FA_WRITE);
if (res != FR_OK) {
printf("open file error:\n");
f_unmount(SD_4_BIT_PATH);
return 0;
}
r_out=0;
absolute_time_t tt=get_absolute_time();
do {
res = f_read(&fil_r, buff, 8192, &br);
if (res != FR_OK) {
printf("read error:%d\n", res);
break;
}
f_write(&fil_w, buff, br, &bw);
if (res != FR_OK) {
printf("write error:%d\n", res);
break;
}
r_out += br;
//if ((i++%100)==0) {
//printf("read bytes:%d\n", br);
// printf("write :%llu\n", r_out);
//}
} while(r_out < fsize);
printf("total time:%llu ms, total_read:%d bytes\n", absolute_time_diff_us(tt, get_absolute_time())/1000, r_out);
f_close(&fil_r);
f_close(&fil_w);
printf("\n#=============#\n");
f_unmount(SD_4_BIT_PATH);
}
uint8_t test_1_bit() {
FATFS fs;
FIL fil_r, fil_w;
FRESULT res;
uint bw, br;
uint8_t buff[16384];
if (f_mount(&fs, SD_1_BIT_PATH, 1) != FR_OK) {
printf("mount error\n");
return 0;
}
printf("mount ok\n");
printf("\n ==============================\ntest under 41.66Mhz SD clock frequency\n");
res = f_open(&fil_r, SD_1_BIT_PATH"/pico_storage.mp4", FA_READ);
if (res != FR_OK) {
printf("open read error:\n");
f_unmount(SD_1_BIT_PATH);
return 0;
}
uint64_t r_out=0;
uint64_t fsize=f_size(&fil_r);
uint32_t i=0;
f_close(&fil_r);
printf("file size:%llu\n",fsize);
uint16_t buff_size[4] = {1024, 4096, 8192, 12288};
for (int r=0; r<4; r++) {
res = f_open(&fil_r, SD_1_BIT_PATH"/pico_storage.mp4", FA_READ);
if (res != FR_OK) {
printf("open read error:\n");
f_unmount(SD_1_BIT_PATH);
return 0;
}
r_out=0;
printf("Test file read speed(SD-1bit): using %dK buffer\n", buff_size[r]/1024);
absolute_time_t tt=get_absolute_time();
do {
res = f_read(&fil_r, buff, buff_size[r], &br);
if (res != FR_OK) {
printf("read error:%d\n", res);
break;
}
r_out += br;
} while(r_out < fsize);
int64_t et=absolute_time_diff_us(tt, get_absolute_time())/1000;
double dd = (double)(r_out)/et*1000/1024/1024;
printf("total time:%llu ms, total_read:%d bytes, = %0.2lfMB/s\n", et, r_out, dd);
printf("--------------\n\n");
f_close(&fil_r);
}
printf("\n======================\n");
printf("Test file write speed(SD-1bit): using 8K buffer\n");
test_data(buff);
res = f_open(&fil_w, SD_1_BIT_PATH"/8k_1_buff.txt", FA_CREATE_ALWAYS|FA_WRITE);
if (res != FR_OK) {
printf("open file error:\n");
f_unmount(SD_1_BIT_PATH);
return 0;
}
absolute_time_t tw=get_absolute_time();
for (int s=0; s < 8192;s++) { // 64MB
f_write(&fil_w, buff, 8192, &bw);
}
int64_t et=absolute_time_diff_us(tw, get_absolute_time())/1000;
double dd = (double)(8192*8192)/et*1000/1024/1024;
printf("total time:%llu ms, total_write:%d bytes, = %0.2lfMB/s\n", et, 8192*8192, dd);
f_close(&fil_w);
printf("\n======================\n");
printf("Test file read in and write out speed(SD-1bit): using 8K buffer\n");
res = f_open(&fil_r, SD_1_BIT_PATH"/pico_storage.mp4", FA_READ);
if (res != FR_OK) {
printf("open read error:\n");
f_unmount(SD_1_BIT_PATH);
return 0;
}
res = f_open(&fil_w, SD_1_BIT_PATH"/test_out_1.mp4", FA_CREATE_ALWAYS|FA_WRITE);
if (res != FR_OK) {
printf("open file error:\n");
f_unmount(SD_1_BIT_PATH);
return 0;
}
r_out=0;
absolute_time_t tt=get_absolute_time();
do {
res = f_read(&fil_r, buff, 8192, &br);
if (res != FR_OK) {
printf("read error:%d\n", res);
break;
}
f_write(&fil_w, buff, br, &bw);
if (res != FR_OK) {
printf("write error:%d\n", res);
break;
}
r_out += br;
//if ((i++%100)==0) {
//printf("read bytes:%d\n", br);
// printf("write :%llu\n", r_out);
//}
} while(r_out < fsize);
printf("total time:%llu ms, total_read:%d bytes\n", absolute_time_diff_us(tt, get_absolute_time())/1000, r_out);
f_close(&fil_r);
f_close(&fil_w);
printf("\n#=============#\n");
f_unmount(SD_1_BIT_PATH);
}
int main()
{
stdio_init_all();
printf("start\n");
datetime_t t = {2023, 10, 20, 5, 3, 0, 0};
rtc_set_datetime(&t);
sleep_ms(1000);
test_1_bit();
test_4_bit();
while(1);
puts("Hello, world!");
return 0;
}
- CMakeListx.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(SDIO_MEM 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(SDIO_MEM main.c)
pico_set_program_name(SDIO_MEM "SDIO_MEM")
pico_set_program_version(SDIO_MEM "0.1")
pico_enable_stdio_uart(SDIO_MEM 1)
pico_enable_stdio_usb(SDIO_MEM 0)
# Add the standard library to the build
target_link_libraries(SDIO_MEM
pico_stdlib)
# Add the standard include files to the build
target_include_directories(SDIO_MEM PRIVATE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
)
# Add any user requested libraries
#target_link_libraries(SDIO_MEM
# hardware_dma
# hardware_pio
# )
add_subdirectory(pico_storage_drv)
# Add any user requested libraries
target_link_libraries(SDIO_MEM
tinyusb_host
tinyusb_board
pico_storage_drv
)
pico_add_extra_outputs(SDIO_MEM)
沒有留言:
張貼留言