STM32F103 и FatFS начинающим

На нелегком пути освоения МК постоянно сталкиваешься с сомнениями. Правильно ли соединил провода, не намудрил ли с инициализацией? А железо вообще исправное? Или все сразу? Слишком много переменных факторов. Конечно, подобное лишь улыбнет гуру, но новичку доставляет немало головной боли.



Одним словом остро не хватает заведомо работающего кода сложнее мигания светодиодом. Буду выкладывать разные проектики, которые надеюсь пригодятся будущим поколениям новичков.

Убедившись на Ардуине что все исправно, сперва взял за основу проект сообщника MikeSmith, который в свою очередь использовал исходники Martin THOMAS. Но Мартин что-то перемудрил и его код имеет свойство впадать в бесконечные циклы. Да и вообще привередлив к разным флешкам.

Дальнейшие поиски привели к коду Domen Puncer. Этот код пришлось слегка адаптировать на предмет дефайнов (очень не люблю хардкод, да и лень было провода переключать на другой порт), но в целом работает веселее. Одинаково хорошо справился с флэшками 16Mb и 16Gb. Не захотел только читать MMC 16Mb, ну да кому они нужны ))

Физические соединения.

Card Shield — STM32
CS — PB12
MOSI — PB15
SCK — PB13
MISO — PB14
+5V — +5V
GND — GND



Тестовый код не делает ничего сверхестественного и должен быть понятен из комментариев.

#include "stm32f10x_conf.h"
#include "ff.h"

char	buff[1024];		// буфер для чтения/записи

int main(void)
{
	FRESULT result;

	// смонтировать диск
	FATFS FATFS_Obj;

	result = f_mount(&FATFS_Obj, "0", 1);
	if (result != FR_OK)
	{
		//printf("Ошибка монтирования диска %d\r\n", result);
	}


	// считаем файлы в корневом каталоге
	DIR dir;
	FILINFO fileInfo;
	int nFiles = 0;

	result = f_opendir(&dir, "/");
	if (result == FR_OK)
	{
		while (((result = f_readdir(&dir, &fileInfo)) == FR_OK) && fileInfo.fname[0])
		{
			nFiles++;
		}
	}
	f_closedir(&dir);


	// открываем файл readme.txt для чтения
	FIL file;
	UINT nRead, nWritten;

	result = f_open(&file, "readme.txt", FA_OPEN_EXISTING | FA_READ);
	if (result == FR_OK)
	{
		f_read(&file, &buff, 1024, &nRead);
		f_close(&file);
	}

	// создаем файл write.txt
	result = f_open(&file, "write.txt", FA_CREATE_ALWAYS | FA_WRITE);
	if (result == FR_OK)
	{
		f_write(&file, &buff, nRead, &nWritten);
		f_close(&file);
	}
}


Проект для CooCox. Проверял на МК STM32F103RB
  • +7
  • 30 декабря 2013, 23:09
  • aliaksei
  • 1
Файлы в топике: sd-test.zip

Комментарии (28)

RSS свернуть / развернуть
очень своевременная статья..)))а вот нет ли у кого… как бы имена файлов складывать в… куда нето в смысле в память да ищеб и таблицу с указателями чтоб сразу )))) ну вообщем всех с наступающим НГ, товарищи!!! ура товарищи!!! даёшь больше тем хороших и разных!!!
+1
а вот нет ли у кого… как бы имена файлов складывать в… куда нето в смысле в память да ищеб и таблицу с указателями чтоб сразу ))))
бАбёр!!! ВЫДЫХАЙ!!!
+1
вообщем я сделал это как то так…
for (;;)
{
f_err_code = f_readdir(&dir, &fno); /* Чтение объекта директории */
if (f_err_code != FR_OK || fno.fname[0] == 0)
break;
copy (myfile[filcount],fno.fname);
filcount++;
}
наверно не очень?? и еще может кто подскажет как включить поддержку длинных имён и чтоб русские назхвания нармально отображались?
0
А… на какой скорости можно читать таким образом, скажем waf c частотой 22000 byte/sec можно?..
0
MikeSmith как раз писал про скорость считывания, DMA и т.д.
0
А почему функция spi_set_speed закомментирована? На 100..400кГц не разбежишься, если что-нибудь крупное надо считать/записать.
И в чем сакральный смысл do-while: #define spi_cs_low() do { GPIO_CS->BRR = GPIO_Pin_CS; } while (0)?
Есть еще один косяк. В конце одной функции CS переключается в 1, а в начале другой функции переключается в 0 без дополнительных тактов, это неправильно. Необходимо послать 8 тактов (1 байт 0xFF) после снятия CS (переключения из 0 в 1). Да, большинство карт на это не обратят внимания, но найдется такая, которая работать не будет (у меня нашлась). Кроме того, без этого пустого байта карта может не перевести DataOut в Z-состояние и будет давить единицу. Если у вас на шине есть другие устройства и чтение глючит, то вам повезло, иначе глючить будет у потребителей — тогда вам не повезло, проблему устанете искать. Решение: #define spi_cs_high() GPIO_CS->BSRR = GPIO_Pin_CS, spi_txrx(0xFF);
Вообще, для обеспечения хорошей совместимости со всем зоопарком карт, лучше лепить sd_nec() везде, где только можно, некоторые тормозные контроллеры карт без этого жить не могут.
+1
И в чем сакральный смысл do-while
Так положено. Иначе при подстановке макроса в некоторые конструкции (не помню какие) будет получаться хрень.
0
Согласен, если подставлять в выражение. Но в коде таковых нет. А если и есть, то фигурных скобок разве недостаточно?
0
Понятия не имею. Но с другой стороны, оптимизатор же все равно все лишнее вырежет — почему бы не сделать гарантированно правильно и надежно?
0
Это нужно, чтобы использовать макрос как обычную функцию. Обычно проблема возникает, когда макрос содержит несколько операторов.
Пример:
#define MY_MACRO() operator1; operator2
Так делать нельзя, иначе будут проблемы при использовании макроса например в операторе IF-ELSE. Поэтому макрос нужно поместить в фигурные скобки:
#define MY_MACRO {operator1; operator2;}
Уже лучше. Но тогда в такой конструкции
if (условие)
   MY_MACRO
else
   something;
после MY_MACRO точку с запятой ставить нельзя — фигурные скобки уже считаются оператором. Но в то же время при использовании обычной функции в IF-ELSE точка с запятой всегда нужна. Получается неудобство — где-то нужно, где-то нет.
Если же макрос определить так:
#define MY_MACRO() do{operator1; operator2;}while(0)
то его использование не будет отличаться от одиночного оператора (или обычной функции).

Одиночный оператор в макросе действительно не требуется помещать в do{}while(0), это уже относится к культуре программирования — можно приучить себя к такому оформлению, и в дальнейшем иметь меньше проблем с «детскими ошипками».
+3
скачал проект coocox собрал на макетке все как у вас, запускаю,
монтируется диск,
далее просмотр файлов в корне диска FR_OK везде

FRESULT f_opendir (
        DIR* dp,                        /* Pointer to directory object to create */
        const TCHAR* path       /* Pointer to the directory path */
)
{
        FRESULT res;
        FATFS* fs;
        DEF_NAMEBUF;


        if (!dp) return FR_INVALID_OBJECT;

        /* Get logical drive number */
        res = find_volume(&fs, &path, 0);
        if (res == FR_OK) {
                dp->fs = fs;
                INIT_BUF(*dp);
                res = follow_path(dp, path);


и на вот этой точке res = follow_path(dp, path); сразу FR_INT_ERR.
далее чтение файла и запись с этой же ошибкой. Перепробовал разные карточки, форматировал FAT16, FAT32 файл readme.txt присутствует. Все равно FR_INT_ERR.

изменил значение
#define _USE_LFN        1               /* 0 to 3 */

с 0 на 1 в ffconf.h
начало читать и писать файлы. Читает из readme.txt пишет в write.txt обьем верный но текст readme.txt и write.txt разный, наверное что то с кодировкой.
С чтением списка файлов остается проблема в функции get_fileinfo
на строчке
if (dp->sect && fno->lfsize && dp->lfn_idx != 0xFFFF) { /* Get LFN if available */


улетает в

/**
  * @brief  This is the code that gets called when the processor receives an 
  *         unexpected interrupt.  This simply enters an infinite loop, 
  *         preserving the system state for examination by a debugger.
  * @param  None
  * @retval None  
  */
static void Default_Handler(void) 
{
  /* Go into an infinite loop. */
  while (1) 
  {
  }
}

startup_stm32f10x_md.c прогресс есть но работает все равно странно.

Читает из readme.txt в буфер
но на строчке
result = f_open(&file, "write.txt", FA_CREATE_ALWAYS | FA_WRITE);

в буфере вместо считаных значений записывается какая то билиберда которую потом записывает в write.txt.
Помогите советом!
0
Как проверить, работает ли карточка? Пытаюсь слепить это все в кучу, не работает и гда налажал не знаю. Есть ли какая-то команда, на которую SD карточка должна гарантированно ответить? Типа как AT в радиомодулях, для проверки.
0
CMD0. Подробно описано здесь и рядом elm-chan.org/docs/mmc/mmc_e.html
0
спасибо!
0
так какая реальная скорость чтения/записи на карту?
0
aliaksei?, а я уже был подумал, что действительно рабочий проект. Узнаете свой код?

0
Насколько я помню, это баг, вызванный миграцией на более новый тулчейн. Гугл должен знать, как его править.
0
Да там просто CMSIS обновить нужно.
0
aliaksei?, а я уже был подумал, что действительно рабочий проект. Узнаете свой код?

Это печально.
ты приходишь ко мне в дом в день свадьбы моей дочери… … Вы даже не попытались разбираться в коде и причине ошибки, (а зачем?) сразу обвинили автора в том, что он выложил «нерабочий» проект.
+3
Я ничего не понял :( поясните пожалуйста, этот проект — полуфабрикат и все автоматом понимают, как его допилить до годного? Тут приведена main.c, но я не вижу, где инитится периферия контроллера? Ну там, направление портов проставить, настройки SPI, и т.д. После просмотра исходников в fatfs_diskio_sdcard_spi.c нашлась функция spi_init (не из SPL). Но нашлась она только в одном месте, там где она определяется 8( Где же она вызывается, в какой момент инитится переферия?
0
Судя по названию, оно должно собираться и сразу работать.
SPI-драйвер инитится из функции f_mount (через довольно длинную цепочку вызовов), он инициализирует нужную ему периферию. Кроме нее в проекте ничего не инициализируется (впрочем, вроде функция SystemInit с базовыми настройками вызывается автоматически стартап-кодом еще до main, там настройка клока и всего такого).
Из забавного — автор не удосужился проверить файл конфигурации FatFs и там помимо прочего выставлена японская кодовая страничка)
0
  • avatar
  • Vga
  • 22 сентября 2016, 19:58
Автор, похоже, забил на свою статью, очень жаль :( Понятно, если будет готовая железка под рукой, я и сам все пойму, но пока она только еще на стадии проектирования :( А хотелось бы знать заранее.

Так что, никто не разобрался, что-ли? ;) И даже Vga? 8)
0
Vga лень… И у меня также нет ни железки, ни поднятого тулчейна, ни настроения все это делать.
0
  • avatar
  • Vga
  • 22 сентября 2016, 16:53
Vga, лень не может, можешь ты.

ни поднятого тулчейна
А поднятый тулчейн на что у тебя есть, правда, интересно?

Но ты ведь всяко проверил эту билиотеку. Так поделись мудростью, не в падлу.
0
А поднятый тулчейн на что у тебя есть, правда, интересно?
Win32.
Но ты ведь всяко проверил эту билиотеку.
Не, FatFs я не ковырял — не возникало такой задачи.
0
  • avatar
  • Vga
  • 22 сентября 2016, 19:41
Господа, если ещё кто-то следит за этой статьей… В общем, никто не сталкивался с проблемой, что библиотека не читает файл дальше 1кб? Даже не догадываюсь куда копать. Как вариант думал обновить библиотеку, но тогда нужно переписывать драйвер (со старым не заводится), а с stm я только пару месяцев знаком.
0
В общем да, какой-то баг с драйвером/либой. Попробовал generic пример с офсайта, где в качестве аппаратного spi используется дрыганье ножками, переписал функции ввода/вывода на STMские, и всё отлично заработало. Надо будет на досуге поизучать аппаратный spi.
0
Большое спасибо за статью и сорцы!
Заработало на F103CB в 48 ног после небольшого мода (освежить библиотеки, подкрутить стек, заменить printf).
0
  • avatar
  • igorp
  • 28 августа 2017, 16:47
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.