2018-02-02 16:35:18 -04:00
|
|
|
|
#pragma GCC optimize ("O2")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
(c) 2017 night_ghost@ykoctpa.ru
|
|
|
|
|
|
|
|
|
|
SD card and Dataflash low-level driver
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "sd.h"
|
|
|
|
|
#include "../diskio.h"
|
|
|
|
|
#include "../ff.h"
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <syscalls.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define CS_HIGH() spi_chipSelectHigh()
|
|
|
|
|
#define CS_LOW() spi_chipSelectLow(1)
|
|
|
|
|
#define MMC_CD spi_detect() /* Card detect (yes:true, no:false, default:true) */
|
|
|
|
|
#define MMC_WP 0 /* Write protected (yes:true, no:false, default:false) */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
Module Private Functions
|
|
|
|
|
|
|
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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 CMD13 (13) /* Get_STATUS */
|
|
|
|
|
#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 */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline uint32_t micros() { return timer_get_count32(TIMER5); }
|
|
|
|
|
|
|
|
|
|
static inline uint32_t millis() { return systick_uptime(); }
|
|
|
|
|
|
|
|
|
|
typedef uint8_t (*spi_WaitFunc)(uint8_t b);
|
|
|
|
|
|
|
|
|
|
// utility function
|
|
|
|
|
extern uint8_t spi_spiSend(uint8_t b);
|
|
|
|
|
extern uint8_t spi_spiRecv(void);
|
|
|
|
|
extern uint8_t spi_spiXchg(uint8_t b);
|
|
|
|
|
extern void spi_spiTransfer(const uint8_t *send, uint32_t send_len, uint8_t *recv, uint32_t recv_len);
|
|
|
|
|
extern void spi_chipSelectHigh(void);
|
|
|
|
|
extern bool spi_chipSelectLow(bool take_sem);
|
|
|
|
|
extern void spi_yield();
|
|
|
|
|
extern uint8_t spi_waitFor(uint8_t out, spi_WaitFunc cb, uint32_t dly);
|
|
|
|
|
extern uint8_t spi_detect();
|
|
|
|
|
extern uint32_t get_fattime ();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static volatile DSTATUS Stat = STA_NOINIT; /* Physical drive status */
|
|
|
|
|
|
|
|
|
|
static volatile uint16_t Timer1, Timer2; /* 1kHz decrement timer stopped at zero (disk_timerproc()) */
|
|
|
|
|
static uint32_t sd_max_sectors=0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extern int printf(const char *msg, ...);
|
|
|
|
|
|
|
|
|
|
#if defined(BOARD_SDCARD_CS_PIN)
|
|
|
|
|
|
|
|
|
|
#define WAIT_IN_ISR
|
|
|
|
|
|
|
|
|
|
static uint8_t CardType; /* Card type flags */
|
|
|
|
|
static uint8_t no_CMD13 = 0;
|
|
|
|
|
static uint8_t csd[16]; // for DMA reads
|
|
|
|
|
|
|
|
|
|
static int8_t xmit_datablock(const uint8_t *buff, uint8_t token);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t sd_get_type() {
|
|
|
|
|
return CardType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* SPI controls (Platform dependent) */
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
/* Initialize MMC interface */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Exchange a byte */
|
|
|
|
|
static inline
|
|
|
|
|
uint8_t xchg_spi (
|
|
|
|
|
uint8_t dat /* Data to send */
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
return spi_spiXchg(dat);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline
|
|
|
|
|
uint8_t rcvr_spi(){
|
|
|
|
|
return xchg_spi(0xFF);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Receive multiple byte */
|
|
|
|
|
static inline
|
|
|
|
|
void rcvr_spi_multi (
|
|
|
|
|
uint8_t *buff, /* Pointer to data buffer */
|
|
|
|
|
uint16_t btr /* Number of bytes to receive (even number) */
|
|
|
|
|
){
|
|
|
|
|
spi_spiTransfer(NULL,0, buff, btr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Send multiple byte */
|
|
|
|
|
static inline
|
|
|
|
|
void xmit_spi_multi (
|
|
|
|
|
const uint8_t *buff, /* Pointer to the data */
|
|
|
|
|
uint16_t btx /* Number of bytes to send (even number) */
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
spi_spiTransfer(buff, btx, NULL, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
void power_on(void){ // Power on the SD-Card Socket if hardware allows
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void power_off(void){
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* Wait for card ready */
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
// TODO: remove all active waiting by transferring check to callback
|
|
|
|
|
|
|
|
|
|
static uint8_t wait_ff(uint8_t b){
|
|
|
|
|
return b==0xff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
|
int wait_ready ( /* 1:Ready, 0:Timeout */
|
|
|
|
|
uint16_t wt /* Timeout [ms] */
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
uint8_t d;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(WAIT_IN_ISR)
|
|
|
|
|
d=spi_waitFor(0xFF,wait_ff,wt*1000);
|
|
|
|
|
|
|
|
|
|
return d == 0xFF;
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
Timer2 = wt;
|
|
|
|
|
do {
|
|
|
|
|
d = xchg_spi(0xFF);
|
|
|
|
|
spi_yield(); /* This loop takes a time. Insert rot_rdq() here for multitask envilonment. */
|
|
|
|
|
} while (d != 0xFF && Timer2); /* Wait for card goes ready or timeout */
|
|
|
|
|
|
|
|
|
|
if(d == 0xFF) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* Deselect card and release SPI */
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
static
|
2018-05-16 06:45:50 -03:00
|
|
|
|
void deselect_cs (void)
|
2018-02-02 16:35:18 -04:00
|
|
|
|
{
|
|
|
|
|
CS_HIGH(); /* Set CS# high */
|
|
|
|
|
xchg_spi(0xFF); /* Dummy clock (force DO hi-z for multiple slave SPI) */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* Select card and wait for ready */
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
static
|
2018-05-16 06:45:50 -03:00
|
|
|
|
int select_cs (void) /* 1:OK, 0:Timeout */
|
2018-02-02 16:35:18 -04:00
|
|
|
|
{
|
|
|
|
|
if(!CS_LOW()) { /* take semaphore and Set CS# low */
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
xchg_spi(0xFF); /* Dummy clock (force DO enabled) */
|
|
|
|
|
if (wait_ready(500)) return 1; /* Wait for card ready */
|
|
|
|
|
|
2018-05-16 06:45:50 -03:00
|
|
|
|
deselect_cs();
|
2018-02-02 16:35:18 -04:00
|
|
|
|
return 0; /* Timeout */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* Receive a data packet from the MMC */
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
static uint8_t wait_noFF(uint8_t b){
|
|
|
|
|
return b!=0xff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
|
int8_t rcvr_datablock ( /* 1:OK, 0:Error */
|
|
|
|
|
uint8_t *buff, /* Data buffer */
|
|
|
|
|
uint16_t btr /* Data block length (byte) */
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
uint8_t ret=0;
|
|
|
|
|
|
|
|
|
|
#if defined(WAIT_IN_ISR)
|
|
|
|
|
ret=spi_waitFor(0xff, wait_noFF, 200000);
|
|
|
|
|
|
|
|
|
|
if(ret != 0xFE) goto done; // Function fails if invalid DataStart token or timeout
|
|
|
|
|
#else
|
|
|
|
|
uint8_t token;
|
|
|
|
|
|
|
|
|
|
Timer1 = 200;
|
|
|
|
|
do { /* Wait for DataStart token in timeout of 200ms */
|
|
|
|
|
token = xchg_spi(0xFF);
|
|
|
|
|
spi_yield(); /* This loop will take a time. Insert rot_rdq() here for multitask environment. */
|
|
|
|
|
} while ((token == 0xFF) && Timer1);
|
|
|
|
|
|
|
|
|
|
if(token != 0xFE) {
|
|
|
|
|
goto done; /* Function fails if invalid DataStart token or timeout */
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
rcvr_spi_multi(buff, btr); /* Store trailing data to the buffer */
|
|
|
|
|
xchg_spi(0xFF); xchg_spi(0xFF); /* Discard CRC */
|
|
|
|
|
|
|
|
|
|
ret=1; /* Function succeeded */
|
|
|
|
|
done:
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* Send a data packet to the MMC */
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
static int8_t xmit_datablock ( /* 1:OK, 0:Failed */
|
|
|
|
|
const uint8_t *buff, /* Ponter to 512 byte data to be sent */
|
|
|
|
|
uint8_t token /* Token */
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
uint8_t resp;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!wait_ready(500)) return 0; /* Wait for card ready */
|
|
|
|
|
|
|
|
|
|
xchg_spi(token); /* Send token */
|
|
|
|
|
if (token != 0xFD) { /* Send data if token is other than StopTran */
|
|
|
|
|
xmit_spi_multi(buff, 512); /* Data */
|
|
|
|
|
xchg_spi(0xFF); xchg_spi(0xFF); /* Dummy CRC */
|
|
|
|
|
|
|
|
|
|
resp = xchg_spi(0xFF); /* Receive data resp */
|
|
|
|
|
if ((resp & 0x1F) != 0x05) {
|
|
|
|
|
return 0; /* Function fails if the data packet was not accepted */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* Send a command packet to the MMC */
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
static uint8_t buf[6]; // to use DMA
|
|
|
|
|
|
|
|
|
|
static uint8_t wait_0x80(uint8_t b){
|
|
|
|
|
return (b & 0x80)==0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
|
uint8_t send_cmd ( /* Return value: R1 resp (bit7==1:Failed to send) */
|
|
|
|
|
uint8_t cmd, /* Command index */
|
|
|
|
|
uint32_t arg /* Argument */
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
uint8_t crc, res;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (cmd & 0x80) { /* Send a CMD55 prior to ACMD<n> */
|
|
|
|
|
cmd &= 0x7F;
|
|
|
|
|
res = send_cmd(CMD55, 0);
|
|
|
|
|
if (res > 1) {
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Select the card and wait for ready except to stop multiple block read */
|
|
|
|
|
if (cmd != CMD12) {
|
2018-05-16 06:45:50 -03:00
|
|
|
|
deselect_cs();
|
2018-02-02 16:35:18 -04:00
|
|
|
|
// spi_yield(); // sync quant so no interrupts when receiving answer
|
2018-05-16 06:45:50 -03:00
|
|
|
|
if (!select_cs()) {
|
2018-02-02 16:35:18 -04:00
|
|
|
|
return 0xFF;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
crc = 0x01; /* Dummy CRC + Stop */
|
|
|
|
|
if (cmd == CMD0) crc = 0x95; /* Valid CRC for CMD0(0) */
|
|
|
|
|
if (cmd == CMD8) crc = 0x87; /* Valid CRC for CMD8(0x1AA) */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Send command packet */
|
|
|
|
|
buf[0] = 0x40 | cmd; /* Start + command index */
|
|
|
|
|
buf[1] = (uint8_t)(arg >> 24); /* Argument[31..24] */
|
|
|
|
|
buf[2] = (uint8_t)(arg >> 16); /* Argument[23..16] */
|
|
|
|
|
buf[3] = (uint8_t)(arg >> 8); /* Argument[15..8] */
|
|
|
|
|
buf[4] = (uint8_t)arg; /* Argument[7..0] */
|
|
|
|
|
buf[5] = crc; /* CRC + Stop */
|
|
|
|
|
|
|
|
|
|
xmit_spi_multi(buf, 6); // entire command in one packet
|
|
|
|
|
|
|
|
|
|
/* Receive command resp */
|
|
|
|
|
if (cmd == CMD12) xchg_spi(0xFF); /* Discard following one byte when CMD12 */
|
|
|
|
|
|
|
|
|
|
#if defined(WAIT_IN_ISR)
|
|
|
|
|
res=spi_waitFor(0xff, wait_0x80, 20000);
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
uint8_t n = 255; /* Wait for response (10 bytes max) */
|
|
|
|
|
do {
|
|
|
|
|
res = xchg_spi(0xFF);
|
|
|
|
|
spi_yield(); /* This loop will take a time. Insert rot_rdq() here for multitask environment. */
|
|
|
|
|
} while ((res & 0x80) && --n);
|
|
|
|
|
|
|
|
|
|
return res; /* Return received response */
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
Public Functions
|
|
|
|
|
|
|
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* Initialize disk drive */
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
DSTATUS sd_initialize() {
|
|
|
|
|
uint8_t n, cmd, ty, ocr[4] ;
|
|
|
|
|
|
|
|
|
|
if (Stat & STA_NODISK) return Stat; /* Is card existing in the soket? */
|
|
|
|
|
if(!(Stat & STA_NOINIT) ) return Stat; // already done
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ty = 0;
|
|
|
|
|
n = send_cmd(CMD0, 0);
|
|
|
|
|
if (n == 1 || n==0) { /* Put the card SPI/Idle state */
|
|
|
|
|
Timer1 = 5000;
|
|
|
|
|
n=send_cmd(CMD8, 0x1AA); /* Initialization timeout = 5 sec */
|
|
|
|
|
if((n&4) == 0) rcvr_spi_multi(ocr, 4); /* Get 32 bit return value of R7 resp if not illegal command */
|
|
|
|
|
if (n == 1) { /* SDv2? */
|
|
|
|
|
if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* Is the card supports vcc of 2.7-3.6V? and did echoing */
|
|
|
|
|
while(Timer1){ /* Wait for end of initialization with ACMD41(HCS) */
|
|
|
|
|
n = send_cmd(ACMD41, 1UL << 30);
|
|
|
|
|
if(n==0) break;
|
|
|
|
|
}
|
|
|
|
|
if (Timer1){
|
|
|
|
|
n = send_cmd(CMD58, 0);
|
|
|
|
|
if((n&4) == 0) rcvr_spi_multi(ocr, 4); /* Get 32 bit return value of R7 resp if not illegal command */
|
|
|
|
|
if( n <= 1) { /* Check CCS bit in the OCR */
|
|
|
|
|
ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* Card id SDv2 */
|
|
|
|
|
} else {
|
|
|
|
|
printf("\nSD CMD58 failed!\n");
|
|
|
|
|
}
|
|
|
|
|
}else{
|
|
|
|
|
printf("\nSD timeout!\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else { /* Not SDv2 card */
|
|
|
|
|
if (send_cmd(ACMD41, 0) <= 1) { /* SDv1 or MMC? */
|
|
|
|
|
ty = CT_SD1; cmd = ACMD41; /* SDv1 (ACMD41(0)) */
|
|
|
|
|
} else {
|
|
|
|
|
ty = CT_MMC; cmd = CMD1; /* MMCv3 (CMD1(0)) */
|
|
|
|
|
}
|
|
|
|
|
while(Timer1) { /* Wait for end of initialization */
|
|
|
|
|
n=send_cmd(cmd, 0);
|
|
|
|
|
if(n==0) break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Timer1){
|
|
|
|
|
if(send_cmd(CMD16, 512) != 0) { /* Set block length: 512 */
|
|
|
|
|
ty = 0;
|
|
|
|
|
printf("\nSD CMD16 failed!\n");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ty = 0;
|
|
|
|
|
printf("\nSD timeout!\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ty=0;
|
|
|
|
|
}
|
|
|
|
|
CardType = ty; /* Card type */
|
|
|
|
|
|
|
|
|
|
if (ty) { /* OK */
|
|
|
|
|
Stat &= ~STA_NOINIT; /* Clear STA_NOINIT flag */
|
|
|
|
|
uint8_t i;
|
|
|
|
|
for(i=0;i<15;i++){ // 15 tries to get size
|
|
|
|
|
sd_getSectorCount(&sd_max_sectors);
|
|
|
|
|
if(sd_max_sectors!=0) break;
|
|
|
|
|
}
|
|
|
|
|
} else { /* Failed */
|
|
|
|
|
Stat = STA_NOINIT;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-16 06:45:50 -03:00
|
|
|
|
deselect_cs();
|
2018-02-02 16:35:18 -04:00
|
|
|
|
|
|
|
|
|
return Stat;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint8_t restart_card(){
|
|
|
|
|
Stat = STA_NOINIT;
|
|
|
|
|
|
|
|
|
|
sd_initialize();
|
|
|
|
|
|
|
|
|
|
if(!(Stat & STA_NOINIT) ) return true; // OK
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t sd_getSectorCount(uint32_t *ptr){
|
|
|
|
|
uint32_t csize;
|
|
|
|
|
|
|
|
|
|
if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) {
|
|
|
|
|
if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */
|
|
|
|
|
csize = csd[9] + ((uint16_t)csd[8] << 8) + ((uint32_t)(csd[7] & 63) << 16) + 1;
|
|
|
|
|
*ptr = csize << 10;
|
|
|
|
|
} else { /* SDC ver 1.XX or MMC ver 3 */
|
|
|
|
|
uint8_t n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
|
|
|
|
|
csize = (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) + 1;
|
|
|
|
|
*ptr = csize << (n - 9);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return RES_OK;
|
|
|
|
|
}
|
|
|
|
|
return RES_ERROR;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* Get disk status */
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
DSTATUS sd_status (){
|
|
|
|
|
|
|
|
|
|
return Stat; /* Return disk status */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t sd_get_state(){
|
|
|
|
|
if(!no_CMD13) {
|
|
|
|
|
if(send_cmd(CMD13, 0)<=1){
|
|
|
|
|
uint8_t ret = xchg_spi(0xFF);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
no_CMD13=1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0; // CMD13 not supported so all OK
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
7 - out of range / CSD overwrite
|
|
|
|
|
6 • Erase param: An invalid selection for erase, sectors or groups.
|
|
|
|
|
5 • Write protect violation: The command tried to write a write-protected block.
|
|
|
|
|
4 • Card ECC failed: Card internal ECC was applied but failed to correct the data.
|
|
|
|
|
3 • CC error: Internal card controller error.
|
|
|
|
|
2 • Error: A general or an unknown error occurred during the operation.
|
|
|
|
|
1 • Write protect erase skip | lock/unlock command failed: This status bit has two functions over-
|
|
|
|
|
loaded. It is set when the host attempts to erase a write-protected sector or makes a sequence or
|
|
|
|
|
password errors during card lock/unlock operation.
|
|
|
|
|
0 • Card is locked: Set when the card is locked by the user. Reset when it is unlocked.
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* Read sector(s) */
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
static bool single_sector_card=false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DRESULT sd_read (
|
|
|
|
|
uint8_t *buff, /* Pointer to the data buffer to store read data */
|
|
|
|
|
uint32_t sector, /* Start sector number (LBA) */
|
|
|
|
|
uint16_t count /* Number of sectors to read (1..128) */
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
uint8_t ret;
|
|
|
|
|
uint16_t sectorInc=1;
|
|
|
|
|
|
|
|
|
|
if (!count) return RES_PARERR; /* Check parameter */
|
|
|
|
|
if (Stat & STA_NOINIT) return RES_NOTRDY; /* Check if drive is ready */
|
|
|
|
|
|
|
|
|
|
if(sd_max_sectors && sector > sd_max_sectors) return RES_PARERR; /* Check parameter */
|
|
|
|
|
|
|
|
|
|
if (!(CardType & CT_BLOCK)) {
|
|
|
|
|
sectorInc = 512;
|
|
|
|
|
sector *= sectorInc; /* LBA to BA conversion (byte addressing cards) */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (count == 1) { /* Single sector read */
|
|
|
|
|
if (( (ret=send_cmd(CMD17, sector)) == 0) /* READ_SINGLE_BLOCK */
|
|
|
|
|
&& rcvr_datablock(buff, 512)) {
|
|
|
|
|
count = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else { /* Multiple sector read */
|
|
|
|
|
if(single_sector_card) {
|
|
|
|
|
do {
|
|
|
|
|
if ((ret=send_cmd(CMD17, sector)) == 0){ /* READ_SINGLE_BLOCK */
|
|
|
|
|
if(rcvr_datablock(buff, 512)) {
|
|
|
|
|
buff += 512;
|
|
|
|
|
sector += sectorInc;
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-05-16 06:45:50 -03:00
|
|
|
|
deselect_cs();
|
2018-02-02 16:35:18 -04:00
|
|
|
|
spi_yield();
|
|
|
|
|
} while(--count);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
// send_cmd(CMD23, count);
|
|
|
|
|
if (CardType & CT_SDC) send_cmd(ACMD23, count); /* Predefine number of sectors */
|
|
|
|
|
|
|
|
|
|
uint16_t got=0;
|
|
|
|
|
if ((ret=send_cmd(CMD18, sector)) == 0) { /* READ_MULTIPLE_BLOCK */
|
|
|
|
|
do {
|
|
|
|
|
if (!rcvr_datablock(buff, 512)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
got++;
|
|
|
|
|
buff += 512;
|
|
|
|
|
} while (--count);
|
|
|
|
|
if(got) send_cmd(CMD12, 0); /* STOP_TRANSMISSION */
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
if(count) { // readed only 1 block or none
|
|
|
|
|
single_sector_card=true;
|
|
|
|
|
sector+=got*sectorInc;
|
|
|
|
|
restart_card();
|
|
|
|
|
return sd_read(buff, sector/sectorInc, count); // read remaining in single sector mode
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-16 06:45:50 -03:00
|
|
|
|
deselect_cs();
|
2018-02-02 16:35:18 -04:00
|
|
|
|
if(count==0) return RES_OK; /* Return result */
|
|
|
|
|
Stat = STA_NOINIT;
|
|
|
|
|
return RES_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* Write sector(s) */
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
DRESULT sd_write (
|
|
|
|
|
const uint8_t *buff, /* Ponter to the data to write */
|
|
|
|
|
uint32_t sector, /* Start sector number (LBA) */
|
|
|
|
|
uint16_t count /* Number of sectors to write (1..128) */
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
if (!count) return RES_PARERR; /* Check parameter */
|
|
|
|
|
if (Stat & STA_NOINIT) return RES_NOTRDY; /* Check drive status */
|
|
|
|
|
if (Stat & STA_PROTECT) return RES_WRPRT; /* Check write protect */
|
|
|
|
|
if(sd_max_sectors && sector > sd_max_sectors) return RES_PARERR; /* Check parameter */
|
|
|
|
|
|
|
|
|
|
uint16_t sectorInc = 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!(CardType & CT_BLOCK)){
|
|
|
|
|
sectorInc = 512;
|
|
|
|
|
sector *= sectorInc; /* LBA ==> BA conversion (byte addressing cards) */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (count == 1) { /* Single sector write */
|
|
|
|
|
if (send_cmd(CMD24, sector) == 0) { /* WRITE_BLOCK */
|
|
|
|
|
if( xmit_datablock(buff, 0xFE)) {
|
|
|
|
|
count = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if(single_sector_card) {
|
|
|
|
|
do {
|
|
|
|
|
if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */
|
|
|
|
|
&& xmit_datablock(buff, 0xFE)) {
|
|
|
|
|
sector+=sectorInc;
|
|
|
|
|
buff += 512;
|
|
|
|
|
} else break; // error
|
|
|
|
|
} while(--count);
|
|
|
|
|
} else {
|
|
|
|
|
/* Multiple sector write */
|
|
|
|
|
if (CardType & CT_SDC) send_cmd(ACMD23, count); /* Predefine number of sectors */
|
|
|
|
|
uint32_t sent=0;
|
|
|
|
|
if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */
|
|
|
|
|
do {
|
|
|
|
|
if (!xmit_datablock(buff, 0xFC)) break;
|
|
|
|
|
buff += 512;
|
|
|
|
|
sent++;
|
|
|
|
|
} while (--count);
|
|
|
|
|
if (!xmit_datablock(0, 0xFD)) count = 1; /* STOP_TRAN token */
|
|
|
|
|
}
|
|
|
|
|
if(count) {
|
|
|
|
|
single_sector_card=true;
|
|
|
|
|
sector+=sent*sectorInc;
|
|
|
|
|
restart_card();
|
|
|
|
|
return sd_write(buff, sector/sectorInc, count); // write remaining in single sector mode
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(count) {
|
2018-05-16 06:45:50 -03:00
|
|
|
|
deselect_cs();
|
2018-02-02 16:35:18 -04:00
|
|
|
|
Stat = STA_NOINIT;
|
|
|
|
|
return RES_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t ret = sd_get_state(); // reset errors
|
|
|
|
|
|
2018-05-16 06:45:50 -03:00
|
|
|
|
deselect_cs();
|
2018-02-02 16:35:18 -04:00
|
|
|
|
|
|
|
|
|
if(ret) return RES_ERROR;
|
|
|
|
|
|
|
|
|
|
return RES_OK; /* Return result */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* Miscellaneous drive controls other than data read/write */
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
DRESULT sd_ioctl (
|
|
|
|
|
uint8_t cmd, /* Control command code */
|
|
|
|
|
void *buff /* Pointer to the conrtol data */
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
DRESULT res;
|
|
|
|
|
uint8_t n;
|
|
|
|
|
uint32_t *dp, st, ed;
|
|
|
|
|
|
|
|
|
|
uint8_t *ptr = (uint8_t *)buff;
|
|
|
|
|
|
|
|
|
|
res = RES_ERROR;
|
|
|
|
|
|
|
|
|
|
if (cmd== CTRL_POWER){
|
|
|
|
|
|
|
|
|
|
switch (*ptr) {
|
|
|
|
|
case 0: // Sub control code == 0 (POWER_OFF)
|
|
|
|
|
// if (SD_PRESENT())
|
|
|
|
|
// power_off(); // Power off
|
|
|
|
|
res = RES_OK;
|
|
|
|
|
break;
|
|
|
|
|
case 1: // Sub control code == 1 (POWER_ON)
|
|
|
|
|
// power_on(); // Power on
|
|
|
|
|
res = RES_OK;
|
|
|
|
|
break;
|
|
|
|
|
case 2: // Sub control code == 2 (POWER_GET)
|
|
|
|
|
*(ptr + 1) = (uint8_t) MMC_CD;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
res = RES_PARERR;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
if (Stat & STA_NOINIT) return RES_NOTRDY; /* Check if drive is ready */
|
|
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
|
case CTRL_SYNC : /* Wait for end of internal write process of the drive */
|
2018-05-16 06:45:50 -03:00
|
|
|
|
if (select_cs()) res = RES_OK;
|
2018-02-02 16:35:18 -04:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GET_SECTOR_COUNT : /* Get drive capacity in unit of sector (uint32_t) */
|
|
|
|
|
res=sd_getSectorCount((uint32_t*)buff);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (uint32_t) */
|
|
|
|
|
if (CardType & CT_SD2) { /* SDC ver 2.00 */
|
|
|
|
|
if (send_cmd(ACMD13, 0) == 0) { /* Read SD status */
|
|
|
|
|
xchg_spi(0xFF);
|
|
|
|
|
if (rcvr_datablock(csd, 16)) { /* Read partial block */
|
|
|
|
|
for (n = 64 - 16; n; n--) xchg_spi(0xFF); /* Purge trailing data */
|
|
|
|
|
*(uint32_t*)buff = 16UL << (csd[10] >> 4);
|
|
|
|
|
res = RES_OK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else { /* SDC ver 1.XX or MMC */
|
|
|
|
|
if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { /* Read CSD */
|
|
|
|
|
if (CardType & CT_SD1) { /* SDC ver 1.XX */
|
|
|
|
|
*(uint32_t*)buff = (((csd[10] & 63) << 1) + ((uint16_t)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1);
|
|
|
|
|
} else { /* MMC */
|
|
|
|
|
*(uint32_t*)buff = ((uint16_t)((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 (!(CardType & CT_SDC)) break; /* Check if the card is SDC */
|
|
|
|
|
if (sd_ioctl(MMC_GET_CSD, csd)) break; /* Get CSD */
|
|
|
|
|
if (!(csd[0] >> 6) && !(csd[10] & 0x40)) break; /* Check if sector erase can be applied to the card */
|
|
|
|
|
dp = buff; st = dp[0]; ed = dp[1]; /* Load sector block */
|
|
|
|
|
if (!(CardType & CT_BLOCK)) {
|
|
|
|
|
st *= 512; ed *= 512;
|
|
|
|
|
}
|
|
|
|
|
if (send_cmd(CMD32, st) == 0 && send_cmd(CMD33, ed) == 0 && send_cmd(CMD38, 0) == 0 && wait_ready(30000)) { /* Erase sector block */
|
|
|
|
|
res = RES_OK; /* FatFs does not check result of this command */
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
/*
|
|
|
|
|
case MMC_GET_TYPE: // Get card type flags (1 byte)
|
|
|
|
|
*ptr = g_card_type;
|
|
|
|
|
res = RES_OK;
|
|
|
|
|
break;
|
|
|
|
|
*/
|
|
|
|
|
case MMC_GET_CSD: /* Receive CSD as a data block (16 bytes) */
|
|
|
|
|
if (send_cmd(CMD9, 0) == 0 /* READ_CSD */
|
|
|
|
|
&& rcvr_datablock(ptr, 16))
|
|
|
|
|
res = RES_OK;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case MMC_GET_CID: /* Receive CID as a data block (16 bytes) */
|
|
|
|
|
if (send_cmd(CMD10, 0) == 0 /* READ_CID */
|
|
|
|
|
&& rcvr_datablock(ptr, 16)){
|
|
|
|
|
/*
|
|
|
|
|
sdcard.manufacturerID = cid[0];
|
|
|
|
|
sdcard.oemID = (cid[1] << 8) | cid[2];
|
|
|
|
|
sdcard.productName[0] = cid[3];
|
|
|
|
|
sdcard.productName[1] = cid[4];
|
|
|
|
|
sdcard.productName[2] = cid[5];
|
|
|
|
|
sdcard.productName[3] = cid[6];
|
|
|
|
|
sdcard.productName[4] = cid[7];
|
|
|
|
|
sdcard.productRevisionMajor = cid[8] >> 4;
|
|
|
|
|
sdcard.productRevisionMinor = cid[8] & 0x0F;
|
|
|
|
|
sdcard.productSerial = (cid[9] << 24) | (cid[10] << 16) | (cid[11] << 8) | cid[12];
|
|
|
|
|
sdcard.productionYear = (((cid[13] & 0x0F) << 4) | (cid[14] >> 4)) + 2000;
|
|
|
|
|
sdcard.productionMonth = cid[14] & 0x0F;
|
|
|
|
|
*/
|
|
|
|
|
res = RES_OK;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case MMC_GET_OCR: /* Receive OCR as an R3 resp (4 bytes) */
|
|
|
|
|
if (send_cmd(CMD58, 0) == 0) { /* READ_OCR */
|
|
|
|
|
// for (n = 4; n; n--) *ptr++ = rcvr_spi();
|
|
|
|
|
rcvr_spi_multi(ptr, 4);
|
|
|
|
|
res = RES_OK;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case MMC_GET_SDSTAT: /* Receive SD status as a data block (64 bytes) */
|
|
|
|
|
if (send_cmd(ACMD13, 0) == 0) { /* SD_STATUS */
|
|
|
|
|
rcvr_spi();
|
|
|
|
|
if (rcvr_datablock(ptr, 64))
|
|
|
|
|
res = RES_OK;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
res = RES_PARERR;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-16 06:45:50 -03:00
|
|
|
|
deselect_cs();
|
2018-02-02 16:35:18 -04:00
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* Device timer function */
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* This function must be called from timer interrupt routine in period
|
|
|
|
|
/ of 1 ms to generate card control timing.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void sd_timerproc (void)
|
|
|
|
|
{
|
|
|
|
|
uint16_t n;
|
|
|
|
|
uint8_t s;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
n = Timer1; /* 1kHz decrement timer stopped at 0 */
|
|
|
|
|
if (n) Timer1 = --n;
|
|
|
|
|
n = Timer2;
|
|
|
|
|
if (n) Timer2 = --n;
|
|
|
|
|
|
|
|
|
|
s = Stat;
|
|
|
|
|
if (MMC_WP) { /* Write protected */
|
|
|
|
|
s |= STA_PROTECT;
|
|
|
|
|
} else { /* Write enabled */
|
|
|
|
|
s &= ~STA_PROTECT;
|
|
|
|
|
}
|
|
|
|
|
if (MMC_CD) { /* Card is in socket */
|
|
|
|
|
s &= ~STA_NODISK;
|
|
|
|
|
} else { /* Socket empty */
|
|
|
|
|
s |= (STA_NODISK | STA_NOINIT);
|
|
|
|
|
}
|
|
|
|
|
Stat = s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#elif defined(BOARD_DATAFLASH_FATFS)
|
|
|
|
|
|
|
|
|
|
// emulate SD card on board's dataflash
|
|
|
|
|
|
|
|
|
|
// flash size
|
|
|
|
|
#define DF_NUM_PAGES 0x1f00
|
|
|
|
|
#define DF_PAGE_SIZE 256L
|
|
|
|
|
|
|
|
|
|
#define DF_RESET BOARD_DATAFLASH_CS_PIN
|
|
|
|
|
|
|
|
|
|
//Winbond M25P16 Serial Flash Embedded Memory 16 Mb, 3V
|
|
|
|
|
#define JEDEC_WRITE_ENABLE 0x06
|
|
|
|
|
#define JEDEC_WRITE_DISABLE 0x04
|
|
|
|
|
#define JEDEC_READ_STATUS 0x05
|
|
|
|
|
#define JEDEC_WRITE_STATUS 0x01
|
|
|
|
|
#define JEDEC_READ_DATA 0x03
|
|
|
|
|
#define JEDEC_FAST_READ 0x0b
|
|
|
|
|
#define JEDEC_DEVICE_ID 0x9F
|
|
|
|
|
#define JEDEC_PAGE_WRITE 0x02
|
|
|
|
|
|
2018-03-19 07:40:05 -03:00
|
|
|
|
#define JEDEC_WREAR 0xC5
|
|
|
|
|
#define JEDEC_ENTER_4B_MODE 0xB7
|
|
|
|
|
|
2018-02-02 16:35:18 -04:00
|
|
|
|
#define JEDEC_BULK_ERASE 0xC7 // full chip erase
|
|
|
|
|
#define JEDEC_SECTOR_ERASE 0x20 // 4k erase
|
|
|
|
|
#define JEDEC_PAGE_ERASE 0xD8 // 64K erase
|
|
|
|
|
|
|
|
|
|
#define JEDEC_STATUS_BUSY 0x01
|
|
|
|
|
#define JEDEC_STATUS_WRITEPROTECT 0x02
|
|
|
|
|
#define JEDEC_STATUS_BP0 0x04
|
|
|
|
|
#define JEDEC_STATUS_BP1 0x08
|
|
|
|
|
#define JEDEC_STATUS_BP2 0x10
|
|
|
|
|
#define JEDEC_STATUS_TP 0x20
|
|
|
|
|
#define JEDEC_STATUS_SEC 0x40
|
|
|
|
|
#define JEDEC_STATUS_SRP0 0x80
|
|
|
|
|
|
|
|
|
|
//#define expect_memorytype 0x20
|
|
|
|
|
//#define expect_capacity 0x15
|
|
|
|
|
|
|
|
|
|
#define MAX_ERASE_SIZE 16384
|
|
|
|
|
|
|
|
|
|
static bool flash_died=false;
|
|
|
|
|
static uint8_t df_manufacturer;
|
|
|
|
|
static uint16_t df_device;
|
|
|
|
|
static uint32_t erase_size = BOARD_DATAFLASH_ERASE_SIZE;
|
2018-03-19 07:40:05 -03:00
|
|
|
|
static uint8_t addr_cmd_length = 4;
|
|
|
|
|
static uint8_t addr_cmd_start = 1;
|
|
|
|
|
static bool addr_4bit = 0;
|
2018-02-02 16:35:18 -04:00
|
|
|
|
|
|
|
|
|
#if BOARD_DATAFLASH_ERASE_SIZE >= 65536
|
|
|
|
|
static uint8_t erase_cmd=JEDEC_PAGE_ERASE;
|
|
|
|
|
#else
|
|
|
|
|
static uint8_t erase_cmd=JEDEC_SECTOR_ERASE;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#pragma pack(push,1)
|
|
|
|
|
static struct {
|
2018-03-19 07:40:05 -03:00
|
|
|
|
uint8_t cmd[5]; // for DMA transfer
|
2018-02-02 16:35:18 -04:00
|
|
|
|
uint8_t sector[DF_PAGE_SIZE]; // we ALWAYS can use DMA!
|
|
|
|
|
} buf;
|
|
|
|
|
#pragma pack(pop)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//[ task management for USB MSC mode
|
|
|
|
|
void hal_set_task_active(void * handle);
|
|
|
|
|
void hal_context_switch_isr();
|
|
|
|
|
void *hal_register_task(voidFuncPtr task, uint32_t stack);
|
|
|
|
|
void hal_set_task_priority(void * handle, uint8_t prio);
|
|
|
|
|
bool hal_is_armed();
|
|
|
|
|
//]
|
|
|
|
|
|
|
|
|
|
#define FLASH_ERASE_QUEUE_SIZE 16
|
|
|
|
|
|
|
|
|
|
static void *_flash_erase_task IN_CCM;
|
|
|
|
|
static volatile uint16_t fe_read_ptr IN_CCM;
|
|
|
|
|
static volatile uint16_t fe_write_ptr IN_CCM;
|
|
|
|
|
typedef struct {
|
|
|
|
|
uint32_t b;
|
|
|
|
|
uint32_t e;
|
|
|
|
|
} fe_item;
|
|
|
|
|
|
|
|
|
|
static fe_item flash_erase_queue[FLASH_ERASE_QUEUE_SIZE] IN_CCM;
|
|
|
|
|
static void * _flash_erase_task IN_CCM;
|
|
|
|
|
|
|
|
|
|
static void sd_flash_eraser();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool chip_is_clear=false;
|
|
|
|
|
|
|
|
|
|
uint8_t sd_get_type() {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* SPI controls (Platform dependent) */
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
/* Initialize MMC interface */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Exchange a byte */
|
|
|
|
|
static inline uint8_t spi_write(
|
|
|
|
|
uint8_t dat /* Data to send */
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
return spi_spiXchg(dat);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline uint8_t spi_read(){
|
|
|
|
|
return spi_spiXchg(0xFF);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Receive multiple byte */
|
|
|
|
|
static inline void read_spi_multi (
|
|
|
|
|
uint8_t *buff, /* Pointer to data buffer */
|
|
|
|
|
uint16_t btr /* Number of bytes to receive (even number) */
|
|
|
|
|
){
|
|
|
|
|
|
|
|
|
|
spi_spiTransfer(NULL,0, buff, btr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Send multiple byte */
|
|
|
|
|
static inline void write_spi_multi (
|
|
|
|
|
const uint8_t *buff, /* Pointer to the data */
|
|
|
|
|
uint16_t btx /* Number of bytes to send (even number) */
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
spi_spiTransfer(buff, btx,NULL, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline
|
|
|
|
|
void cs_release (void)
|
|
|
|
|
{
|
|
|
|
|
CS_HIGH(); /* Set CS# high */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
|
bool cs_assert(void) /* 1:OK, 0:Timeout */
|
|
|
|
|
{
|
|
|
|
|
if(!CS_LOW()) { /* take semaphore and Set CS# low */
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// This function is mainly to test the device
|
|
|
|
|
static void ReadManufacturerID()
|
|
|
|
|
{
|
|
|
|
|
// activate dataflash command decoder
|
|
|
|
|
if (!cs_assert()) return;
|
|
|
|
|
|
|
|
|
|
// Read manufacturer and ID command...
|
|
|
|
|
buf.cmd[0] = JEDEC_DEVICE_ID;
|
|
|
|
|
|
2018-03-19 07:40:05 -03:00
|
|
|
|
spi_spiTransfer(buf.cmd, 1, buf.cmd, addr_cmd_length);
|
2018-02-02 16:35:18 -04:00
|
|
|
|
|
|
|
|
|
df_manufacturer = buf.cmd[0];
|
|
|
|
|
df_device = (buf.cmd[1] << 8) | buf.cmd[2]; //capacity
|
|
|
|
|
|
|
|
|
|
// release SPI bus for use by other sensors
|
|
|
|
|
cs_release();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Read the status register
|
|
|
|
|
static uint8_t ReadStatusReg()
|
|
|
|
|
{
|
|
|
|
|
uint8_t tmp;
|
|
|
|
|
|
|
|
|
|
if (!cs_assert()) return JEDEC_STATUS_BUSY;
|
|
|
|
|
|
|
|
|
|
// Read status command
|
|
|
|
|
buf.cmd[0] = JEDEC_READ_STATUS;
|
|
|
|
|
|
|
|
|
|
spi_spiTransfer(buf.cmd, 1, &buf.cmd[1], 1);
|
|
|
|
|
tmp = buf.cmd[1];
|
|
|
|
|
|
|
|
|
|
cs_release();
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint8_t ReadStatus()
|
|
|
|
|
{
|
|
|
|
|
// We only want to extract the READY/BUSY bit
|
|
|
|
|
uint8_t status = ReadStatusReg();
|
|
|
|
|
|
|
|
|
|
return status & JEDEC_STATUS_BUSY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
// Wait for dataflash
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
static int wait_ready ( /* 1:Ready, 0:Timeout */
|
|
|
|
|
uint16_t wt /* Timeout [ms] */
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
if(flash_died) return 0;
|
|
|
|
|
|
|
|
|
|
Timer2 = wt;
|
|
|
|
|
do {
|
|
|
|
|
if(ReadStatus()==0) return 1; //ready
|
|
|
|
|
|
|
|
|
|
spi_yield(); /* This loop takes a time. Insert rot_rdq() here for multitask envilonment. */
|
|
|
|
|
} while(Timer2); /* Wait for card goes ready or timeout */
|
|
|
|
|
|
|
|
|
|
flash_died = true;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void Flash_Jedec_WriteEnable(void){
|
|
|
|
|
if (!wait_ready(500)) return; /* Wait for card ready */
|
|
|
|
|
|
|
|
|
|
if (!cs_assert()) return;
|
|
|
|
|
spi_write(JEDEC_WRITE_ENABLE);
|
|
|
|
|
cs_release();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-03-19 07:40:05 -03:00
|
|
|
|
static void Flash_Enter4B_Mode(){
|
|
|
|
|
if (!wait_ready(500)) return; /* Wait for card ready */
|
|
|
|
|
|
|
|
|
|
if (!cs_assert()) return;
|
|
|
|
|
spi_write(JEDEC_ENTER_4B_MODE);
|
|
|
|
|
cs_release();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-02-02 16:35:18 -04:00
|
|
|
|
static bool read_page( uint8_t *ptr, uint32_t pageNum){
|
|
|
|
|
uint32_t PageAdr = pageNum * DF_PAGE_SIZE;
|
|
|
|
|
|
|
|
|
|
if (!wait_ready(500)) return 0; /* Wait for card ready */
|
|
|
|
|
|
|
|
|
|
if (!cs_assert()) return 0;
|
|
|
|
|
|
2018-03-19 07:40:05 -03:00
|
|
|
|
{
|
|
|
|
|
uint8_t i = 0;
|
2018-02-02 16:35:18 -04:00
|
|
|
|
|
2018-03-19 07:40:05 -03:00
|
|
|
|
buf.cmd[i++] = JEDEC_READ_DATA;
|
|
|
|
|
if(addr_4bit) {
|
|
|
|
|
buf.cmd[i++] = (PageAdr >> 24) & 0xff;
|
|
|
|
|
}
|
|
|
|
|
buf.cmd[i++] = (PageAdr >> 16) & 0xff;
|
|
|
|
|
buf.cmd[i++] = (PageAdr >> 8) & 0xff;
|
|
|
|
|
buf.cmd[i++] = (PageAdr >> 0) & 0xff;
|
|
|
|
|
}
|
2018-02-02 16:35:18 -04:00
|
|
|
|
|
2018-03-19 07:40:05 -03:00
|
|
|
|
spi_spiTransfer(&buf.cmd[0], addr_cmd_length, buf.sector, DF_PAGE_SIZE);
|
2018-02-02 16:35:18 -04:00
|
|
|
|
|
|
|
|
|
cs_release();
|
2018-03-19 07:40:05 -03:00
|
|
|
|
|
2018-02-02 16:35:18 -04:00
|
|
|
|
uint16_t i;
|
|
|
|
|
for(i=0; i<DF_PAGE_SIZE;i++){
|
|
|
|
|
ptr[i] = ~buf.sector[i]; // let filesystem will be inverted, this allows extend files without having to Read-Modify-Write on FAT
|
2018-03-19 07:40:05 -03:00
|
|
|
|
} // original: 0xFF is clear and 0 can be programmed any time
|
2018-02-02 16:35:18 -04:00
|
|
|
|
// inverted: 0 is clear and 1 can be programmed any time
|
|
|
|
|
// to mark cluster as used it should be set 1 in the FAT. Also new entries in dirs can be created without RMW
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool write_page(const uint8_t *ptr, uint32_t pageNum){
|
|
|
|
|
uint32_t PageAdr = pageNum * DF_PAGE_SIZE;
|
|
|
|
|
|
2018-03-19 07:40:05 -03:00
|
|
|
|
{
|
|
|
|
|
uint16_t i;
|
|
|
|
|
for(i=0; i<DF_PAGE_SIZE;i++){
|
|
|
|
|
buf.sector[i] = ~ptr[i]; // let filesystem will be inverted, this allows extend files without having to Read-Modify-Write on FAT
|
|
|
|
|
}
|
2018-02-02 16:35:18 -04:00
|
|
|
|
}
|
2018-03-19 07:40:05 -03:00
|
|
|
|
|
2018-02-02 16:35:18 -04:00
|
|
|
|
Flash_Jedec_WriteEnable();
|
|
|
|
|
|
|
|
|
|
if (!cs_assert()) return 0;
|
|
|
|
|
|
2018-03-19 07:40:05 -03:00
|
|
|
|
uint8_t i = addr_cmd_start;
|
|
|
|
|
|
|
|
|
|
buf.cmd[i++] = JEDEC_PAGE_WRITE;
|
|
|
|
|
if(addr_4bit) {
|
2018-04-11 05:01:16 -03:00
|
|
|
|
buf.cmd[i++] = (PageAdr >> 24) & 0xff;
|
2018-03-19 07:40:05 -03:00
|
|
|
|
}
|
|
|
|
|
buf.cmd[i++] = (PageAdr >> 16) & 0xff;
|
|
|
|
|
buf.cmd[i++] = (PageAdr >> 8) & 0xff;
|
|
|
|
|
buf.cmd[i++] = (PageAdr >> 0) & 0xff;
|
2018-02-02 16:35:18 -04:00
|
|
|
|
|
|
|
|
|
#if 0
|
2018-03-19 07:40:05 -03:00
|
|
|
|
write_spi_multi(&buf.cmd[addr_cmd_start], addr_cmd_length);
|
2018-02-02 16:35:18 -04:00
|
|
|
|
write_spi_multi(buf.sector, DF_PAGE_SIZE);
|
|
|
|
|
#else
|
2018-03-19 07:40:05 -03:00
|
|
|
|
write_spi_multi(&buf.cmd[addr_cmd_start], addr_cmd_length + DF_PAGE_SIZE);
|
2018-02-02 16:35:18 -04:00
|
|
|
|
#endif
|
|
|
|
|
cs_release();
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool erase_page(uint16_t pageNum)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
Flash_Jedec_WriteEnable();
|
|
|
|
|
|
|
|
|
|
uint32_t PageAdr = pageNum * DF_PAGE_SIZE;
|
|
|
|
|
|
2018-03-19 07:40:05 -03:00
|
|
|
|
uint8_t i=0;
|
|
|
|
|
buf.cmd[i++] = erase_cmd;
|
|
|
|
|
if(addr_4bit) {
|
|
|
|
|
buf.cmd[i++] = (PageAdr >> 24) & 0xff;
|
|
|
|
|
}
|
|
|
|
|
buf.cmd[i++] = (PageAdr >> 16) & 0xff;
|
|
|
|
|
buf.cmd[i++] = (PageAdr >> 8) & 0xff;
|
|
|
|
|
buf.cmd[i++] = (PageAdr >> 0) & 0xff;
|
2018-02-02 16:35:18 -04:00
|
|
|
|
|
|
|
|
|
if (!cs_assert()) return 0;
|
2018-03-19 07:40:05 -03:00
|
|
|
|
write_spi_multi(buf.cmd, addr_cmd_length);
|
2018-02-02 16:35:18 -04:00
|
|
|
|
cs_release();
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ChipErase()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
buf.cmd[0] = JEDEC_BULK_ERASE;
|
|
|
|
|
|
|
|
|
|
Flash_Jedec_WriteEnable();
|
|
|
|
|
|
|
|
|
|
if (!cs_assert()) return;
|
|
|
|
|
|
|
|
|
|
write_spi_multi(buf.cmd, 1);
|
|
|
|
|
|
|
|
|
|
chip_is_clear=true;
|
|
|
|
|
|
|
|
|
|
cs_release();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t sd_getSectorCount(uint32_t *ptr){
|
|
|
|
|
|
|
|
|
|
uint8_t capacity = df_device & 0xFF;
|
|
|
|
|
uint8_t memtype = (df_device>>8) & 0xFF;
|
|
|
|
|
uint32_t size=0;
|
|
|
|
|
|
|
|
|
|
const char * mfg=NULL;
|
|
|
|
|
|
|
|
|
|
switch(df_manufacturer){
|
|
|
|
|
case 0xEF: // Winbond Serial Flash
|
|
|
|
|
if (memtype == 0x40) {
|
|
|
|
|
mfg="Winbond";
|
|
|
|
|
size = (1 << ((capacity & 0x0f) + 8));
|
|
|
|
|
/*
|
|
|
|
|
const uint8_t _capID[11] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x43};
|
|
|
|
|
const uint32_t _memSize[11] = {64L*K, 128L*K, 256L*K, 512L*K, 1L*M, 2L*M, 4L*M, 8L*M, 16L*M, 32L*M, 8L*M}; megabytes
|
|
|
|
|
const uint32_t _memSize[11] = {64L*K, 128L*K, 256L*K, 512L*K, 1L*M, 2L*M, 4L*M, 8L*M, 16L*M, 32L*M, 8L*M}; megabytes
|
|
|
|
|
*/
|
|
|
|
|
erase_size=4096;
|
|
|
|
|
erase_cmd=JEDEC_SECTOR_ERASE;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 0xbf: // SST
|
|
|
|
|
if (memtype == 0x25) {
|
|
|
|
|
mfg="Microchip";
|
|
|
|
|
size = (1 << ((capacity & 0x07) + 12));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0x20: // micron
|
|
|
|
|
if (memtype == 0xba){// JEDEC_ID_MICRON_N25Q128 0x20ba18
|
|
|
|
|
mfg="Micron";
|
|
|
|
|
size = (1 << ((capacity & 0x0f) + 8));
|
|
|
|
|
erase_size=4096;
|
|
|
|
|
erase_cmd=JEDEC_SECTOR_ERASE;
|
|
|
|
|
} else if(memtype==0x20) { // JEDEC_ID_MICRON_M25P16 0x202015
|
|
|
|
|
mfg="Micron";
|
|
|
|
|
size = (1 << ((capacity & 0x0f) + 8));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0xC2: //JEDEC_ID_MACRONIX_MX25L6406E 0xC22017
|
|
|
|
|
if (memtype == 0x20) {
|
|
|
|
|
mfg="MACRONIX";
|
|
|
|
|
size = (1 << ((capacity & 0x0f) + 8));
|
|
|
|
|
erase_size=4096;
|
|
|
|
|
erase_cmd=JEDEC_SECTOR_ERASE;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0x9D: // ISSI
|
|
|
|
|
if (memtype == 0x40 || memtype == 0x30) {
|
|
|
|
|
mfg = "ISSI";
|
|
|
|
|
size = (1 << ((capacity & 0x0f) + 8));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(mfg && size) {
|
|
|
|
|
printf("%s SPI Flash found sectors=%ld\n", mfg, size);
|
|
|
|
|
} else {
|
|
|
|
|
printf("unknown Flash! mfg=%x type=%x cap=%x\n ",df_manufacturer, memtype, capacity);
|
|
|
|
|
size = BOARD_DATAFLASH_PAGES; // as defined
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-19 07:40:05 -03:00
|
|
|
|
if(size>65537) {
|
|
|
|
|
addr_cmd_length = 5;
|
|
|
|
|
addr_cmd_start = 0;
|
|
|
|
|
addr_4bit = true;
|
|
|
|
|
Flash_Enter4B_Mode();
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-02 16:35:18 -04:00
|
|
|
|
if(erase_size > MAX_ERASE_SIZE) size -= (erase_size/DF_PAGE_SIZE); // reserve for RMW ops
|
|
|
|
|
|
|
|
|
|
*ptr = size / (FAT_SECTOR_SIZE/DF_PAGE_SIZE); // in 512b blocks
|
|
|
|
|
|
|
|
|
|
return RES_OK;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
Public Functions
|
|
|
|
|
|
|
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* Initialize disk drive */
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
DSTATUS sd_initialize () {
|
|
|
|
|
static bool initialized=0;
|
|
|
|
|
|
|
|
|
|
if(initialized) return Stat;
|
|
|
|
|
|
|
|
|
|
ReadManufacturerID();
|
|
|
|
|
|
|
|
|
|
sd_getSectorCount(&sd_max_sectors);
|
|
|
|
|
|
|
|
|
|
initialized=1;
|
|
|
|
|
|
|
|
|
|
Stat=0;
|
|
|
|
|
return Stat;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* Get disk status */
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
DSTATUS sd_status (){
|
|
|
|
|
|
|
|
|
|
return Stat; /* Return disk status */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* Read sector(s) */
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
DRESULT sd_read (
|
|
|
|
|
uint8_t *buff, /* Pointer to the data buffer to store read data */
|
|
|
|
|
uint32_t sector, /* Start sector number (LBA) */
|
|
|
|
|
uint16_t count /* Number of sectors to read (1..128) */
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if (!count) return RES_PARERR; /* Check parameter */
|
|
|
|
|
if (Stat & STA_NOINIT) return RES_NOTRDY; /* Check if drive is ready */
|
|
|
|
|
if(sector > sd_max_sectors) return RES_PARERR; /* Check parameter */
|
|
|
|
|
|
|
|
|
|
count *= FAT_SECTOR_SIZE/DF_PAGE_SIZE; // 256bytes page from 512byte sector
|
|
|
|
|
sector *= FAT_SECTOR_SIZE/DF_PAGE_SIZE;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
if(!read_page(buff, sector)) break;
|
|
|
|
|
buff += DF_PAGE_SIZE;
|
|
|
|
|
sector += 1;
|
|
|
|
|
} while(--count);
|
|
|
|
|
|
|
|
|
|
return count ? RES_ERROR : RES_OK; /* Return result */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* Write sector(s) */
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool sd_move_block(uint32_t sec_from, uint32_t sec_to, const uint8_t *buff, uint32_t sec, uint16_t count);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
в системе нет памяти, а выполнять Read-Modify-Write для флешки как-то надо. придется использовать свободный блок
|
|
|
|
|
флеши для буфферизации. Ресурс это конечно уменьшит, зато быстрее перепаяют на нормальную :)
|
|
|
|
|
по-уму надо читать FAT и выбирать случайный свободный кластер, но использование для этого функций самой FatFs
|
|
|
|
|
приводит к смещению окна в неожиданное время, например когда запись вызывается для сброса измененных структур FAT
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
bool sd_move_block(uint32_t sec_from, uint32_t sec_to, const uint8_t *buff, uint32_t sec, uint16_t count){
|
|
|
|
|
|
|
|
|
|
if(!erase_page(sec_to)) return RES_ERROR;
|
|
|
|
|
|
|
|
|
|
uint8_t *sbuffer = malloc(DF_PAGE_SIZE); // read just the needed sector, not in stack for not in CCM
|
|
|
|
|
if(!sbuffer) return false;
|
|
|
|
|
|
|
|
|
|
uint16_t cnt;
|
|
|
|
|
|
|
|
|
|
for(cnt=erase_size/DF_PAGE_SIZE; cnt>0; cnt--){
|
|
|
|
|
if(buff && sec_from >= sec && sec_from < (sec + count) ) { // if replacement data fit to sector
|
|
|
|
|
memcpy(sbuffer, buff, DF_PAGE_SIZE); // will use it
|
|
|
|
|
buff+=DF_PAGE_SIZE;
|
|
|
|
|
} else { // read from source
|
|
|
|
|
if(!read_page(sbuffer, sec_from)) break;
|
|
|
|
|
}
|
|
|
|
|
if(!write_page(sbuffer, sec_to)) break;
|
|
|
|
|
sec_from++;
|
|
|
|
|
sec_to++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(sbuffer);
|
|
|
|
|
|
|
|
|
|
return cnt==0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DRESULT sd_write (
|
|
|
|
|
const uint8_t *buff, /* Ponter to the data to write */
|
|
|
|
|
uint32_t sector, /* Start sector number (LBA) */
|
|
|
|
|
uint16_t count /* Number of sectors to write (1..128) */
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
if (!count) return RES_PARERR; /* Check parameter */
|
|
|
|
|
if (Stat & STA_NOINIT) return RES_NOTRDY; /* Check drive status */
|
|
|
|
|
if(sector > sd_max_sectors) return RES_PARERR; /* Check parameter */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint16_t pos = sector % erase_size/FAT_SECTOR_SIZE; // number of sector in erase block
|
|
|
|
|
if( pos == 0 && count >= erase_size/FAT_SECTOR_SIZE){ // begin of erase block - write full cluster
|
|
|
|
|
count *= FAT_SECTOR_SIZE/DF_PAGE_SIZE; // 256bytes page from 512byte sector
|
|
|
|
|
sector *= FAT_SECTOR_SIZE/DF_PAGE_SIZE;
|
|
|
|
|
if(!erase_page(sector)) return RES_ERROR;
|
|
|
|
|
do {
|
|
|
|
|
if(!write_page(buff, sector)) break;
|
|
|
|
|
buff += DF_PAGE_SIZE;
|
|
|
|
|
sector += 1;
|
|
|
|
|
} while(--count);
|
|
|
|
|
|
|
|
|
|
} else { // read-modify-write
|
|
|
|
|
uint8_t *ptr;
|
|
|
|
|
bool need_erase = false; // check for free space for write
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
uint8_t *sbuffer = malloc(FAT_SECTOR_SIZE*count); // read just the needed sectors
|
|
|
|
|
if(!sbuffer) return RES_ERROR; // no memory at all
|
|
|
|
|
|
|
|
|
|
ptr = sbuffer;
|
|
|
|
|
|
|
|
|
|
uint32_t r_sector = sector;
|
|
|
|
|
uint16_t r_count = count;
|
|
|
|
|
|
|
|
|
|
r_sector *= FAT_SECTOR_SIZE/DF_PAGE_SIZE;
|
|
|
|
|
r_count *= FAT_SECTOR_SIZE/DF_PAGE_SIZE; // read data will be rewritten
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
if(!read_page(ptr, r_sector)) break;
|
|
|
|
|
ptr += DF_PAGE_SIZE;
|
|
|
|
|
r_sector += 1;
|
|
|
|
|
} while(--r_count);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const uint8_t *pp;
|
|
|
|
|
for(ptr = sbuffer, pp=buff; ptr < sbuffer+FAT_SECTOR_SIZE*count;ptr++,pp++){
|
|
|
|
|
// if(~*ptr & *pp){ // у нас не должно быть нулей там где нужна 1
|
|
|
|
|
// filesystem is inverted, so - у нас не должно быть 1 там где нужен 0
|
|
|
|
|
// пример: чистая FF - инверсия 00, можно писАть любой байт
|
|
|
|
|
// считали F0 - инверсия 0F - можно писАть *0
|
|
|
|
|
if(*ptr & *pp){
|
|
|
|
|
need_erase=true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
free(sbuffer);// don't need more
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(need_erase){// write do dirty block
|
|
|
|
|
|
|
|
|
|
uint8_t *cluster = malloc(erase_size);
|
|
|
|
|
if(!cluster) { // we can try to allocate up to 64K so absense of memory should not be error!
|
|
|
|
|
if(erase_size <= MAX_ERASE_SIZE) return RES_ERROR; // we have no reserved page
|
|
|
|
|
|
|
|
|
|
// we can use any free sector of flash as buffer, too
|
|
|
|
|
uint32_t fr_sec = sd_max_sectors * (FAT_SECTOR_SIZE/DF_PAGE_SIZE); // the last page
|
|
|
|
|
|
|
|
|
|
// read data from beginning of erase block up to part that will be rewritten
|
|
|
|
|
uint32_t r_sector = (sector * (FAT_SECTOR_SIZE/DF_PAGE_SIZE)) & ~(erase_size/DF_PAGE_SIZE - 1);
|
|
|
|
|
|
|
|
|
|
// move data to sectors of free block
|
|
|
|
|
if(!sd_move_block(r_sector, fr_sec, NULL, 0, 0)) return RES_ERROR;
|
|
|
|
|
// move back with inserting of data to write
|
|
|
|
|
if(!sd_move_block(fr_sec, r_sector, buff, sector * (FAT_SECTOR_SIZE/DF_PAGE_SIZE), count * (FAT_SECTOR_SIZE/DF_PAGE_SIZE))) return RES_ERROR; // move back
|
|
|
|
|
count=0; // all OK
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else { // we have enough memory for cluster buffer
|
|
|
|
|
|
|
|
|
|
ptr = cluster;
|
|
|
|
|
|
|
|
|
|
// read data from beginning of erase block up to part that will be rewritten
|
|
|
|
|
uint32_t r_sector = (sector * (FAT_SECTOR_SIZE/DF_PAGE_SIZE)) & ~(erase_size/DF_PAGE_SIZE - 1);
|
|
|
|
|
uint32_t w_sector = r_sector;
|
|
|
|
|
uint16_t r_count = erase_size/DF_PAGE_SIZE; // read full page
|
|
|
|
|
do {
|
|
|
|
|
if(r_sector >= sector * (FAT_SECTOR_SIZE/DF_PAGE_SIZE)) break;
|
|
|
|
|
if(!read_page(ptr, r_sector)) break;
|
|
|
|
|
ptr += DF_PAGE_SIZE;
|
|
|
|
|
r_sector += 1;
|
|
|
|
|
} while(--r_count);
|
|
|
|
|
|
|
|
|
|
// Yes I know that we could to go out from the buffer end - but this used only for FAT_FS which writes FAT per one sector
|
|
|
|
|
// memcpy(cluster+FAT_SECTOR_SIZE*pos, (uint8_t *)buff, FAT_SECTOR_SIZE*count); // insert sectors to write to right place
|
|
|
|
|
memcpy(ptr, (uint8_t *)buff, FAT_SECTOR_SIZE*count); // insert sectors to write to right place
|
|
|
|
|
|
|
|
|
|
// skip part that will be rewritten
|
|
|
|
|
r_count -= (FAT_SECTOR_SIZE/DF_PAGE_SIZE) * count;
|
|
|
|
|
r_sector += (FAT_SECTOR_SIZE/DF_PAGE_SIZE) * count;
|
|
|
|
|
ptr += FAT_SECTOR_SIZE * count;
|
|
|
|
|
|
|
|
|
|
// read the tail
|
|
|
|
|
while(r_count) {
|
|
|
|
|
if(!read_page(ptr, r_sector)) break;
|
|
|
|
|
ptr += DF_PAGE_SIZE;
|
|
|
|
|
r_sector += 1;
|
|
|
|
|
--r_count;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if(r_count) return RES_ERROR;
|
|
|
|
|
|
|
|
|
|
// erase page
|
|
|
|
|
if(!erase_page(w_sector)) return RES_ERROR;
|
|
|
|
|
|
|
|
|
|
ptr = cluster;
|
|
|
|
|
uint16_t w_count = erase_size/DF_PAGE_SIZE;
|
|
|
|
|
do {
|
|
|
|
|
if(!write_page(ptr, w_sector)) break;
|
|
|
|
|
ptr += DF_PAGE_SIZE;
|
|
|
|
|
w_sector += 1;
|
|
|
|
|
} while(--w_count);
|
|
|
|
|
|
|
|
|
|
count = w_count;
|
|
|
|
|
|
|
|
|
|
free(cluster);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else { // block is ready to write - just write needed part
|
|
|
|
|
count *= FAT_SECTOR_SIZE/DF_PAGE_SIZE; // 256bytes page from 512byte sector
|
|
|
|
|
sector *= FAT_SECTOR_SIZE/DF_PAGE_SIZE;
|
|
|
|
|
do {
|
|
|
|
|
if(!write_page(buff, sector)) break;
|
|
|
|
|
buff += DF_PAGE_SIZE;
|
|
|
|
|
sector += 1;
|
|
|
|
|
} while(--count);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return count ? RES_ERROR : RES_OK; /* Return result */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* Miscellaneous drive controls other than data read/write */
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
static inline void enqueue_flash_erase(uint32_t from, uint32_t to){
|
|
|
|
|
|
|
|
|
|
int16_t new_wp = fe_write_ptr+1;
|
|
|
|
|
if(new_wp >= FLASH_ERASE_QUEUE_SIZE) { // move write pointer
|
|
|
|
|
new_wp=0; // ring
|
|
|
|
|
}
|
|
|
|
|
while(new_wp == fe_read_ptr) hal_yield(0); // buffer overflow
|
|
|
|
|
|
|
|
|
|
flash_erase_queue[fe_write_ptr].b = from;
|
|
|
|
|
flash_erase_queue[fe_write_ptr].e = to;
|
|
|
|
|
|
|
|
|
|
fe_write_ptr=new_wp; // move forward
|
|
|
|
|
|
|
|
|
|
hal_set_task_active(_flash_erase_task);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DRESULT sd_ioctl (
|
|
|
|
|
uint8_t ctl, /* Control command code */
|
|
|
|
|
void *buff /* Pointer to the conrtol data */
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
DRESULT res;
|
|
|
|
|
|
|
|
|
|
uint8_t *ptr = (uint8_t *)buff;
|
|
|
|
|
|
|
|
|
|
res = RES_ERROR;
|
|
|
|
|
|
|
|
|
|
if (ctl== CTRL_POWER){
|
|
|
|
|
|
|
|
|
|
switch (*ptr) {
|
|
|
|
|
case 0: // Sub control code == 0 (POWER_OFF)
|
|
|
|
|
res = RES_OK;
|
|
|
|
|
break;
|
|
|
|
|
case 1: // Sub control code == 1 (POWER_ON)
|
|
|
|
|
res = RES_OK;
|
|
|
|
|
break;
|
|
|
|
|
case 2: // Sub control code == 2 (POWER_GET)
|
|
|
|
|
*(ptr + 1) = (uint8_t) MMC_CD;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
res = RES_PARERR;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
if (Stat & STA_NOINIT) return RES_NOTRDY; /* Check if drive is ready */
|
|
|
|
|
|
|
|
|
|
switch (ctl) {
|
|
|
|
|
case CTRL_SYNC : /* Wait for end of internal write process of the drive */
|
|
|
|
|
res = RES_OK;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GET_SECTOR_COUNT : /* Get drive capacity in unit of sector (uint32_t ) */
|
|
|
|
|
*(uint32_t *)buff = sd_max_sectors;
|
|
|
|
|
res = RES_OK;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GET_BLOCK_SIZE : /* Get erase block size in unit of FAT sector (uint32_t ) */
|
|
|
|
|
*(uint32_t *)buff = erase_size/FAT_SECTOR_SIZE;
|
|
|
|
|
res = RES_OK;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GET_SECTOR_SIZE:
|
|
|
|
|
*(uint32_t *)buff = FAT_SECTOR_SIZE; // always
|
|
|
|
|
res = RES_OK;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CTRL_TRIM : { /* Erase a block of sectors (used when _USE_TRIM == 1) */
|
|
|
|
|
uint32_t start_sector = ((uint32_t *)buff)[0];
|
|
|
|
|
uint32_t end_sector = ((uint32_t *)buff)[1];
|
|
|
|
|
uint32_t block, last_block=-1;
|
|
|
|
|
|
|
|
|
|
if(start_sector>=sd_max_sectors || end_sector>=sd_max_sectors) return RES_PARERR;
|
|
|
|
|
if(chip_is_clear) { // just after full erase
|
|
|
|
|
res = RES_OK;
|
|
|
|
|
chip_is_clear=false;
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(hal_is_armed()) return RES_OK;
|
|
|
|
|
|
|
|
|
|
#if 0 // in separate thread
|
|
|
|
|
|
|
|
|
|
erase_page(start_sector * (FAT_SECTOR_SIZE/DF_PAGE_SIZE)); // clear 1st sector of DataFlash
|
|
|
|
|
|
|
|
|
|
enqueue_flash_erase(start_sector+(erase_size/DF_PAGE_SIZE), end_sector); //and enqueue
|
|
|
|
|
#else
|
|
|
|
|
uint32_t sector;
|
|
|
|
|
for(sector=start_sector; sector <= end_sector;sector++){
|
|
|
|
|
uint32_t df_sect = sector * (FAT_SECTOR_SIZE/DF_PAGE_SIZE); // sector in DataFlash
|
|
|
|
|
block = df_sect / (erase_size/DF_PAGE_SIZE); // number of EraseBlock
|
|
|
|
|
if(last_block!=block){
|
|
|
|
|
last_block=block;
|
|
|
|
|
if(!erase_page(df_sect)) return RES_ERROR;
|
|
|
|
|
putch('.');
|
|
|
|
|
extern void digitalToggle(uint8_t pin);
|
|
|
|
|
|
|
|
|
|
digitalToggle(HAL_GPIO_A_LED_PIN);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
res = RES_OK;
|
|
|
|
|
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
case CTRL_FORMAT:{
|
|
|
|
|
ChipErase();
|
|
|
|
|
res = RES_OK;
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
res = RES_PARERR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* Device timer function */
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
|
/* This function must be called from timer interrupt routine in period
|
|
|
|
|
/ of 1 ms to generate card control timing.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void sd_timerproc (void)
|
|
|
|
|
{
|
|
|
|
|
uint16_t n;
|
|
|
|
|
|
|
|
|
|
n = Timer1; /* 1kHz decrement timer stopped at 0 */
|
|
|
|
|
if (n) Timer1 = --n;
|
|
|
|
|
n = Timer2;
|
|
|
|
|
if (n) Timer2 = --n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif //defined(BOARD_DATAFLASH_FATFS)
|