/* * (c) Domen Puncer, Visionect, d.o.o. * BSD License */ #include #include "stm32f10x_lib.h" /* * Code is split into 3 parts: * - generic SPI code: adapt for your MCU * - sd card code, with crc7 and crc16 calculations * there's lots of it, but it's simple * - fatfs interface. If you use anything else, look here for * interface to SD card code */ struct hwif { int initialized; int sectors; int erase_sectors; }; typedef struct hwif hwif; enum sd_speed { SD_SPEED_INVALID, SD_SPEED_400KHZ, SD_SPEED_25MHZ }; /*** spi functions ***/ void spi_set_speed(enum sd_speed speed); /* for real worksense board SD card is connected to SPI1, PA4-7 */ void spi_init(void) { GPIO_InitTypeDef gpio; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); gpio.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; gpio.GPIO_Speed = GPIO_Speed_50MHz; gpio.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &gpio); gpio.GPIO_Pin = GPIO_Pin_4; gpio.GPIO_Speed = GPIO_Speed_50MHz; gpio.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOA, &gpio); spi_set_speed(SD_SPEED_400KHZ); } #define spi_cs_low() do { GPIOA->BRR = GPIO_Pin_4; } while (0) #define spi_cs_high() do { GPIOA->BSRR = GPIO_Pin_4; } while (0) void spi_set_speed(enum sd_speed speed) { SPI_InitTypeDef spi; int prescaler = SPI_BaudRatePrescaler_128; if (speed == SD_SPEED_400KHZ) prescaler = SPI_BaudRatePrescaler_128; else if (speed == SD_SPEED_25MHZ) prescaler = SPI_BaudRatePrescaler_2; /* ^ with /2 APB1 this will be 15mhz/234k at 60mhz * 18/281 at 72. which is ok, 100DR = data; while ((SPI_SD->SR & SPI_I2S_FLAG_RXNE) == 0) ; return SPI_SD->DR; } /* crc helpers */ u8 crc7_one(u8 t, u8 data) { int i; const u8 g = 0x89; t ^= data; for (i=0; i<8; i++) { if (t & 0x80) t ^= g; t <<= 1; } return t; } u8 crc7(const u8 *p, int len) { int j; u8 crc = 0; for (j=0; j>1; } /* http://www.eagleairaust.com.au/code/crc16.htm */ u16 crc16_ccitt(u16 crc, u8 ser_data) { crc = (u8)(crc >> 8) | (crc << 8); crc ^= ser_data; crc ^= (u8)(crc & 0xff) >> 4; crc ^= (crc << 8) << 4; crc ^= ((crc & 0xff) << 4) << 1; return crc; } u16 crc16(const u8 *p, int len) { int i; u16 crc = 0; for (i=0; i> 24); crc = crc7_one(crc, arg >> 24); spi_txrx(arg >> 16); crc = crc7_one(crc, arg >> 16); spi_txrx(arg >> 8); crc = crc7_one(crc, arg >> 8); spi_txrx(arg); crc = crc7_one(crc, arg); //spi_txrx(0x95); /* crc7, for cmd0 */ spi_txrx(crc | 0x1); /* crc7, for cmd0 */ } u8 sd_get_r1() { int tries = 1000; u8 r; while (tries--) { r = spi_txrx(0xff); if ((r & 0x80) == 0) return r; } return 0xff; } u16 sd_get_r2() { int tries = 1000; u16 r; while (tries--) { r = spi_txrx(0xff); if ((r & 0x80) == 0) break; } if (tries < 0) return 0xff; r = r<<8 | spi_txrx(0xff); return r; } /* * r1, then 32-bit reply... same format as r3 */ u8 sd_get_r7(u32 *r7) { u32 r; r = sd_get_r1(); if (r != 0x01) return r; r = spi_txrx(0xff) << 24; r |= spi_txrx(0xff) << 16; r |= spi_txrx(0xff) << 8; r |= spi_txrx(0xff); *r7 = r; return 0x01; } static const char *r1_strings[7] = { "in idle", "erase reset", "illegal command", "communication crc error", "erase sequence error", "address error", "parameter error" }; void print_r1(u8 r) { int i; printf("R1: %02x\n", r); for (i=0; i<7; i++) if (r & (1<=4; i--) if (r3 & 1<>30 & 1, r3>>31); printf("success\n"); } printf("acmd41 - hcs.. "); tries = 1000; /* needs to be polled until in_idle_state becomes 0 */ do { /* send we don't support SDHC */ spi_cs_low(); /* next cmd is ACMD */ sd_cmd(55, 0); r = sd_get_r1(); sd_nec(); spi_cs_high(); if (r == 0xff) goto err_spi; /* well... it's probably not idle here, but specs aren't clear */ if (r & 0xfe) { printf("fail\n"); print_r1(r); goto err; } spi_cs_low(); sd_cmd(41, 0); /* 0x40000000 would be HCS */ r = sd_get_r1(); sd_nec(); spi_cs_high(); if (r == 0xff) goto err_spi; if (r & 0xfe) { printf("fail\n"); print_r1(r); goto err; } } while (r != 0 && tries--); if (tries == -1) { printf("timeouted\n"); goto err; } printf("success\n"); printf("cmd16 - block length.. "); spi_cs_low(); sd_cmd(16, 512); r = sd_get_r1(); sd_nec(); spi_cs_high(); if (r == 0xff) goto err_spi; if (r & 0xfe) { printf("fail\n"); print_r1(r); goto err; } printf("success\n"); printf("cmd59 - enable crc.. "); /* crc on */ spi_cs_low(); sd_cmd(59, 0); r = sd_get_r1(); sd_nec(); spi_cs_high(); if (r == 0xff) goto err_spi; if (r & 0xfe) { printf("fail\n"); print_r1(r); goto err; } printf("success\n"); /* now we can up the clock to <= 25 MHz */ spi_set_speed(SD_SPEED_25MHZ); return 0; err_spi: printf("fail spi\n"); return -1; err: return -2; } int sd_read_status(hwif *hw) { u16 r2; spi_cs_low(); sd_cmd(13, 0); r2 = sd_get_r2(); sd_nec(); spi_cs_high(); if (r2 & 0x8000) return -1; if (r2) print_r2(r2); return 0; } /* 0xfe marks data start, then len bytes of data and crc16 */ int sd_get_data(hwif *hw, u8 *buf, int len) { int tries = 20000; u8 r; u16 _crc16; u16 calc_crc; int i; while (tries--) { r = spi_txrx(0xff); if (r == 0xfe) break; } if (tries < 0) return -1; for (i=0; i>8); spi_txrx(crc); /* normally just one dummy read in between... specs don't say how many */ while (tries--) { b[bi++] = r = spi_txrx(0xff); if (r != 0xff) break; } if (tries < 0) return -1; /* poll busy, about 300 reads for 256 MB card */ tries = 100000; while (tries--) { if (spi_txrx(0xff) == 0xff) break; } if (tries < 0) return -2; /* data accepted, WIN */ if ((r & 0x1f) == 0x05) return 0; return r; } int sd_read_csd(hwif *hw) { u8 buf[16]; int r; int capacity; spi_cs_low(); sd_cmd(9, 0); r = sd_get_r1(); if (r == 0xff) { spi_cs_high(); return -1; } if (r & 0xfe) { spi_cs_high(); printf("%s ", __func__); print_r1(r); return -2; } r = sd_get_data(hw, buf, 16); sd_nec(); spi_cs_high(); if (r == -1) { printf("failed to get csd\n"); return -3; } printf("CSD: CSD v%i taac:%02x, nsac:%i, tran:%02x, classes:%02x%x, read_bl_len:%i, " "read_bl_part:%i, write_blk_misalign:%i, read_blk_misalign:%i, dsr_imp:%i, " "c_size:%i, vdd_rmin:%i, vdd_rmax:%i, vdd_wmin:%i, vdd_wmax:%i, " "c_size_mult:%i, erase_blk_en:%i, erase_s_size:%i, " "wp_grp_size:%i, wp_grp_enable:%i, r2w_factor:%i, write_bl_len:%i, write_bl_part:%i, " "filef_gpr:%i, copy:%i, perm_wr_prot:%i, tmp_wr_prot:%i, filef:%i\n", (buf[0] >> 6) + 1, buf[1], buf[2], buf[3], buf[4], buf[5] >> 4, 1<<(buf[5] & 0xf), /* classes, read_bl_len */ buf[6]>>7, (buf[6]>>6)&1, (buf[6]>>5)&1, (buf[6]>>4)&1, (buf[6]&0x3)<<10 | buf[7]<<2 | buf[8]>>6, /* c_size */ (buf[8]&0x38)>>3, buf[8]&0x07, buf[9]>>5, (buf[9]>>2)&0x7, 1<<(2+(((buf[9]&3) << 1) | buf[10]>>7)), /* c_size_mult */ (buf[10]>>6)&1, ((buf[10]&0x3f)<<1 | buf[11]>>7) + 1, /* erase sector size */ (buf[11]&0x7f) + 1, /* write protect group size */ buf[12]>>7, 1<<((buf[12]>>2)&7), 1<<((buf[12]&3)<<2 | buf[13]>>6), /* write_bl_len */ (buf[13]>>5)&1, buf[14]>>7, (buf[14]>>6)&1, (buf[14]>>5)&1, (buf[14]>>4)&1, (buf[14]>>2)&3 /* file format */); capacity = (((buf[6]&0x3)<<10 | buf[7]<<2 | buf[8]>>6)+1) << (2+(((buf[9]&3) << 1) | buf[10]>>7)) << (buf[5] & 0xf); /* ^ = (c_size+1) * 2**(c_size_mult+2) * 2**read_bl_len */ printf("capacity = %i\n", capacity); hw->sectors = capacity/512; /* if erase_blk_en = 0, then only this many sectors can be erased at once * this is NOT yet tested */ hw->erase_sectors = 1; if (((buf[10]>>6)&1) == 0) hw->erase_sectors = ((buf[10]&0x3f)<<1 | buf[11]>>7) + 1; return 0; } int sd_read_cid(hwif *hw) { u8 buf[16]; int r; spi_cs_low(); sd_cmd(10, 0); r = sd_get_r1(); if (r == 0xff) { spi_cs_high(); return -1; } if (r & 0xfe) { spi_cs_high(); printf("%s ", __func__); print_r1(r); return -2; } r = sd_get_data(hw, buf, 16); sd_nec(); spi_cs_high(); if (r == -1) { printf("failed to get cid\n"); return -3; } printf("CID: mid:%x, oid:%c%c, pnm:%c%c%c%c%c, prv:%i.%i, psn:%02x%02x%02x%02x, mdt:%i/%i\n", buf[0], buf[1], buf[2], /* mid, oid */ buf[3], buf[4], buf[5], buf[6], buf[7], /* pnm */ buf[8] >> 4, buf[8] & 0xf, /* prv */ buf[9], buf[10], buf[11], buf[12], /* psn */ 2000 + (buf[13]<<4 | buf[14]>>4), 1 + (buf[14] & 0xf)); return 0; } int sd_readsector(hwif *hw, u32 address, u8 *buf) { int r; spi_cs_low(); sd_cmd(17, address*512); /* read single block */ r = sd_get_r1(); if (r == 0xff) { spi_cs_high(); r = -1; goto fail; } if (r & 0xfe) { spi_cs_high(); printf("%s\n", __func__); print_r1(r); r = -2; goto fail; } r = sd_get_data(hw, buf, 512); sd_nec(); spi_cs_high(); if (r == -1) { r = -3; goto fail; } return 0; fail: printf("failed to read sector %li, err:%i\n", address, r); return r; } int sd_writesector(hwif *hw, u32 address, const u8 *buf) { int r; spi_cs_low(); sd_cmd(24, address*512); /* write block */ r = sd_get_r1(); if (r == 0xff) { spi_cs_high(); r = -1; goto fail; } if (r & 0xfe) { spi_cs_high(); printf("%s\n", __func__); print_r1(r); r = -2; goto fail; } spi_txrx(0xff); /* Nwr (>= 1) high bytes */ r = sd_put_data(hw, buf, 512); sd_nec(); spi_cs_high(); if (r != 0) { printf("sd_put_data returned: %i\n", r); r = -3; goto fail; } /* efsl code is weird shit, 0 is error in there? * not that it's properly handled or anything, * and the return type is char, fucking efsl */ return 0; fail: printf("failed to write sector %li, err:%i\n", address, r); return r; } /*** public API - on top of sd/spi code ***/ int hwif_init(hwif* hw) { int tries = 10; if (hw->initialized) return 0; spi_init(); while (tries--) { if (sd_init(hw) == 0) break; } if (tries == -1) return -1; /* read status register */ sd_read_status(hw); sd_read_cid(hw); if (sd_read_csd(hw) != 0) return -1; hw->initialized = 1; return 0; } int sd_read(hwif* hw, u32 address, u8 *buf) { int r; int tries = 10; r = sd_readsector(hw, address, buf); while (r < 0 && tries--) { if (sd_init(hw) != 0) continue; /* read status register */ sd_read_status(hw); r = sd_readsector(hw, address, buf); } if (tries == -1) printf("%s: couldn't read sector %li\n", __func__, address); return r; } int sd_write(hwif* hw, u32 address,const u8 *buf) { int r; int tries = 10; r = sd_writesector(hw, address, buf); while (r < 0 && tries--) { if (sd_init(hw) != 0) continue; /* read status register */ sd_read_status(hw); r = sd_writesector(hw, address, buf); } if (tries == -1) printf("%s: couldn't write sector %li\n", __func__, address); return r; } /*** fatfs code that uses the public API ***/ #include "diskio.h" hwif hw; DSTATUS disk_initialize(BYTE drv) { if (hwif_init(&hw) == 0) return 0; return STA_NOINIT; } DSTATUS disk_status(BYTE drv) { if (hw.initialized) return 0; return STA_NOINIT; } DRESULT disk_read(BYTE drv, BYTE *buff, DWORD sector, BYTE count) { int i; for (i=0; i