SD card: add the layer between hardware and Petit FatFs.
This is all the commands to read from and write to SPI, initializing the card, read in blocks and so on. This should make Petit FatFs actually usable. So far read-only and no M-codes to let end users play with this stuff. The demonstration code was changed to list the SD card's top level directory over and over again.
This commit is contained in:
parent
fe9ef63225
commit
9de0c65460
50
mendel.c
50
mendel.c
|
|
@ -266,20 +266,48 @@ int main (void)
|
||||||
init();
|
init();
|
||||||
|
|
||||||
#ifdef SD
|
#ifdef SD
|
||||||
// This demonstrates SPI. Gives a nice dance on the scope.
|
// This demonstrates SD card reading. It simply lists the top level
|
||||||
// Will go away as soon as we can do something useful with the SD card.
|
// 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"
|
#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);
|
while (1) {
|
||||||
c++;
|
FATFS sdfile;
|
||||||
delay_ms(50);
|
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
|
#endif
|
||||||
|
|
||||||
// main loop
|
// main loop
|
||||||
|
|
|
||||||
2
pff.c
2
pff.c
|
|
@ -38,6 +38,8 @@
|
||||||
file name conflicts.
|
file name conflicts.
|
||||||
- Changed former integer.h content to use fixed width types (int8_t, etc).
|
- Changed former integer.h content to use fixed width types (int8_t, etc).
|
||||||
- Wrapped code in #ifdef SD to compile it only when needed.
|
- 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 */
|
#include "pff.h" /* Petit FatFs configurations and declarations */
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
/---------------------------------------------------------------------------*/
|
/---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
#define _USE_READ 1 /* Enable pf_read() function */
|
#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_LSEEK 0 /* Enable pf_lseek() function */
|
||||||
#define _USE_WRITE 0 /* Enable pf_write() function */
|
#define _USE_WRITE 0 /* Enable pf_write() function */
|
||||||
|
|
||||||
|
|
|
||||||
266
pff_diskio.c
266
pff_diskio.c
|
|
@ -1,5 +1,11 @@
|
||||||
/*-----------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------*/
|
||||||
/* Low level disk I/O module skeleton for Petit FatFs (C)ChaN, 2014 */
|
/* 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. */
|
/* Changes for Teacup see pff.c. */
|
||||||
|
|
||||||
|
|
@ -7,72 +13,238 @@
|
||||||
|
|
||||||
#ifdef SD
|
#ifdef SD
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "spi.h"
|
||||||
|
#include "delay.h"
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
/* Definitions for MMC/SDC command. */
|
||||||
/* Initialize Disk Drive */
|
#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)
|
/* Card type flags (card_type). */
|
||||||
{
|
#define CT_MMC 0x01 /* MMC ver 3 */
|
||||||
DSTATUS stat;
|
#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<n> is the command sequence of CMD55-CMD<n>. */
|
||||||
|
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.
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
Initialize disk drive and also find out which kind of card we're talking to.
|
||||||
/* Read Partial Sector */
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
DRESULT disk_readp (
|
Description about what's going on here see
|
||||||
BYTE* buff, /* Pointer to the destination object */
|
http://elm-chan.org/docs/mmc/mmc_e.html, center section.
|
||||||
DWORD sector, /* Sector number (LBA) */
|
*/
|
||||||
UINT offset, /* Offset in the sector */
|
DSTATUS disk_initialize(void) {
|
||||||
UINT count /* Byte count (bit15:destination) */
|
BYTE n, cmd, ty, ocr[4];
|
||||||
)
|
UINT timeout;
|
||||||
{
|
|
||||||
DRESULT res;
|
|
||||||
|
|
||||||
// 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);
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
/* Find out wether it's a block device (CCS bit in OCR). */
|
||||||
/* Write Partial Sector */
|
if (timeout && send_cmd(CMD58, 0) == 0) {
|
||||||
/*-----------------------------------------------------------------------*/
|
for (n = 0; n < 4; n++)
|
||||||
|
ocr[n] = spi_rw(0xFF);
|
||||||
DRESULT disk_writep (
|
ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* SDv2 */
|
||||||
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
|
|
||||||
|
|
||||||
}
|
}
|
||||||
} 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 */
|
#endif /* SD */
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,12 @@ typedef enum {
|
||||||
/* Prototypes for disk control functions */
|
/* Prototypes for disk control functions */
|
||||||
|
|
||||||
DSTATUS disk_initialize (void);
|
DSTATUS disk_initialize (void);
|
||||||
DRESULT disk_readp (BYTE* buff, DWORD sector, UINT offser, UINT count);
|
#if _USE_READ
|
||||||
DRESULT disk_writep (const BYTE* buff, DWORD sc);
|
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_NOINIT 0x01 /* Drive not initialized */
|
||||||
#define STA_NODISK 0x02 /* No medium in the drive */
|
#define STA_NODISK 0x02 /* No medium in the drive */
|
||||||
|
|
|
||||||
5
sd.h
5
sd.h
|
|
@ -10,6 +10,11 @@
|
||||||
#ifdef SD_CARD_SELECT_PIN
|
#ifdef SD_CARD_SELECT_PIN
|
||||||
#define SD
|
#define SD
|
||||||
|
|
||||||
|
#include "pff.h"
|
||||||
|
|
||||||
|
// Feature set of Petit FatFs is currently defined early in pff_conf.h.
|
||||||
|
|
||||||
|
|
||||||
void sd_init(void);
|
void sd_init(void);
|
||||||
|
|
||||||
#endif /* SD_CARD_SELECT_PIN */
|
#endif /* SD_CARD_SELECT_PIN */
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue