Урок 33
Часть 3
SPI. Карта SD. FAT
Продолжим начатое дело по программированию и использованию флеш-карты SD. В предыдущей части нашего урока мы написали инициализацию нашей карты, теперь пришло время что-то с неё считать, а может даже и записать, пока не обращая внимание на структурирование данных в виде файловой системы.
Поэтому создадим функцию для записи блока с карты SD выше функции main(). Почему сразу записи, да потому, что читать нам ещё нечего, так как сначала надо что-то записать, чтобы потом прочитать и убедиться, что это именно то, что мы записали
unsigned char SD_Write_Block (char* bf, unsigned char dt1, unsigned char dt2, unsigned char dt3, unsigned char dt4)
{
}
Вот сколько у нас входных параметров. Почему много, потому что во-первых мы передаём указатель на место в памяти, откуда мы будем писать данные в блок флеш-памяти карты. Во-вторых, чтобы инициировать передачу блока данных во флеш-карту, мы будем передавать определённую команду, в которой предусмотрено 4 основных параметра.
Создадим в функции две переменных
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;
Теперь команда. Посмотрим техническую документацию и найдём команду для записи блока
Индекс команды у нас 24 или по-шестнадцатеричному — 0x18, первый бит, как мы знаем 0, второй всегда 1, поэтому будет 0x58.
Основным аргументом будет адрес данных. Поэтому напишем вызов функции передачи команды
long int cnt;
result=SD_cmd(0x58,dt1,dt2,dt3,dt4,0x95); //CMD24 даташит стр 51 и 97-98
Так как все параметры у нас во входе функции, то мы их пока не увидим, увидим, когда будем нашу функцию записи вызывать.
Выйдем, если что-то не то, если всё то, то вычистим мусор из регистра сдвига карты
result=SD_cmd(0x58,dt1,dt2,dt3,dt4,0x95); //CMD24 даташит стр 51 и 97-98
if (result!=0x00) return 6; //Выйти, если результат не 0x00
SPI_SendByte (0xFF);
Вообще мы будем пробовать писать данные по адресу 0x400, так как если мы вдруг будем позже пробовать 32-битную файловую систему, то в первые байти мы писать не можем, так как там служебная информация файловой системы FAT32. В 0x300 мы писать не можем также, потому что блоки у нас по 512 байт и мы просто с такого адреса писать не можем, это не адрес блока.
Передадим начало буфера
SPI_SendByte (0xFF);
SPI_SendByte (0xFE); //Начало буфера
Начало буфера — это своего рода метка, она входит в пакет передачи данных, состоящий из метки начала и собственно самих данных
Именно такая метка (0b11111110) должна быть для CMD 24, CMD17 и CMD18
Ну, и теперь непосредственно передаём данные из буфера и в конце контрольную сумму и любой байт
SPI_SendByte (0xFE); //Начало буфера
for (cnt=0;cnt<512;cnt++) SPI_SendByte(bf[cnt]); //Данные
SPI_SendByte (0xFF); //Котрольная сумма
SPI_SendByte (0xFF);
Контрольная сумма любая, поэтому по этому поводу не заморачиваемся.
Теперь примем результат команды из шины и произведём первичную проверку
SPI_SendByte (0xFF);
result=SPI_ReceiveByte();
if ((result&0x05)!=0x05) return 6; //Выйти, если результат не 0x05 (Даташит стр 111)
Аналогично, как и в предыдущей функции, создадим цикл и дождёмся свободного состояния карты, и, если всё нормально, то возвращаем 0
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;
}
Вызовем нашу функцию в main() и отобразим результат на дисплее в следующей строке
str_lcd(str);
result=SD_Write_Block(buffer,0x00,0x00,0x04,0x00);//Запишем блок из буфера
sprintf(str,«%d»,result);
setpos(0,1);
str_lcd(str);
В первом пармаетре у нас буфер, в котором у нас есть строчка, с которой мы игрались в прошлом занятии.
Далше идёт у нас адрес, просто разбитый по 8 бит. А полностью получается 0x00000400, то есть именно тот адрес в который мы и будем пытаться писать блок.
Проверим сначала в протеусе
Хоть у нас пока нет функции чтения с карты, но проверить мы можем, открыв наш файл образа карты памяти
Мы видим, что вся наша информация записана в правильное место.
Теперь поиграем с чтением. Создадим функцию опять же над функцией main() и сразу напишем в неё локальные переменные
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;
}
Дальше всё скопируем с предыдущей функции записи и будем потихоньку вносить изменения
Команда будет у нас уже CMD17
Соответственно, CMD17 у нас превращается в 0x51, остальные параметры аналогичные
long int cnt;
result=SD_cmd (0x51,dt1,dt2,dt3,dt4,0x95); //CMD17 даташит стр 50 и 96
if (result!=0x00) return 5; //Выйти, если результат не 0x00
Затем также передача 0xFF с целью выждать время и заодно прочистить регистр SPI у карты
if (result!=0x00) return 5; //Выйти, если результат не 0x00
SPI_SendByte (0xFF);
Посмотрим, как читаются данные с карты
Мы видим, что никаких меток у нас нет, поэтому сразу принимаем ответ и проводим первичную его проверк
SPI_SendByte (0xFF);
cnt=0;
do{ //Ждем начала блока
result=SPI_ReceiveByte();
cnt++;
} while ( (result!=0xFE)&&(cnt<0xFFFF) );
if (cnt>=0xFFFF) return 5;
А вот теперь уже читаем данные
if (cnt>=0xFFFF) return 5;
for (cnt=0;cnt<512;cnt++) bf[cnt]=SPI_ReceiveByte(); //получаем байты блока из шины в буфер
Затем мы обязаны принять контрольную сумму, поэтому примем её символически, никак не проверяя результат, даже не видя его, и возвращаем 0
for (cnt=0;cnt<512;cnt++) bf[cnt]=SPI_ReceiveByte(); //получаем байты блока из шины в буфер
SPI_ReceiveByte(); //Получаем контрольную сумму
SPI_ReceiveByte();
return 0;
}
Теперь вызовем нашу функцию в main() и отобразим результат в третьей строке дисплея
str_lcd(str);
result=SD_Read_Block(buffer,0x00,0x00,0x04,0x00); //Считаем блок в буфер
sprintf(str,«%d»,result);
setpos(0,2);
str_lcd(str);
Попробуем собрать код и запустить его в протеусе
Всё у нас считалось.
Теперь проверим в живой схеме, прошив перед этим контроллер
Только мы не видим, что именно у нас считалось. Если мы отобразим наш буфер, в который мы считали, то толку от этого не будет, так как там уже есть этот текст. Поэтому мы должны назначить буфер для чтения, так как наш буфер с текстом нам уже не подойдёт, ибо в нём уже есть текст и мы просто не проверим работу функции. Поэтому создадим ещё один буфер, а предыдущий закомментируем, так как у нас не хватит на два буфера оперативной памяти и компилятор даст ошибку и не будет собирать код
//char buffer[512] =»The…»; //Буфер данных для записи/чтения
char buffer2[512] ={}; //Буфер данных для чтения
И также закомментируем код вызова и отображения результата функции записи в main(), а также исправим имя буфера в вызове функции чтения, затем добавим задержку, раскомментируем функцию вывода буфера на экран дисплея, исправив там также имя буфера
// result=SD_Write_Block(buffer,0x00,0x00,0x04,0x00);//Запишем блок из буфера
// sprintf(str,»%d»,result);
// setpos(0,1);
// str_lcd(str);
result=SD_Read_Block(buffer2,0x00,0x00,0x04,0x00); //Считаем блок в буфер
sprintf(str,«%d»,result);
setpos(0,2);
str_lcd(str);
_delay_ms(1000);
for (i=0;i<=22;i++) {str80_lcd(buffer2+i*20);_delay_ms(1000);}
while(1)
Сначала соберём код и проверим в протеусе
Затем прошьём контроллер и посмотрим результат вживую
Как видим, всё у нас работает.
Таким образом, мы научились работать по шине SPI с флеш-картой SD, научились туда писать блоки, читать блоки, что очень неплохо. А со следующей части нашего занятия мы начнём уже работать с определением типа карты, а также будем учиться работать с файловой системой на карте.
Предыдущая часть Программирование МК AVR Следующая часть
Техническая документация на Secure Digital
Программатор, модуль SD и дисплей можно приобрести здесь:
Программатор USBASP USBISP с адаптером USBASP USBISP 3.3 с адаптером
Смотреть ВИДЕОУРОК (нажмите на картинку)
Спасибо огромное за Ваши статьи. Неделю пытался проинициализировать SD карту аппаратным SPI, а тут в течение 3х часов (с перерывами) и все заработало! Удачи!
Уважаемый admin! Нет ли у Вас в планах разжевать для широких масс тему USB MS class именно с mega8 + SD карта? В разделе stm32 есть, а здесь пока нет.