Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: STM32L4 + SD + DMA + FatFs
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Allregia
Джентельмены, нет ли у кого работающего примера работы SD+FatFs через DMA на L4? Буду очень признателен.

Пробовали пример от STM32L476G_EVAL, но там только в поллинге.
Попробовали пределать под DMA, но пока не работает, читает только первый килобайт а потом ошибка, с записью вообще пока не работает.
В поллинге нет проблем, но он работает только при выключенных прерываниях (оно есть в коде на еваборду).
Genadi Zawidowski
Есть для не "L" а для "F"

https://188.134.5.254/browser/trunk

Файл sdcard.c и dskio.c

ПРограммирование адреса периферии не тут, надюсь поможет. Это как пониямете из работающего проекта..

CODE
/* DMA2 Stream6 Channel 4 */
const uint_fast8_t ch = 4;

DMA2_Stream6->M0AR = addr;
DMA2_Stream6->NDTR = (DMA2_Stream6->NDTR & ~ DMA_SxNDT) |
(1 * DMA_SxNDT_0); // Особый случай - регистр игнорируется. В примерах от ST пишется 1

if (direction == 0)
{
// Read from SD CARD
//DMA2_Stream6->NDTR = (DMA2_Stream6->NDTR & ~ DMA_SxNDT) |
// (length0 * count / 4 * DMA_SxNDT_0); // Особый случай - регистр игнорируется. В примерах от ST пишется 1

DMA2_Stream6->FCR =
1 * DMA_SxFCR_DMDIS | // use FIFO mode
3 * DMA_SxFCR_FTH_0 | // 11: full FIFO
0;

DMA2_Stream6->CR =
0 * DMA_SxCR_DIR_0 | // 00: Peripheral-to-memory
ch * DMA_SxCR_CHSEL_0 | // канал
1 * DMA_SxCR_PFCTRL | // 1: The peripheral is the flow controller (required for SDIO/SDMMC connected DMA)
1 * DMA_SxCR_MBURST_0 | // 01: INCR4 (incremental burst of 4 beats)
1 * DMA_SxCR_PBURST_0 | // 01: INCR4 (incremental burst of 4 beats)
//0 * DMA_SxCR_DIR_0 | // 00: Peripheral-to-memory
1 * DMA_SxCR_MINC | //инкремент памяти
2 * DMA_SxCR_MSIZE_0 | //длина в памяти - 32 bit
2 * DMA_SxCR_PSIZE_0 | //длина в DR - 32 bit
//1 * DMA_SxCR_CIRC | //циклический режим не требуется при DBM
2 * DMA_SxCR_PL_0 | // Priority level - High
0 * DMA_SxCR_CT | // M0AR selected
//0 * DMA_SxCR_DBM | // double buffer mode seelcted
0;
}
else
{
// Write to SD CARD

//DMA2_Stream6->NDTR = (DMA2_Stream6->NDTR & ~ DMA_SxNDT) |
// (length0 * count * DMA_SxNDT_0); // Особый случай - регистр игнорируется. В примерах от ST пишется 1

DMA2_Stream6->FCR =
1 * DMA_SxFCR_DMDIS | // use FIFO mode
1 * DMA_SxFCR_FTH_0 | // 01: 1/2 full FIFO (changed from read !)
0;

DMA2_Stream6->CR =
1 * DMA_SxCR_DIR_0 | // 01: Memory-to-peripherial
ch * DMA_SxCR_CHSEL_0 | // канал
1 * DMA_SxCR_PFCTRL | // 1: The peripheral is the flow controller (required for SDIO/SDMMC connected DMA)
0 * DMA_SxCR_MBURST_0 | // 00: single transfer (changed from read !)
1 * DMA_SxCR_PBURST_0 | // 01: INCR4 (incremental burst of 4 beats)
//0 * DMA_SxCR_DIR_0 | // 00: Peripheral-to-memory
1 * DMA_SxCR_MINC | //инкремент памяти
0 * DMA_SxCR_MSIZE_0 | //длина в памяти - 8 bit (changed from read !)
2 * DMA_SxCR_PSIZE_0 | //длина в DR - 32 bit
//1 * DMA_SxCR_CIRC | //циклический режим не требуется при DBM
2 * DMA_SxCR_PL_0 | // Priority level - High
0 * DMA_SxCR_CT | // M0AR selected
//0 * DMA_SxCR_DBM | // double buffer mode seelcted
0;
}

DMA2->HIFCR = (DMA_HIFCR_CTCIF6 /*| DMA_HIFCR_CTEIF6 */); // Clear TC interrupt flag соответствующий stream
//DMA2_Stream6->CR |= (DMA_SxCR_TCIE /* | DMA_SxCR_TEIE */); // разрешить прерывания от DMA по TC и TE

DMA2_Stream6->CR |= DMA_SxCR_EN;
Allregia
Спасибо, попробую.
Allregia
Трудно там разобраться, тем более что L4 и F4 немного отличаются (напрмиер каналы DMA).
Может есть у кого именно для L4?
Genadi Zawidowski
Да, у L проще... так попробовали бы по аналогии... Главные вещи - едигичка в регистре длины, размеры данных у источника-получателя.
Шаманъ
Genadi Zawidowski, а зачем разные размеры источника и приемника при записи? Вот тут:

0 * DMA_SxCR_MSIZE_0 | //длина в памяти - 8 bit (changed from read !)
2 * DMA_SxCR_PSIZE_0 | //длина в DR - 32 bit

У меня везде стоит по 32бита. Кстати в регистр длины в принципе можно писать что угодно, после начала работы DMA он сбросится в 0xFFFF об этом прямо указано в доках.

Genadi Zawidowski
Там было много непонятного... много казалось бы разумного отсеялось.. осталось то что совпало с примером от ST. Когда оно заработало, я не трогал... Вохможно тут и 32 бита можно.
Шаманъ
Цитата(Genadi Zawidowski @ Jul 27 2017, 14:41) *
Там было много непонятного...

По этой причине я написал по докам с нуля. Хотя поковыряться пришлось тоже - забыл вначале Hardware Flow Control включить sm.gif.
Genadi Zawidowski
Если посмотришь, у меня только инициализация DMA стянута. Остальное тоже только по докам. Сперва вообще запускал на Ренесансе по усеченной документации... Потом уже на STM32F.
AHTOXA
Цитата(Шаманъ @ Jul 27 2017, 16:34) *
Genadi Zawidowski, а зачем разные размеры источника и приемника при записи? Вот тут:

0 * DMA_SxCR_MSIZE_0 | //длина в памяти - 8 bit (changed from read !)
2 * DMA_SxCR_PSIZE_0 | //длина в DR - 32 bit

У меня везде стоит по 32бита. Кстати в регистр длины в принципе можно писать что угодно, после начала работы DMA он сбросится в 0xFFFF об этом прямо указано в доках.

А, тут не всё так просто. Вот, почитайте эту тему, я там походил по граблям...
Allregia
У L4, как я понял. в DMA нет барста, поэтому так выравнивать не надо.
AHTOXA
Дело не только и не столько в burst, сколько в пословном доступе к невыровненным данным. Этого, думаю, и L4 не любит.
(FatFs при определённых условиях вызывает функцию записи блока с адресом буфера, не кратным 4).
Шаманъ
Цитата(AHTOXA @ Jul 27 2017, 17:46) *
А, тут не всё так просто. Вот, почитайте эту тему, я там походил по граблям...

У меня такого не может произойти - доступ всегда по выровненным адресам.
Genadi Zawidowski
Используя у себя FatFs R0.12b, дорабатывал напильником выравнивание члена buf структуры FIL и win в структуре FATFS - перестали идти невыровненные обращения. Мне это требовалось еще и для нормальной работы DATA CACHE процессоров, вернее чтобы FatFs работал при наличии DCache. Все это есть в проекте.
AHTOXA
Выравнивание buf структуры FIL не помогает, всё равно FatFs при определённых условиях передаёт в функцию записи блока невыровненный указатель. Почитайте по моей ссылке. Но у вас доступ DMA к памяти побайтный (0 * DMA_SxCR_MSIZE_0), поэтому не страшно.
Genadi Zawidowski
Цитата
buf структуры FIL не помогает

Я два поля упомянул. Чтобы выравнивание работало, надо и саму структуру (структуры) выровнять. И секцию соответственно в скрипье линкера вырповнять.
Я не боролся с выравниванием для DMA, я решал поблемы с DCache.
Цитата
всё равно FatFs при определённых условиях передаёт в функцию записи блока невыровненный указатель

Детский сад... fatfs в исходниках, ловится и дебажится, я без дебагера полдня возился.
Allregia
Цитата(AHTOXA @ Jul 27 2017, 20:43) *
Дело не только и не столько в burst, сколько в пословном доступе к невыровненным данным. Этого, думаю, и L4 не любит.
(FatFs при определённых условиях вызывает функцию записи блока с адресом буфера, не кратным 4).


Так на 4 я выравниваю, я имел ввиду что если нет барста то не надо на 512 выравнивать.
Вообще, когда-то делал на Ф4 с FatFs R0.11 и драйвром от Nemui Trinomius (http://nemuisan.blog.bai.ne.jp)
Проблем не замечал.
А вот с L4 тут напарник занимается, я сильно не влазил, так он уже неделю ковыряется. То что калокуб нагенерил было вообще сразу снесено, им только проверили что железо работает, потом переделали под драйвер от EVAL - хоть поллинг нормально заработал, а с ДМА пока засада, даже на чтении -пишет блоками до 1023 байта, дальше дикс эррор, если дать блок 8К или 16К, как задумывалось изначально, то эррор сразу (это я все пишу со слов напарника).
Мое личное мнение (проверю на след. неделе), что он не делает, илиделает не правильно, какую-нибудь из проверок.

У меня disk_read выглядел так:

Код
DRESULT disk_read (
                   BYTE drv,        /* Physical drive nmuber (0..) */
                   BYTE *buff,        /* Data buffer to store read data */
                   DWORD sector,    /* Sector address (LBA) */
                   BYTE count        /* Number of sectors to read (1..255) */
                     )
{
  switch (drv)   {
    case SDIO_DRIVE: {    

             Status = SD_ReadMultiBlocks((uint8_t*)(buff),(sector)*SECTOR_SIZE,SECTOR_SIZE,count);

             /* Check if the Transfer is finished */
             Status = SD_WaitReadOperation();

            /* Wait until end of DMA transfer */
            while(SD_GetStatus() != SD_TRANSFER_OK);

            if (Status == SD_OK)    return RES_OK;
            else                           return RES_ERROR;
    }

  }
  return RES_PARERR;
}


а у него:
Код
DRESULT SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count){
  DRESULT res = RES_ERROR;
  uint32_t timeout = 100000;

  if(BSP_SD_ReadBlocks_DMA((uint32_t*)buff,
                                        (uint32_t) (sector),
                                         count, SD_DATATIMEOUT) == MSD_OK)
  {
    while(BSP_SD_GetCardState()!= MSD_OK)  {
         if (timeout-- == 0)   {
              return RES_ERROR;
         }
    }
    res = RES_OK;
  }
  return res;
}
AHTOXA
Цитата(Genadi Zawidowski @ Jul 29 2017, 03:03) *
Я два поля упомянул. Чтобы выравнивание работало, надо и саму структуру (структуры) выровнять. И секцию соответственно в скрипье линкера вырповнять.

Да хоть обвыравнивайся. Раз вы не хотите читать по ссылке, я здесь повторю.
Итак. Пишем в в файл последовательно два раза блок размером 971байт. (Блок выровнен).
При этом будут следующие вызовы:
для первого раза:
Код
  disk_write(0, block, sector, 1);       // первый сектор берётся прямо из блока
  disk_write(0, file.buf, sector+1, 1);  // второй сектор пока не полный, поэтому пишется через файловый буфер

для второго раза:
Код
  disk_write(0, file.buf, sector+1, 1);   // дописываем 53 байта во второй сектор (1024-911) (через файловый буфер)
  disk_write(0, block[53], sector+2, 1);  // вот здесь плохо, адрес block[53] - не выровнен на границу слова.

Видите, в четвёртом вызове в функцию disk_write() передан адрес block[53], который, очевидно, не выровнен. (Хотя сам блок выровнен).

Цитата(Genadi Zawidowski @ Jul 29 2017, 03:03) *
Детский сад... fatfs в исходниках, ловится и дебажится, я без дебагера полдня возился.

Не знаю, что вы там отдебажили, разве что переделали FatFs на запись строго через файловый буфер. Но это однозначное уменьшение быстродействия. Думаю, что вы просто не заметили проблемы, потому что у вас настроен побайтовый доступ DMA к памяти.
Genadi Zawidowski
невыровненные обращения к фпайлу fatfs делает через внктренний буфер, если размер данных операции записи позволяет, часть зхаписи идет напрямую из памяти где и лежат... Может, у Вас какая-нибуь старая версия?
Шаманъ
Народ, может просто сделать RTFM?

Отсюда и дальше во ссылке:
http://elm-chan.org/fsw/ff/doc/dread.html

Если все структуры данных в файле и в памяти будут выровнены по границе слова, то невыровненного доступа просто быть не может. Также его не может быть, если писать кусками меньшими по длине, чем сектор (тогда все идет через внутренний буфер).

Цитата(Genadi Zawidowski @ Jul 29 2017, 14:29) *
невыровненные обращения к фпайлу fatfs делает через внктренний буфер, если размер данных операции записи позволяет, часть зхаписи идет напрямую из памяти где и лежат...

А вот не всегда там все будет выровнено. Так будет только если выполняется условие (((UINT)data & 3) == (f_tell(fp) & 3)).

У меня большие объемы пишутся вообще посекторно (с соответствующим выравниванием), маленькие через внутренний буфер FatFs вот собственно и все. Ничего править в FatFs надобности нет.
Genadi Zawidowski
Цитата
Ничего править в FatFs надобности нет.

Включишь кэш данных процессора - появится.
Цитата
А вот не всегда там все будет выровнено. Так будет только если выполняется условие (((UINT)data & 3) == (f_tell(fp) & 3)).

Обмен с картой идет из буфера. Она же не умеет (HS, XS) частями сектора переписывать... понял, о чем ты... Все буферы что я отдаю f_write и f_read, тоже выровнены. Во избежание...
AHTOXA
Цитата(Шаманъ @ Jul 30 2017, 11:22) *
Народ, может просто сделать RTFM?

Ну давайте, сделаем RTFM sm.gif Открываем ссылку: disk_write(), читаем:
Цитата
The specified memory address is not that always aligned to word boundary because the argument is defined as BYTE*.

Ещё вопросы есть?

Давайте ещё раз на пальцах объясню, как получается невыровненный адрес.

1. Пишем 511 байт. Они записываются в файловый буфер, буфер записывается в сектор.
2. Пишем 10К (неважно сколько, просто больше 513 байтов).
2.1 FatFs дописывает 1 байт в файловый буфер, скидывает его в сектор;
2.2 FatFs видит, что следующий сектор записывается целиком, и пишет его напрямую, минуя файловый буфер. В качестве адреса в функцию записи сектора передаётся указатель на второй байт массива в 10К (потому что первый байт ушёл в предыдущий сектор).
Ч. Т. Д.

ЗЫ. Версия, на которой я это проверял - 0.09b. Возможно, с тех пор что-то поменялось, буду рад это узнать.
Шаманъ
Цитата(Genadi Zawidowski @ Jul 30 2017, 09:44) *
Включишь кэш данных процессора - появится.

Он у меня включен, но я умею его приготовить соответствующим образом cool.gif То кинцо, что я тебе посылал довольно активно использует кеши, без них времени потребовалось бы намного больше (или письмо не дошло?). Но разговор то был не про кэши?

Цитата(AHTOXA @ Jul 30 2017, 10:37) *
Ну давайте, сделаем RTFM sm.gif Открываем ссылку: disk_write(), читаем:

Ещё вопросы есть?

У меня вопросов и не было, и если Вы внимательно прочитаете мое сообщение, то я не возражал, что в общем случае доступ может быть невыровненный. Однако там же описано, что сделать, чтобы он всегда был выровненный. Про это я тоже написал в том же сообщении.

Вы в Вашем примере пишите как попало, отсюда и невыровненный доступ. Никакого откровения здесь нет - все есть в мануале на FatFS, почему я и посоветовал сделать RTFM.
Allregia
Джентельмены, мы пишем стрим строго по 8К или 16К блоками, так что проблемы с выравниваниями думаю нет.
А вот остальное - может кто что сказать по поводу того, что я постил выше?
AHTOXA
Цитата(Шаманъ @ Jul 30 2017, 12:58) *
Однако там же описано, что сделать, чтобы он всегда был выровненный. Про это я тоже написал в том же сообщении.

Первым пунктом там идёт:
Цитата
Convert word transfer to byte transfer in this function if needed. - Recommended.

На что, собственно, я и напираю уже который пост. И это наиболее универсальное решение.

А как сделать, чтобы он был всегда выровненный при записи блоков данных произвольной длины? (Например, текстовых логов). Вот прямо для приведённого мной выше сценария, можете рассказать? Мне кажется, это невозможно. (Ну разве что пропатчить FatFs, чтобы все записи велись через файловый буфер, но это будет приличное замедление).

Цитата(Allregia @ Jul 30 2017, 13:05) *
Джентельмены, мы пишем стрим строго по 8К или 16К блоками, так что проблемы с выравниваниями думаю нет.

Прошу прощения, что увёл тему несколько в сторону. Но вопрос, я считаю, важный.
Шаманъ
Цитата(AHTOXA @ Jul 30 2017, 11:17) *
А как сделать, чтобы он был всегда выровненный при записи блоков данных произвольной длины? (Например, текстовых логов).

Для текстовых логов писать их кусками менее 512байт каждый. Как правило оно само собой так и получается. По крайней мере я не могу припомнить, чтобы в текстовый лог я писал за раз более 0.5К...

Для нетекстовых изменить структуру данных так, чтобы все было выровнено по границам слова - и в памяти, и в файле.

В общем случае, если хочется ни о чем не париться, то функции обмена с картой должны уметь работать с невыровненными данными. Как вариант написать "надстройки" над функциями чтения/записи, которые будут дробить вызовы записи/чтения фатфс, чтобы фрагменты были менее 512байт, и использовать эти функции там, где может быть проблема с невыровненными данными. Хотя проще в случае невыровненных данных обработать ситуацию прямо в disk_read/disk_write.

Цитата
Вот прямо для приведённого мной выше сценария, можете рассказать? Мне кажется, это невозможно.

В disk_read/disk_write в случае невыровненного адреса данных переписываем данные во временный буфер (выровненный, на 512байт) и пишем посекторно из него (при чтении делаем также - читаем в буфер, копируем в память). Это так сказать по методу "чайника" подход sm.gif Более правильно конечно разобраться с ДМА и, если он умеет работать с невыровненными адресами, просто правильно "приготовить" его (но у меня никогда такой надобности не возникало, потому здесь ничего не подскажу).

Цитата(Allregia @ Jul 30 2017, 11:05) *
может кто что сказать по поводу того, что я постил выше?

Могу сказать одно, чем неделю (!) переделывать чужой код лучше написать свое строго по мануалу. Вы ведь не знаете какие грабли припрятаны в чужом коде?
AHTOXA
Цитата(Шаманъ @ Jul 30 2017, 13:43) *
Более правильно конечно разобраться с ДМА и, если он умеет работать с невыровненными адресами, просто правильно "приготовить" его (но у меня никогда такой надобности не возникало, потому здесь ничего не подскажу).

Вот, вот оно! sm.gif Я ведь как раз и советую настроить DMA на доступ по байтам, и тогда всё работает как часы! Собственно, с чего я и влез в эту тему: вы спросили, зачем у Геннадия доступ к памяти побайтный, и я привёл пример, когда это может быть важно.

Кстати, я проводил замеры скорости, и у меня побайтный оказался даже немного быстрее, чем доступ по 32 бита.
Шаманъ
Цитата(AHTOXA @ Jul 30 2017, 12:13) *
Вот, вот оно! sm.gif Я ведь как раз и советую настроить DMA на доступ по байтам, и тогда всё работает как часы! Собственно, с чего я и влез в эту тему: вы спросили, зачем у Геннадия доступ к памяти побайтный, и я привёл пример, когда это может быть важно.

Так меня смутило что в одну сторону побайтно, а в другую пословно. Если бы в обоих случаях было побайтно, то было бы простое объяснение.

Цитата
Кстати, я проводил замеры скорости, и у меня побайтный оказался даже немного быстрее, чем доступ по 32 бита.

Это смотря что-кому важно. Мне намного важнее эффективное использование шины, а не скорость записи на карту (там ничего особого не нужно), потому пословно будет эффективнее (применительно к моему проекту).
AHTOXA
Цитата(Шаманъ @ Jul 30 2017, 14:24) *
Так меня смутило что в одну сторону побайтно, а в другую пословно. Если бы в обоих случаях было побайтно, то было бы простое объяснение.

Теперь понятно beer.gif.

Цитата(Шаманъ @ Jul 30 2017, 14:24) *
Это смотря что-кому важно. Мне намного важнее эффективное использование шины, а не скорость записи на карту (там ничего особого не нужно), потому пословно будет эффективнее (применительно к моему проекту).

Для меня важна была не скорость (это побочный эффект, да и то не уверен, что всё верно измерил), а надёжность. Чтобы я мог писать в файл какие угодно данные в любом порядке, и всё при этом писалось правильно.

Ну и универсальность. В таком виде мой драйвер FatFs я могу смело включать в любой проект, не вспоминая про выравнивание.
Genadi Zawidowski
Цитата
Геннадия доступ к памяти побайтный, и я привёл пример, когда это может быть важно.

будет возможность (рабочая плата под руками) - проверю - переведу оба направления в побайтный вариант обмена с памятью.
Шаманъ
Цитата(Genadi Zawidowski @ Jul 30 2017, 16:29) *
будет возможность (рабочая плата под руками) - проверю - переведу оба направления в побайтный вариант обмена с памятью.

Я только что ради интереса проверил - работает. На запись проверил с невыровненным буфером (записл 1байт, потом 1023, потом как обычно) - тоже работает.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.