http://narodstream.ru/avr-urok-33-spi-k ... t-chast-1/
для начала хотя бы освоить чтение запись в сектора карты, а потом уже разбираться с файловой системой.
Написал проект (см. ATmega328P + SD.zip (Atmel Studio 7 + эмуляция Proteus 8.7 + схема)) и собрал макетку
(см. Схема.pdf), от примера отличается - с секторами я пытаюсь работать сразу на ATmega328P а не на ATmega8A и экран у меня работает через I2C шину, а не напрямую, в качестве разъёма карточки - припаял переходник SD-microSD, т.е. работать
предполагается только с форматом microSD.
В Протеусе работает нормально, в железе нет.
Алгоритм работы макетной платы: по очереди бесконечно зажигаюnся светодиоды D1...D3, выводятся тестовые строки на экран, запускается таймер1, в нем опрашивается кнопка S3 на PC0, нажатие кнопки на S3 очищает экран, в первой строке
экрана выводятся три числа с номерами ошибок, если ошибок нет то все числа нули, во второй строке четыре байта адреса
начала сектора 512 байт в шестнадцатеричном формате, начиная с сектора 4096, в третьей строке начало буфера чтения забитого символами "c", в четвёртой строке начало буфера чтения после считывание туда сектора из карточки, если всё нормально, видно начало тестового паттерна, каждое следующие нажатие на кнопку увеличивает адрес на один сектор.
Проверял на четырёх карточках:
1) microSD на 64Мб - первое нажатие на кнопку ошибка инициализации 3, последующие пишется-читается нормально
2) microSD на 1Гб - всегда ошибка инициализации 3
3) microSD на 2Гб - первое нажатие на кнопку ошибка инициализации 3, последующие пишется-читается нормально
4) microSDHC на 4Гб - всегда ошибка инициализации 4
Какие будут идеи ?
Есть подозрение что проблемма только в неправильной инициализации карточек.
Листинг файла main.cpp:
Спойлер
Код: Выделить всё
//*******************************************************************************
#include "main.h"
//*******************************************************************************
#define MOSI 3
#define MISO 4
#define SCK 5
#define SS 2
//*******************************************************************************
char buffer[512] = "SD test SD test SD яяяяяяяя1111111199999999яяяяяяяя1111111199999999яяяяяяяя1111111199999999яяяяяяяя1111111199999999яяяяяяяя1111111199999999яяяяяяяя1111111199999999яяяяяяяя1111111199999999яяяяяяяя1111111199999999яяяяяяяя1111111199999999яяяяяяяя1111111199999999яяяяяяяя1111111199999999яяяяяяяя1111111199999999яяяяяяяя1111111199999999яяяяяяяя1111111199999999яяяяяяяя1111111199999999яяяяяяяя1111111199999999яяяяяяяя1111111199999999яяяяяяяя1111111199999999яяяяяяяя1111111199999999яяяяяяяя1111111199999999яяяяяяяя1111"; //Буфер данных для записи/чтения
char buffer2[512] ={}; //Буфер данных для чтения
//*******************************************************************************
signed char stethik_kn = 0;
uint32_t SD_adr = 2097152; // будем записывать сектора по 512 байт начиная с сектора 4096
//*******************************************************************************
void port_ini(void) //настройка четырёх выводов порта для софтового SPI
{
PORTB|=(1<<SS)|(1<<MISO)|(1<<MOSI);
DDRB|=(1<<SS)|(1<<MOSI)|(1<<SCK);
}
void SPI_SendByte (unsigned char byte)
{
/////////////////////////////////////////////////////////////////////////
for (unsigned char i=0; i<8; i++) //движемся по битам байта
{
if ((byte&0x80)==0x00)//проверяем левый бит
PORTB&=~(1<<MOSI); //если 0, то выставляем 0 на шине
else PORTB|=(1<<MOSI); //если 1, то выставляем 1
byte<<=1; //сдвигаем влево, в сторону старшего для проверки следующего бита
PORTB|=(1<<SCK); //фронт на ножке SCK
asm("nop"); //1 такт подождём
// asm("nop"); //1 такт подождём
// asm("nop"); //1 такт подождём
PORTB&=~(1<<SCK); //спад на ножке SCK
}
/////////////////////////////////////////////////////////////////////////
}
unsigned char SPI_ReceiveByte (void)
{
//////////////////////////////////////////////////////////////////////////////
unsigned char result=0;
for(unsigned char i=0; i<8; i++)
{
PORTB|=(1<<SCK); //фронт на ножке SCK
result<<=1; //сдвигаем влево, чтобы записать новый бит
if ((PINB&(1<<MISO))!=0x00) //запишем новый бит (в младший разряд)
result=result|0x01; //Считать бит данных
PORTB&=~(1<<SCK); //спад на ножке SCK
asm("nop"); //1 такт подождём
// asm("nop"); //1 такт подождём
// asm("nop"); //1 такт подождём
}
return result;
/////////////////////////////////////////////////////////////////////////////
}
unsigned char SD_cmd(char dt0, char dt1, char dt2, char dt3, char dt4, char dt5) //передача команды (пример даташит стр 40)
{
////////////////////////////////////////////////////////////////////////////
unsigned char result;
long int cnt;
SPI_SendByte (dt0); //Индекс
SPI_SendByte (dt1); //Аргумент
SPI_SendByte (dt2);
SPI_SendByte (dt3);
SPI_SendByte (dt4);
SPI_SendByte (dt5); //контрольная сумма
cnt=0;
do //Ждем ответ в формате R1 (даташит стр 109)
{
result=SPI_ReceiveByte();
cnt++;
}
while ( ((result&0x80)!=0x00)&&(cnt<0xFFFF) );
return result;
////////////////////////////////////////////////////////////////////////////
}
unsigned char SD_Init(void)
{
///////////////////////////////////////////////////////////////////////////////////////////
unsigned char temp;
long int cnt;
for (unsigned char i=0; i<10; i++) { SPI_SendByte(0xFF); } //80 импульсов (не менее 74) Даташит стр 94
PORTB&=~(1<<SS); //опускаем SS
temp=SD_cmd (0x40,0x00,0x00,0x00,0x00,0x95); // CMD0 Даташит стр 102 и 96
if (temp!=0x01) { return 3;} //Выйти, если ответ не 0х01 (результат
SPI_SendByte (0xFF);
cnt=0;
do
{
temp=SD_cmd (0x41,0x00,0x00,0x00,0x00,0x95); //CMD1 (аналогично CMD0, только индекс меняется)
SPI_SendByte (0xFF);
cnt++;
}
while ( (temp!=0x00)&&(cnt<0xFFFF) ); //Ждем ответа R1
if (cnt>=0xFFFF) {return 4;}
return 0;
////////////////////////////////////////////////////////////////////////////////////////////
}
unsigned char SD_Write_Block (char* bf, unsigned char dt1, unsigned char dt2, unsigned char dt3, unsigned char dt4)
{
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unsigned char result;
long int cnt;
result=SD_cmd(0x58,dt1,dt2,dt3,dt4,0x95); //CMD24 даташит стр 51 и 97-98
if (result!=0x00) return 6; //Выйти, если результат не 0x00
SPI_SendByte (0xFF);
SPI_SendByte (0xFE); //Начало буфера
for (cnt=0;cnt<512;cnt++) SPI_SendByte(bf[cnt]); //Данные
SPI_SendByte (0xFF); //Котрольная сумма
SPI_SendByte (0xFF);
result=SPI_ReceiveByte();
if ((result&0x05)!=0x05) return 6; //Выйти, если результат не 0x05 (Даташит стр 111)
cnt=0;
do //Ждем окончания состояния BUSY
{
result=SPI_ReceiveByte();
cnt++;
}
while ( (result!=0xFF)&&(cnt<0xFFFF) );
if (cnt>=0xFFFF) return 6;
return 0;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
unsigned char SD_Read_Block (char* bf, unsigned char dt1, unsigned char dt2, unsigned char dt3, unsigned char dt4)
{
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unsigned char result;
long int cnt;
result=SD_cmd (0x51,dt1,dt2,dt3,dt4,0x95); //CMD17 даташит стр 50 и 96
if (result!=0x00) return 5; //Выйти, если результат не 0x00
SPI_SendByte (0xFF);
cnt=0;
do //Ждем начала блока
{
result=SPI_ReceiveByte();
cnt++;
}
while ( (result!=0xFE)&&(cnt<0xFFFF) );
if (cnt>=0xFFFF) return 5;
for (cnt=0;cnt<512;cnt++) bf[cnt]=SPI_ReceiveByte(); //получаем байты блока из шины в буфер
SPI_ReceiveByte(); //Получаем контрольную сумму
SPI_ReceiveByte();
return 0;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
void SD_work (void) //основная функция чтения-записи SD
{
//-----------------------------------------------------------------
unsigned char result;
char str[20];
unsigned char SD_adr_0 = 0;
unsigned char SD_adr_1 = 0;
unsigned char SD_adr_2 = 0;
unsigned char SD_adr_3 = 0;
// SD_adr_0 = SD_adr & 0xff;
// SD_adr_1 = (SD_adr & 0xff00) >> 8;
// SD_adr_2 = (SD_adr & 0xff0000) >> 16;
// SD_adr_3 = (SD_adr & 0xff000000) >> 24;
SD_adr_0 = SD_adr; //разбивка адреса uint32_t на 4 байта
SD_adr_1 = SD_adr >> 8;
SD_adr_2 = SD_adr >> 16;
SD_adr_3 = SD_adr >> 24;
sprintf(str,"adr: %02x %02x %02x %02x",SD_adr_3, SD_adr_2, SD_adr_1, SD_adr_0); //вывод адреса начала блока во 2 сторку экрана
clearlcd();//очистим дисплей
setpos(0,1);
str_lcd(str);
for (unsigned int i=0; i<512; i++) //затирание buffer2 символами "с" и вывод первых 19 в 3 строку экрана
{
buffer2[i] = 'c';
}
for (unsigned char i=0; i<=19; i++)
{
str[i] = buffer2[i];
}
setpos(0,2);
str_lcd(str);
port_ini(); //инициализация SD карты, вывод номера ошибки в первую строку экрана (первое число) 0 - нет ошибки
result=SD_Init();
sprintf(str,"error: %d",result);
//clearlcd();//очистим дисплей
setpos(0,0);
str_lcd(str);
result=SD_Write_Block(buffer,SD_adr_3,SD_adr_2,SD_adr_1,SD_adr_0);//Запишем блок из буфера, вывод номера ошибки в первую строку экрана (второе число) 0 - нет ошибки
sprintf(str,"%d",result);
setpos(9,0);
str_lcd(str);
result=SD_Read_Block(buffer2,SD_adr_3,SD_adr_2,SD_adr_1,SD_adr_0); //Считаем блок в буфер, вывод номера ошибки в первую строку экрана (третье число) 0 - нет ошибки
sprintf(str,"%d",result);
setpos(11,0);
str_lcd(str);
//_delay_ms(1000);
for (unsigned char i=0; i<=19; i++) //вывод первых 19 символолв из буффера в 4 строку экрана
{
str[i] = buffer2[i];
}
buffer2[19] = '\0';
setpos(0,3);
str_lcd(str);
//_delay_ms(1000);
SD_adr = SD_adr + 512;
//-----------------------------------------------------------------
}
//-----------------------------------------------------------------------------------------------------------------------------------
void bibip (uint32_t time_ms, uint32_t freq_hz) //пишание пьезодинамиком на PD0 (время в милисикундах, частота а Герцах)
{
//////////////////////////////////////////////////////////////
uint32_t cycles = 0;
uint32_t period = 0;
period = F_CPU / (24 * freq_hz); //24 - подобранный коэфициент в Протеусе для ATmega328P
cycles = (833 * time_ms)/period; //833 - подобранный коэфициент в Протеусе для ATmega328P
for (unsigned int i =0; i <= cycles; i++)
{
PORTD |= (1<<0);
for (unsigned int q = 0; q <= period; q++){asm("nop");}
PORTD &= ~(1<<0);
for (unsigned int q = 0; q <= period; q++){asm("nop");}
}
///////////////////////////////////////////////////////////////
}
void migalka (void) //мигание светодиодами под пишалку для отладки
{
///////////////////////////////////////////////
PORTB |= (1<<0);
bibip(50,500);
_delay_ms(500);
PORTB &= ~(1<<0);
PORTB |= (1<<1);
bibip(50,1000);
_delay_ms(500);
PORTB &= ~(1<<1);
PORTD |= (1<<1);
bibip(50,2000);
_delay_ms(500);
PORTD &= ~(1<<1);
///////////////////////////////////////////
}
//-----------------------------------------------------------------------------------------------------------------------------------
void timers_ini(void) //настройка таймера1 на 10 ms
{
///////////////////////////////////////////////////////////////////////////
TCCR1B = 0;
TCCR1A = 0;
TCCR1B |= (1<<WGM12); // устанавливаем режим СТС (сброс по совпадению) для первого таймера
// OCR1AH = 0xFF; //записываем в регистр число для сравнения - старшый байт
// OCR1AL = 0x7F; //записываем в регистр число для сравнения - младший байт
OCR1A = 196; //записываем в регистр число для сравнения
TCCR1B |= ( 1 << CS12 ) | ( 1 << CS10 );//устанавливаем предделитель на 1024
TIMSK1 |= (1<<OCIE1A); // Разрешить прерывание по совпадению OCR1A
//TIMSK1 |= (1<<OCIE1B); // Разрешить прерывание по совпадению OCR1B
//TIMSK1 |= (1<<TOIE1); // Разрешить прерывание по переполнению
/////////////////////////////////////////////////////////////////////////////
}
ISR (TIMER1_COMPA_vect ) //прерывание таймера1
{
//////////////////дрыгаем PC1 для отладки таймера в протеусе/////////
if ((PINC&0b00000010)) //проверяем состояние PC1
{
PORTC &= ~(1<<1); //записать в PC1 ноль
}
else
{
PORTC |= (1<<1); //записать в PC1 единицу
}
///////////////////////антидребезговый код кнопки на PC1/////////////////////
if (0 == (PINC&0b00000001))
{
stethik_kn++;
}
else
{
if (stethik_kn < 0)
{
stethik_kn++; return;
}
}
if ((stethik_kn > 2) && (0 == (PINC&0b00000001)))
{
stethik_kn = -100;
bibip(50,1200);
cli(); //запрещаем прерывание
SD_work();
sei();//разрешить прерывания
}
///////////////////////////////////////////////////////////////////
}
//-----------------------------------------------------------------------------------------------------------------------------------
int main(void)
{
////////////////////////////////////////////////////////////////////////////////////////////////////
DDRB |= (1<<0); //записать в PC0 единицу (что бы выставить его на выход)
PORTB &= ~(1<<0); //записать в PC0 ноль
DDRB |= (1<<1);
PORTB &= ~(1<<1);
DDRD |= (1<<0);
PORTD &= ~(1<<0);
DDRD |= (1<<1);
PORTD &= ~(1<<1);
DDRC &= ~(1<<0); //записать в PC0 ноль (что бы выставить его на вход)
PORTC |= (1<<0); //записать в PC0 единицу, для включения резистора подтяжки
DDRC |= (1<<1); //записать в PC1 единицу (что бы выставить его на выход)
PORTC &= ~(1<<1); //записать в PC1 ноль
//*****************************************************************************************************
timers_ini();
sei();//разрешить прерывания
migalka();
//-----------------------------------------------------------------
I2C_Init();//инициализируем TWI
LCD_ini(); //инициализируем дисплей
clearlcd();//очистим дисплей
setpos(0,0);
str_lcd("Hello World!");
setpos(2,1);
str_lcd("String 2");
setpos(4,2);
str_lcd("String 3");
setpos(6,3);
str_lcd("String 4");
//-----------------------------------------------------------------
while(1)
{
migalka();
}
////////////////////////////////////////////////////////////////////////////////////////////////
}
- Вложения
-
- Схема.pdf
- (57.9 КБ) 291 скачивание
-
- ATmega328P + SD.zip
- (213.58 КБ) 361 скачивание


