You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
299 lines
7.1 KiB
299 lines
7.1 KiB
/*
|
|
It is an open source software to implement SD routines to
|
|
small embedded systems. This is a free software and is opened for education,
|
|
research and commercial developments under license policy of following trems.
|
|
|
|
(C) 2013 vinxru (aleksey.f.morozov@gmail.com)
|
|
|
|
It is a free software and there is NO WARRANTY.
|
|
No restriction on use. You can use, modify and redistribute it for
|
|
personal, non-profit or commercial use UNDER YOUR RESPONSIBILITY.
|
|
Redistributions of source code must retain the above copyright notice.
|
|
|
|
Version 0.99 5-05-2013
|
|
*/
|
|
#ifndef F_CPU
|
|
#define F_CPU 16000000
|
|
#endif
|
|
#include <util/delay.h>
|
|
#include <avr/io.h>
|
|
#include "spi.h"
|
|
#include "sd.h"
|
|
#include "fs.h"
|
|
|
|
uint8_t sd_sdhc; /* Èñïîëüçóåòñÿ SDHC êàðòà */
|
|
|
|
/**************************************************************************
|
|
* Ïðîòîêîë SPI äëÿ ATMega8 *
|
|
* Ìîæåò îòëè÷àòüñÿ äëÿ ðàçíûõ ÌÊ. *
|
|
**************************************************************************/
|
|
|
|
/* Êóäà ïîäêëþ÷åíà ëèíèÿ CS êàðòû */
|
|
#define SD_CS_ENABLE PORTB &= ~0x01; //SS LOW
|
|
#define SD_CS_DISABLE PORTB |= 0x01; //SS HIGH
|
|
|
|
/* Ñîâìåñòèìîñòü ñ ðàçíûìè âåðñèÿìè CodeVisionAVR */
|
|
#ifndef SPI2X
|
|
#define SPI2X 0
|
|
#endif
|
|
|
|
/**************************************************************************
|
|
* Îòïðàâêà êîìàíäû *
|
|
**************************************************************************/
|
|
|
|
/* Èñïîëüçóåìûå êàìàíäû SD êàðòû */
|
|
|
|
#define GO_IDLE_STATE (0x40 | 0 )
|
|
#define SEND_IF_COND (0x40 | 8 )
|
|
#define READ_SINGLE_BLOCK (0x40 | 17)
|
|
#define WRITE_SINGLE_BLOCK (0x40 | 24)
|
|
#define SD_SEND_OP_COND (0x40 | 41)
|
|
#define APP_CMD (0x40 | 55)
|
|
#define READ_OCR (0x40 | 58)
|
|
|
|
static uint8_t sd_sendCommand(uint8_t cmd, uint32_t arg)
|
|
{
|
|
uint8_t response, retry;
|
|
|
|
/* Ðàçìåùåíèå ýòîãî êîäà òóò -4 êîìàíäû, õîòÿ âðîäå ëèøíèå ïðîâåðêè */
|
|
if(sd_sdhc == 0 && (cmd==READ_SINGLE_BLOCK || cmd==WRITE_SINGLE_BLOCK))
|
|
arg <<= 9;
|
|
|
|
/* Âûáèðàåì êàðòó */
|
|
SD_CS_ENABLE
|
|
|
|
/* Çàãîëîâîê êîìàíäû */
|
|
spi_transmit(cmd);
|
|
spi_transmit(((uint8_t*)&arg)[3]);
|
|
spi_transmit(((uint8_t*)&arg)[2]);
|
|
spi_transmit(((uint8_t*)&arg)[1]);
|
|
spi_transmit(((uint8_t*)&arg)[0]);
|
|
|
|
/* Ïàðó êîìàíä òðåáóþò CRC. Îñòàëüíûå æå êîìàíäû èãíîðèðóþò åãî, ïîýòîìó óïðîùàåì êîä */
|
|
spi_transmit(cmd == SEND_IF_COND ? 0x87 : 0x95);
|
|
|
|
/* Æäåì ïîäòâðåæäåíèå (256 òàêòîâ) */
|
|
retry = 0;
|
|
while((response = spi_receive()) == 0xFF)
|
|
if(++retry == 0) break;
|
|
|
|
/* Ðåçóëüòàò êîìàíäû READ_OCR îáðàáàòûâàåì òóò, òàê êàê â êîíöå ýòîé ôóíêöèè ìû ñíèìåì CS è ïðîïóñêàåì 1 áàéò */
|
|
if(response == 0 && cmd == READ_OCR)
|
|
{
|
|
/* 32 áèòà èç êîòîðûõ íàñ èíòåðåñóåò îäèí áèò */
|
|
sd_sdhc = spi_receive() & 0x40;
|
|
spi_receive();
|
|
spi_receive();
|
|
spi_receive();
|
|
}
|
|
|
|
/* îòïóñêàåì CS è ïàóçà â 1 áàéò*/
|
|
SD_CS_DISABLE
|
|
spi_receive();
|
|
|
|
return response;
|
|
}
|
|
|
|
/**************************************************************************
|
|
* Ïðîâåðêà ãîòîâíîñòè/íàëè÷èÿ êàðòû *
|
|
**************************************************************************/
|
|
|
|
uint8_t sd_check()
|
|
{
|
|
uint8_t i = 0;
|
|
do
|
|
{
|
|
sd_sendCommand(APP_CMD, 0);
|
|
if(sd_sendCommand(SD_SEND_OP_COND, 0x40000000) == 0) return 0;
|
|
}
|
|
while(--i);
|
|
return 1;
|
|
}
|
|
|
|
/**************************************************************************
|
|
* Èíèöèàëèçàöèÿ êàðòû (ýòà ôóíêöèÿ âûçûâàåòñÿ ôóíêöèåé sd_init) *
|
|
**************************************************************************/
|
|
|
|
static uint8_t sd_init_int()
|
|
{
|
|
uint8_t i;
|
|
|
|
/* Ñáðàñûâàåì SDHC ôëàã */
|
|
sd_sdhc = 0;
|
|
|
|
/* Ìèíèìóì 80 ïóñòûõ òàêòîâ */
|
|
for(i=10; i; --i)
|
|
spi_receive();
|
|
|
|
/* CMD0 Ïîñûëàåì êîìàíäó ñáðîñà */
|
|
if(sd_sendCommand(GO_IDLE_STATE, 0) != 1) goto abort;
|
|
|
|
/* CMD8 Óçíàåì âåðñèþ êàðòû */
|
|
i = 0;
|
|
if(sd_sendCommand(SEND_IF_COND, 0x000001AA))
|
|
i = 1;
|
|
|
|
/* CMD41 Îæèäàíèå îêîí÷àíèÿ èíèöèàëèçàöèè */
|
|
if(sd_check()) goto abort;
|
|
|
|
/* Òîëüêî äëÿ âòîðîé âåðñèè êàðòû */
|
|
if(i)
|
|
{
|
|
/* CMD58 îïðåäåëåíèå SDHC êàðòû. Îòâåò îáðàáàòûâàåòñÿ â ôóíêöèè sd_sendCommand */
|
|
if(sd_sendCommand(READ_OCR, 0) != 0) goto abort;
|
|
}
|
|
|
|
return 0;
|
|
abort:
|
|
return 1;
|
|
}
|
|
|
|
/**************************************************************************
|
|
* Èíèöèàëèçàöèÿ êàðòû *
|
|
**************************************************************************/
|
|
|
|
uint8_t sd_init()
|
|
{
|
|
|
|
LED2_DDR |= (1 << LED2_PIN);
|
|
LED1_DDR |= (1 << LED1_PIN);
|
|
|
|
uint8_t tries;
|
|
|
|
/* Îñâîáîæäàåì CS íà âñÿêèé ñëó÷àé */
|
|
SD_CS_DISABLE
|
|
|
|
/* Âêëþ÷àåì SPI */
|
|
//spi_init();
|
|
|
|
/* Äåëàåì íåñêîëüêî ïîïûòîê èíèöèàëèçàöèè */
|
|
tries = 10;
|
|
while(sd_init_int())
|
|
if(--tries == 0)
|
|
{
|
|
lastError = ERR_DISK_ERR;
|
|
return 1;
|
|
}
|
|
|
|
/* Âêë÷þàåì ìàêñèìàëüíóþ ñêîðîñòü */
|
|
spi_highSpeed();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**************************************************************************
|
|
* Îæèäàíèå îïðåäåëåííîãî áàéòà íà øèíå *
|
|
**************************************************************************/
|
|
|
|
static uint8_t sd_waitBus(uint8_t byte)
|
|
{
|
|
uint16_t retry = 0;
|
|
do
|
|
{
|
|
if(spi_receive() == byte) return 0;
|
|
}
|
|
while(++retry);
|
|
return 1;
|
|
}
|
|
|
|
/**************************************************************************
|
|
* ×òåíèå ïðîèçâîëüíîãî ó÷àñòêà ñåêòîðà *
|
|
**************************************************************************/
|
|
|
|
uint8_t sd_read(uint8_t* buffer, uint32_t sector, uint16_t offsetInSector, uint16_t length)
|
|
{
|
|
LED_R_ON();
|
|
uint8_t b;
|
|
uint16_t i;
|
|
|
|
/* Ïîñûëàåì êîìàíäó */
|
|
if(sd_sendCommand(READ_SINGLE_BLOCK, sector)) goto abort;
|
|
|
|
/* Ñðàçó æå âîçðàùàåì CS, ÷òî áû ïðèíÿòü îòâåò êîìàíäû */
|
|
SD_CS_ENABLE
|
|
|
|
/* Æäåì ñòàðòîâûé áàéò */
|
|
if(sd_waitBus(0xFE)) goto abort;
|
|
|
|
/* Ïðèíèìàåì 512 áàéò */
|
|
for(i=512; i; --i)
|
|
{
|
|
b = spi_receive();
|
|
if(offsetInSector)
|
|
{
|
|
offsetInSector--;
|
|
continue;
|
|
}
|
|
if(length == 0) continue;
|
|
length--;
|
|
*buffer++ = b;
|
|
}
|
|
|
|
/* CRC èãíîðèðóåì */
|
|
spi_receive();
|
|
spi_receive();
|
|
|
|
/* îòïóñêàåì CS è ïàóçà â 1 áàéò*/
|
|
SD_CS_DISABLE
|
|
spi_receive();
|
|
|
|
/* Îê */
|
|
LED_R_OFF();
|
|
return 0;
|
|
|
|
/* Îøèáêà è îòïóñêàåì CS.*/
|
|
abort:
|
|
SD_CS_DISABLE
|
|
lastError = ERR_DISK_ERR;
|
|
LED_R_OFF();
|
|
return 1;
|
|
}
|
|
|
|
/**************************************************************************
|
|
* Çàïèñü ñåêòîðà (512 áàéò) *
|
|
**************************************************************************/
|
|
|
|
uint8_t sd_write512(uint8_t* buffer, uint32_t sector)
|
|
{
|
|
LED_W_ON();
|
|
uint16_t n;
|
|
|
|
/* Ïîñûëàåì êîìàíäó */
|
|
if(sd_sendCommand(WRITE_SINGLE_BLOCK, sector)) goto abort;
|
|
|
|
/* Ñðàçó æå âîçðàùàåì CS, ÷òî áû îòïðàâèòü áëîê äàííûõ */
|
|
SD_CS_ENABLE
|
|
|
|
/* Ïîñûëàåì ñòàðòîâûé áàéò */
|
|
spi_transmit(0xFE);
|
|
|
|
/* Äàííûå */
|
|
for(n=512; n; --n)
|
|
spi_transmit(*buffer++);
|
|
|
|
/* CRC èãíîðèðóåòñÿ */
|
|
spi_transmit(0xFF);
|
|
spi_transmit(0xFF);
|
|
|
|
/* Îòâåò ÌÊ */
|
|
if((spi_receive() & 0x1F) != 0x05) goto abort;
|
|
|
|
/* Æäåì îêîí÷àíèÿ çàïèñè, ò.å. ïîêà íå îñâîáîäèòñÿ øèíà */
|
|
if(sd_waitBus(0xFF)) goto abort;
|
|
|
|
/* îòïóñêàåì CS è ïàóçà â 1 áàéò*/
|
|
SD_CS_DISABLE
|
|
spi_receive();
|
|
|
|
/* Îê */
|
|
LED_W_OFF();
|
|
return 0;
|
|
|
|
/* Îøèáêà.*/
|
|
abort:
|
|
SD_CS_DISABLE
|
|
lastError = ERR_DISK_ERR;
|
|
LED_W_OFF();
|
|
return 1;
|
|
}
|