diff --git a/mendel.c b/mendel.c index 1b02b98..759cf73 100644 --- a/mendel.c +++ b/mendel.c @@ -266,20 +266,48 @@ int main (void) init(); #ifdef SD - // This demonstrates SPI. Gives a nice dance on the scope. - // Will go away as soon as we can do something useful with the SD card. + // This demonstrates SD card reading. It simply lists the top level + // directory over and over again. This demo will go away as soon as we + // have the G-code implementations to do something useful in place. #include "delay.h" - uint8_t c = 0; - while (1) { - // Trigger signal for the scope. - WRITE(STEPPER_ENABLE_PIN, 0); - delay_us(10); - WRITE(STEPPER_ENABLE_PIN, 1); - spi_rw(c); - c++; - delay_ms(50); + while (1) { + FATFS sdfile; + FRESULT res; + + if ((res = pf_mount(&sdfile)) == FR_OK) + serial_writestr_P(PSTR("SD card mounted successfully.\n")); + else { + serial_writestr_P(PSTR("Mounting SD card failed :-/\n")); + delay_ms(10); + if (res == FR_NOT_READY) + serial_writestr_P(PSTR("No medium or hardware error.\n")); + else if (res == FR_DISK_ERR) + serial_writestr_P(PSTR("Error while reading.\n")); + else if (res == FR_NO_FILESYSTEM) + serial_writestr_P(PSTR("No valid FAT partition.\n")); + } + + // Inspired by http://elm-chan.org/fsw/ff/pf/readdir.html. + FILINFO fno; + DIR dir; + char* path ="/"; + + res = pf_opendir(&dir, path); + if (res == FR_OK) { + for (;;) { + res = pf_readdir(&dir, &fno); + if (res != FR_OK || fno.fname[0] == 0) + break; + serial_writestr((uint8_t *)path); + serial_writestr((uint8_t *)fno.fname); + serial_writechar('\n'); + delay_ms(2); // Time for sending the characters. + } + } + delay_ms(5000); } + #endif // main loop diff --git a/pff.c b/pff.c index 52deffd..d7477d7 100644 --- a/pff.c +++ b/pff.c @@ -38,6 +38,8 @@ file name conflicts. - Changed former integer.h content to use fixed width types (int8_t, etc). - Wrapped code in #ifdef SD to compile it only when needed. + - Added the hardware connection layer from sample code of the same source, + see pff_diskio.c. */ #include "pff.h" /* Petit FatFs configurations and declarations */ diff --git a/pff_conf.h b/pff_conf.h index dd4d1f2..59ec26b 100644 --- a/pff_conf.h +++ b/pff_conf.h @@ -11,7 +11,7 @@ /---------------------------------------------------------------------------*/ #define _USE_READ 1 /* Enable pf_read() function */ -#define _USE_DIR 0 /* Enable pf_opendir() and pf_readdir() function */ +#define _USE_DIR 1 /* Enable pf_opendir() and pf_readdir() function */ #define _USE_LSEEK 0 /* Enable pf_lseek() function */ #define _USE_WRITE 0 /* Enable pf_write() function */ diff --git a/pff_diskio.c b/pff_diskio.c index 79e4214..c91664e 100644 --- a/pff_diskio.c +++ b/pff_diskio.c @@ -1,5 +1,11 @@ /*-----------------------------------------------------------------------*/ /* Low level disk I/O module skeleton for Petit FatFs (C)ChaN, 2014 */ +/* */ +/* ... completed by ... */ +/* */ +/* PFF - Low level disk control module for AVR (C)ChaN, 2014 */ +/* File avr_mmcp.c, part of avr/ in pffsample, a package with sample */ +/* implementations on http://elm-chan.org/fsw/ff/00index_p.html */ /*-----------------------------------------------------------------------*/ /* Changes for Teacup see pff.c. */ @@ -7,72 +13,238 @@ #ifdef SD +#include +#include "spi.h" +#include "delay.h" -/*-----------------------------------------------------------------------*/ -/* Initialize Disk Drive */ -/*-----------------------------------------------------------------------*/ +/* Definitions for MMC/SDC command. */ +#define CMD0 (0x40 + 0) /* GO_IDLE_STATE */ +#define CMD1 (0x40 + 1) /* SEND_OP_COND (MMC) */ +#define ACMD41 (0xC0 + 41) /* SEND_OP_COND (SDC) */ +#define CMD8 (0x40 + 8) /* SEND_IF_COND */ +#define CMD16 (0x40 + 16) /* SET_BLOCKLEN */ +#define CMD17 (0x40 + 17) /* READ_SINGLE_BLOCK */ +#define CMD24 (0x40 + 24) /* WRITE_BLOCK */ +#define CMD55 (0x40 + 55) /* APP_CMD */ +#define CMD58 (0x40 + 58) /* READ_OCR */ -DSTATUS disk_initialize (void) -{ - DSTATUS stat; +/* Card type flags (card_type). */ +#define CT_MMC 0x01 /* MMC ver 3 */ +#define CT_SD1 0x02 /* SD ver 1 */ +#define CT_SD2 0x04 /* SD ver 2 */ +#define CT_BLOCK 0x08 /* Block addressing */ - // Put your code here - return stat; +static BYTE card_type; /* Card type flags. */ + + +/** Send a command packet to MMC/SD card. + + \param cmd Command. + \param arg Argument (32 bit). + + \return response byte (bit 7 == 1: send failed). +*/ +static BYTE send_cmd(BYTE cmd, DWORD arg) { + BYTE n, res; + + if (cmd & 0x80) { /* ACMD is the command sequence of CMD55-CMD. */ + cmd &= 0x7F; + res = send_cmd(CMD55, 0); + if (res > 1) + return res; + } + + /* Select the card */ + spi_deselect_sd(); + spi_rw(0xFF); + spi_select_sd(); + spi_rw(0xFF); + + /* Send command packet */ + spi_rw(cmd); /* Start + Command index */ + spi_rw((BYTE)(arg >> 24)); /* Argument[31..24] */ + spi_rw((BYTE)(arg >> 16)); /* Argument[23..16] */ + spi_rw((BYTE)(arg >> 8)); /* Argument[15..8] */ + spi_rw((BYTE)arg); /* Argument[7..0] */ + n = 0x01; /* Dummy CRC + Stop */ + if (cmd == CMD0) n = 0x95; /* Valid CRC for CMD0(0) + Stop */ + if (cmd == CMD8) n = 0x87; /* Valid CRC for CMD8(0x1AA) Stop */ + spi_rw(n); + + /* Receive command response */ + n = 10; /* Wait for a response, try 10 times. */ + do { + res = spi_rw(0xFF); + n--; + } while ((res & 0x80) && n); + + return res; } +/** Initialize disk drive. + \return 0 on success, else STA_NOINIT. -/*-----------------------------------------------------------------------*/ -/* Read Partial Sector */ -/*-----------------------------------------------------------------------*/ + Initialize disk drive and also find out which kind of card we're talking to. -DRESULT disk_readp ( - BYTE* buff, /* Pointer to the destination object */ - DWORD sector, /* Sector number (LBA) */ - UINT offset, /* Offset in the sector */ - UINT count /* Byte count (bit15:destination) */ -) -{ - DRESULT res; + Description about what's going on here see + http://elm-chan.org/docs/mmc/mmc_e.html, center section. +*/ +DSTATUS disk_initialize(void) { + BYTE n, cmd, ty, ocr[4]; + UINT timeout; - // Put your code here + spi_deselect_sd(); + spi_speed_100_400(); + for (n = 10; n; n--) /* 80 dummy clocks with CS = high. */ + spi_rw(0xFF); - return res; -} + /* Find card type: MMCv3, SDv1 or SDv2. */ + ty = 0; + if (send_cmd(CMD0, 0) == 1) { /* Software reset, enter idle state. */ + if (send_cmd(CMD8, 0x1AA) == 1) { /* SDv2? (rejected on others) */ + for (n = 0; n < 4; n++) /* Get trailing return value of R7. */ + ocr[n] = spi_rw(0xFF); + if (ocr[2] == 0x01 && ocr[3] == 0xAA) { + /* The card can work at Vdd range of 2.7 - 3.6V. */ + /* Wait for leaving idle state (ACMD41 with HCS bit). */ + for (timeout = 10000; timeout && send_cmd(ACMD41, 1UL << 30); timeout--) + delay_us(100); -/*-----------------------------------------------------------------------*/ -/* Write Partial Sector */ -/*-----------------------------------------------------------------------*/ - -DRESULT disk_writep ( - const BYTE* buff, /* Pointer to the data to be written, - NULL:Initiate/Finalize write operation */ - DWORD sc /* Sector number (LBA) or Number of bytes to send */ -) -{ - DRESULT res; - - - if (!buff) { - if (sc) { - - // Initiate write process - - } else { - - // Finalize write process - + /* Find out wether it's a block device (CCS bit in OCR). */ + if (timeout && send_cmd(CMD58, 0) == 0) { + for (n = 0; n < 4; n++) + ocr[n] = spi_rw(0xFF); + ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* SDv2 */ } - } else { + } + } + else { /* SDv1 or MMCv3 */ + if (send_cmd(ACMD41, 0) <= 1) { + ty = CT_SD1; cmd = ACMD41; /* SDv1 */ + } + else { + ty = CT_MMC; cmd = CMD1; /* MMCv3 */ + } - // Send data to the disk + /* Wait for leaving idle state. */ + for (timeout = 10000; timeout && send_cmd(cmd, 0); timeout--) + delay_us(100); + + /* Set R/W block length to 512. */ + if ( ! timeout || send_cmd(CMD16, 512) != 0) + ty = 0; + } + } + + card_type = ty; + spi_deselect_sd(); + spi_rw(0xFF); + + return ty ? 0 : STA_NOINIT; +} + +/** Read partial sector. + + \param buffer Received data should go in here. + \param sector Sector number (LBA). + \param offset Offset into the sector. + \param count Byte count (bit 15: destination). + + \return RES_OK on success, else RES_ERROR. + + This is the main reading function. Forming this into directory listings, + file reads and such stuff is done by Petit FatFs it's self. + + Description about what's going on here see + http://elm-chan.org/docs/mmc/mmc_e.html, bottom section. +*/ +#if _USE_READ +DRESULT disk_readp(BYTE* buffer, DWORD sector, UINT offset, UINT count) { + DRESULT result; + BYTE token = 0xFF; + uint16_t timeout; + UINT trailing = 514 - offset - count; /* 514 = block size + 2 bytes CRC */ + + /* Convert to byte address on non-block cards. */ + if ( ! (card_type & CT_BLOCK)) + sector *= 512; + + /* Read one sector, copy only as many bytes as required. */ + result = RES_ERROR; + spi_speed_max(); + if (send_cmd(CMD17, sector) == 0) { /* Read single block. */ + /* Wait for data packet. */ + for (timeout = 40000; timeout && (token == 0xFF); timeout--) + token = spi_rw(0xFF); + + if (token == 0xFE) { /* Valid data arrived. */ + /* Skip leading bytes. */ + while (offset--) + spi_rw(0xFF); + + /* Receive the requested part of the sector. */ + do { + *buffer++ = spi_rw(0xFF); + } while (--count); + + /* Skip trailing bytes and CRC. */ + while (trailing--) + spi_rw(0xFF); + + result = RES_OK; + } + } + + spi_deselect_sd(); /* Every send_cmd() selects. */ + spi_rw(0xFF); + + return result; +} +#endif /* _USE_READ */ + +/** Write partial pector. + + \param buff Pointer to the data to be written. + NULL: Initiate/Finalize write operation. + \param sc Sector number (LBA) or number of bytes to send. + + \return RES_OK on success, else RES_ERROR. + + This is the main writing function. Forming this into file writes and such + stuff is done by Petit FatFs it's self. + + Description about what's going on here see + http://elm-chan.org/docs/mmc/mmc_e.html, bottom section. +*/ +#if _USE_WRITE +DRESULT disk_writep(const BYTE* buff, DWORD sc) { + #error disk_writep() not yet implemented + DRESULT res; + + if ( ! buff) { + if (sc) { + + // Initiate write process } + else { - return res; + // Finalize write process + + } + } + else { + + // Send data to the disk + + } + + return res; } +#endif /* _USE_WRITE */ #endif /* SD */ diff --git a/pff_diskio.h b/pff_diskio.h index e471115..5546a3b 100644 --- a/pff_diskio.h +++ b/pff_diskio.h @@ -34,8 +34,12 @@ typedef enum { /* Prototypes for disk control functions */ DSTATUS disk_initialize (void); -DRESULT disk_readp (BYTE* buff, DWORD sector, UINT offser, UINT count); -DRESULT disk_writep (const BYTE* buff, DWORD sc); +#if _USE_READ + DRESULT disk_readp (BYTE* buffer, DWORD sector, UINT offset, UINT count); +#endif +#if _USE_WRITE + DRESULT disk_writep (const BYTE* buffer, DWORD sc); +#endif #define STA_NOINIT 0x01 /* Drive not initialized */ #define STA_NODISK 0x02 /* No medium in the drive */ diff --git a/sd.h b/sd.h index de20a8b..7e7a6d4 100644 --- a/sd.h +++ b/sd.h @@ -10,6 +10,11 @@ #ifdef SD_CARD_SELECT_PIN #define SD +#include "pff.h" + +// Feature set of Petit FatFs is currently defined early in pff_conf.h. + + void sd_init(void); #endif /* SD_CARD_SELECT_PIN */