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