Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Неверные данные при чтении по SPI через DMA
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему
avg33
Всем привет. Пытаюсь прочитать SPI флэшку по DMA. Работаю через USART.
Суть дела в следующем. Допустим, буду читать из флэш по 100 байт. Для этого надо отправить на флэш 105 байт: 5 байт команды и 100 байт фиктивной записи.
Сначала я повесил прием на 2 канал DMA, а запись во флэш реализовал прямо в цикле for:

Код
uint8_t tx_buf[105]; //отсюда пишем во флэш, первые 5 байт - команда
uint8_t rx_buf[105]; //сюда DMA положит прочитанное

void read()
{
    //настройка DMA на прием по 2 каналу
    //по окончании приема 105 байт сработает прерывание IRQ_RX()
    init_DMA_RX();
    UART2_SSR2_RIE = 1; //разрешить прерывание по чтению
    
    //запись 105 байт
    FLASH_CS = FLASH_ON; //установить CS
    for(uint16_t i=0; i<105; ++i)
    {
        while (UART2_SSR2_TDRE == 0); //жду освобождения регистра передачи
        TRD2 = tx_buf[i]; //отправляю байт на флэш
    }
    FLASH_CS = FLASH_OFF; //снять CS
}

void IRQ_RX()
{
    //сюда придем по завершению приема
    //здесь выполняем всякие служебные вещи, чистим флаги и тд
    //и повторное чтение в заисивмости от условия
    if(/*условие*/)
        read();
}


Такой сценарий нормально работает. И при одиночном и при повторном вызове read() в rx_buf лежат валидные данные: 5 байт 0xFF и 100 байт из флэш
Но писать через for долго. Поэтому передачу tx_buf я тоже повесил на канал DMA:
Код
void read()
{
    //настройка DMA на прием по 2 каналу
    //по окончании приема 105 байт сработает прерывание IRQ_RX()
    init_DMA_RX();
    UART2_SSR2_RIE = 1; //разрешить прерыване по чтению
    
    //настройка DMA на передачу по 3 каналу
    //по окончании передачи 105 байт сработает прерывание IRQ_TX()
    init_DMA_TX();
    UART2_SSR2_TIE = 1; //разрешить прерывание по чтению
    
    FLASH_CS = FLASH_ON; //установить CS
}

void IRQ_TX()
{
    //сюда придем по завершению приема
    //здесь выполняем всякие служебные вещи, чистим флаги и тд
    //и снимаем CS
    FLASH_CS = FLASH_OFF;
}

void IRQ_RX()
{
/*...*/
    if(/*условие*/)
        read();
}


И вот этот сценарий дает сбой. Проблема в том, что в какой-то момент устанавливается в 1 флаг переполнения ORE USART`а.
Соответственно, в rx_buf по окончанию приема лежат невалидные данные. Обычно в конце буфера какая-то чушь, иногда в начале или вообще весь буфер бред.
Бывает и такое, что первый вызов read() дает корректные данные, но второй всегда невалидные.
Момент выставления ORE в единицу уловить невозможно, я так и не выявил зависимости. Что я делаю не так, в чем может быть проблема?
Arlleex
Цитата(avg33 @ Aug 7 2018, 20:53) *
Всем привет. Пытаюсь прочитать SPI флэшку по DMA. Работаю через USART.

Что за контроллер-то?
Не забывайте, что контроллер DMA выставит флаг "транзакция завершена" после перекладывания последней порции данный лишь в сдвиговый регистр. Поэтому
Код
void IRQ_TX()
{
    //сюда придем по завершению приема
    //здесь выполняем всякие служебные вещи, чистим флаги и тд
    //и снимаем CS
    FLASH_CS = FLASH_OFF;
}

не придете Вы сюда по завершению приема. Вы должны в прерывании этом выставить флажок (либо настроить прерывание по Transfer Complete), чтобы сигнализировать о реальном завершении транзакции. Иначе снимите CS до истинной передачи последнего слова.
Либо Вы не правильно настроили связку сигналов DMA-контроллера с модулем USART по передаче.
avg33
Arlleex, благодарю за оперативный ответ.

Цитата("Arlleex")
Что за контроллер-то?

F2MC-16FX MB96600 Series

Цитата("Arlleex")
Иначе снимите CS до в процессе передачи последнего слова.

И вправду. А если снять его в IRQ_RX() ? По идее прерывание по приему должно сработать после того, как DMA переложит последний байт в буфер. Или там тоже нюансы со сдвиговым регистром?

Цитата("Arlleex")
Либо Вы не правильно настроили связку сигналов DMA-контроллера с модулем USART по передаче.

По передаче или приему? Или и то и другое?) Я просто указал DMA сколько и откуда писать, куда читать, по каким перываниям срабатывать. Даже не знаю, где там можно ошибиться
Arlleex
Цитата(avg33 @ Aug 7 2018, 22:08) *
F2MC-16FX MB96600 Series

Не работал, но логика периферийных узлов во всех МК, в общем-то не глобально отличается...

Цитата(avg33 @ Aug 7 2018, 22:08) *
И вправду. А если снять его в IRQ_RX()?

Уже теплее. Так и нужно, да.

Цитата(avg33 @ Aug 7 2018, 22:08) *
По передаче или приему? Или и то и другое?) Я просто указал DMA сколько и откуда писать, куда читать, по каким перываниям срабатывать. Даже не знаю, где там можно ошибиться

Ну если прием у Вас работает при передаче "ручками", то, очевидно, исходя от противного - проблема в передаче laughing.gif Хотя не ручаюсь точно - ORE это же флаг переполнения входного буфера (то есть туда пришли данные когда еще предыдущие не считали). Поэкспериментируйте, решение где-то рядом.
avg33
Цитата("Arlleex")
Хотя не ручаюсь точно - ORE это же флаг переполнения входного буфера (то есть туда пришли данные когда еще предыдущие не считали).

Заметил, что если поставить точку останова в IRQ_TX(), то на этот момент ORE уже стоит. И если DMA что-то не успел дописать в буфер, то с этого байта в буфере будет мусор из повторяющегося значения входного регистра. А еще бывает мусор в начале буфера и его на ORE никак не списать, ибо дальше идут данные валидные. Черт знает вообще откуда это все берется
avg33
1. По совету Arlleex переместил снятие CS из прерывания по передаче IRQ_TX() в прерывание по чтению IRQ_RX()
2. Обнаружено, что с флешки на ножку контроллера RX приходит 3.3V, а контроллер работает с 5V. На всякий случай на вывод флешки был вкорячен подтягивающий резистор

После этого стало намного лучше. Но на скоростях >12000 бод время от времени в буфере продолжали появляться невалидные данные.
При этом у проблемы появилась системность: на очередном чтении буфер сначала целиком заполнялся 0xFF (это уровень на линии по умолчанию), а следующая итерация чтения давала либо то же самое, либо читала что-то похожее на правду, но гарантированно устанавливала ORE.
Порывшись в отладчике стало ясно, что к данному глюку приводит ситуация, когда прерывание по чтению IRQ_RX() вызывается раньше, чем прерывание по передаче IRQ_TX(). В IRQ_TX() у меня только снятие флага прерывания по передаче и отключение канала DMA. Не совсем понимаю, как это может вызвать забиваение буфера в 0xFF после очередного вызова read(), но четкая связь на лицо.

В общем, мне нужно сделать так, чтобы IRQ_TX() гарантированно вызывался раньше, чем IRQ_RX(). Я попробовал назначить IRQ_TX() более низкий приоритет и, кажется, это работает.
Насколько надежен и адекватен такой метод или лучше разрешить данную ситуацию как-то иначе через извращения в коде?
Arlleex
Цитата(avg33 @ Aug 9 2018, 19:55) *
Насколько надежен и адекватен такой метод или лучше разрешить данную ситуацию как-то иначе через извращения в коде?

Все-таки для начала желательно посмотреть логическим анализатором на лапки этого чудного SPI.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.