STM32F407 Урок 04. Работаем с SD картой в режиме SDIO.
Добрый день, уважаемые читатели. Сегодня мы поработаем с подключенной к нашему контроллеру картой SD, с которой вы сможете считать файл, записать свой файл и вывести каталог на OLED дисплей, который мы подключали в прошлом уроке .
Советую в CubeMX создать новый проект, у меня при добавлении к старому, почему-то упорно неправильно показывался общий объем карты, пока не создал новый, бился несколько дней над глюком. Как всегда не забываем включить Debug mode, подключить кварцы, выставим частоту на максимум, подключим к I2C1 наш дисплей, не забываем про DMA в режим передачи и NVIC. Кто забыл - ссылка на прошлый урок в абзаце выше.
Включаем SDIO, судя по картинке подключенных пинов и даташиту на плату - все правильно, в конфигурации выставим делитель тактовой карточки на 3.
Не забываем про DMA на прием и передачу, настройки по умолчанию :
Прерывания, куда же без них :
И ягодка на торте - в Middleware FATFS с поддержкой SD карт, при желании, в настройках можете включить поддержку длинных имен и увеличить максимальный размер сектора до 4096 :
Еще один важный момент, определим контакт по которому FatFS будет определять что карта вставлена, на схеме я такого не нашел, поэтому определим для него PA0 подтянутый к земле - карта всегда вставлена :
Этот-же пин определим в меню Platform Settings, иначе при генерации проекта выпадет предупреждение.
Вроде все готово, увеличиваем кучу и стэк в 2 раза, пересылаем в Atollic и начнем уже программировать. Копируем библиотеку ic1306 и заголовочный файл в наш проект, советую обновить библиотеку, я добавил туда печать мелким шрифтом и возможность переворота экрана при инициализации. Прописываем следующие строки :
/* USER CODE BEGIN Includes */
#include <ic1306.h>
/* USER CODE END Includes */
В бесконечном цикле введем задержку, чтоб попусту не лопатил :
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_Delay(500);
}
/* USER CODE END 3 */
}
До бесконечного чикла проинициализируем дисплей и очистим его и поприветствуем для начала :
/* USER CODE BEGIN 2 */
display_init (50,0x22,0);
display_clear(0,7);
display_SmallPrint(0,0,"Hello");
/* USER CODE END 2 */
После приветствия смонтируем нашу карту :
display_SmallPrint(0,0,"Hello");
FATFS SDFatFs;
char USER_Path[4];
if(f_mount(&SDFatFs,(TCHAR const*)USER_Path,1)!=FR_OK)
{display_SmallPrint(0,7,"Error mount"); }
else
{display_SmallPrint(0,7,"OK mount");
FATFS_UnLinkDriver(USER_Path);
}
Если карта вставлена и с ней все в порядке - любуемся на OK. Добавим описание переменных, которые будем использовать дальше.
char USER_Path[4];FATFS* fs;
char str[32] = {0,};
FRESULT res;
DWORD fre_clust, fre_sect, tot_sect;
В случае успешного монтирования добавим вывод информации о карте:
{display_SmallPrint(0,7,"OK mount");
res = f_getfree("", &fre_clust, &fs);
tot_sect = (fs->n_fatent - 2) * fs->csize;
fre_sect= fre_clust * fs->csize;
sprintf(str, "Fclst %lu", fre_clust);
display_SmallPrint(0,0,str);
sprintf(str, "Faten %lu", fs->n_fatent);
display_SmallPrint(0,1,str);
sprintf(str, "Size %d", fs->csize);
display_SmallPrint(0,2,str);
sprintf(str,"t_sec %lu",tot_sect);
display_SmallPrint(0,3,str);
sprintf(str,"f_sec %lu",fre_sect);
display_SmallPrint(0,4,str);
sprintf(str,"%lu KB tot",fre_sect/2);
display_SmallPrint(0,5,str);
sprintf(str,"%lu KB avail",tot_sect/2);
display_SmallPrint(0,6,str);
Здесь по строкам :
- Fclst - количество свободных кластеров
- Faten - общее количество кластеров
- Size - число секторов в кластере
- t_sec - общее число секторов
- f_sec - число свободных секторов
- xxxx KB tot - объем карты
- xxxx KB avail - размер свободного места
Если мы подключим логический анализатор (у меня клон SL) к выведенным на гребенки пинам и выберем в Pulse View протокол sd, то можем наблюдать такую картинку :
Как видно - сначала подается команда сброса GO_IDLE_STATE , затем проверяется вольтаж SEND_IF_COND, и уже расширенной командой узнаем CardStatus, как я понимаю, мы наблюдаем определение типа карт, далее декодер sd ругается, как найду причину - дополню. Неплохое описание программы PulseView вы можете найти на странице сайта eax.me.
Запишем на карту текстовый файл с именем "1.txt" желательно больше 44 символов и без перевода строки. Добавим еще описание переменных :
DWORD fre_clust, fre_sect, tot_sect;FIL MyFile;
uint32_t bytesread;
После вывода информации добавим следующий код :
display_SmallPrint(0,6,str);
if(f_open(&MyFile, "1.txt", FA_READ) != FR_OK)
{display_SmallPrint(0,7,"Error file open");}
else {f_lseek(&MyFile,2);
res = f_read(&MyFile, str, 21, (void *)&bytesread);
if((bytesread == 0) || (res != FR_OK))
{display_SmallPrint(0,7,"Error read");}
else display_SmallPrint(0,7,str);
HAL_Delay(5000);
res = f_read(&MyFile, str, 21, (void *)&bytesread);
if((bytesread == 0) || (res != FR_OK))
{display_SmallPrint(0,7,"Error read");}
else display_SmallPrint(0,7,str);
f_close(&MyFile);
HAL_Delay(5000);
}
В нижней строке дисплея вы увидите сначала 21 символ начиная со второго и через 5сек следующий 21 символ, как видно из кода, не обязательно делать каждый раз f_lseek , если вы последовательно читаете порциями большой файл. Подробнее изучить команды FatFS можно на сайте разработчика или на русском языке. Теперь попробуем записать файл с нашими данными, определим следующие переменные :
uint32_t bytesread;const char wtext[] = "STM32F407 write file";
uint32_t byteswritten;
Добавим следующий код для записи файла :
HAL_Delay(5000);
}
if((res=f_open(&MyFile, "STM32wri.txt", FA_CREATE_ALWAYS | FA_WRITE)) != FR_OK)
{sprintf(str, "Error open wr %d", res);
display_SmallPrint(0,7,str);}
else
{res = f_write(&MyFile, wtext, sizeof(wtext), (void *)&byteswritten);
if((byteswritten == 0) || (res != FR_OK)) display_SmallPrint(0,7,"Error write");
else display_SmallPrint(0,7,"OK write");
f_close(&MyFile); }
HAL_Delay(5000);
Для вывода каталога карты добавим описание следующих переменных :
uint32_t byteswritten;FILINFO fileInfo;
DIR dir;
И сам код для чтения каталога :
HAL_Delay(5000);
res = f_opendir(&dir, "/");
if (res == FR_OK)
{
for(uint8_t i=0;i<8;i++)
{
res = f_readdir(&dir, &fileInfo);
if (res==FR_OK && fileInfo.fname[0])
{if(fileInfo.fattrib&AM_DIR) {display_SmallPrint(0,i,"DIR:"); i++;}
if(strlen(fileInfo.fname)) display_SmallPrint(0,i,(uint8_t*)fileInfo.fname);
}
else break;
}
f_closedir(&dir);
}
Если у вас карта с фотика, можете выдать список фотографий изменив команду f_opendir(&dir, "/DCIM");
На сегодня все, в следующем уроке поработаем с установленной на плате spi FLASH W25Q16. Если будут пожелания - поработаем с SD картой в режиме SPI.
Адрес для контактов : imax9@narod.ru
Если вам понравились мои работы и вы желаете поддержать сайт - сделайте дотацию.
При копировании статьи – обязательна ссылка на авторство и источник. Без разрешения автора копирование запрещено.
© Максим Ильин 2020г.