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

/*
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;
}