Перейти к содержанию
    

Неверные данные при чтении по SPI через DMA

Всем привет. Пытаюсь прочитать 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 в единицу уловить невозможно, я так и не выявил зависимости. Что я делаю не так, в чем может быть проблема?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Всем привет. Пытаюсь прочитать SPI флэшку по DMA. Работаю через USART.

Что за контроллер-то?

Не забывайте, что контроллер DMA выставит флаг "транзакция завершена" после перекладывания последней порции данный лишь в сдвиговый регистр. Поэтому

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

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

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

Изменено пользователем Arlleex

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Arlleex, благодарю за оперативный ответ.

 

Что за контроллер-то?

F2MC-16FX MB96600 Series

 

Иначе снимите CS до в процессе передачи последнего слова.

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

 

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

По передаче или приему? Или и то и другое?) Я просто указал DMA сколько и откуда писать, куда читать, по каким перываниям срабатывать. Даже не знаю, где там можно ошибиться

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

F2MC-16FX MB96600 Series

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

 

И вправду. А если снять его в IRQ_RX()?

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

 

По передаче или приему? Или и то и другое?) Я просто указал DMA сколько и откуда писать, куда читать, по каким перываниям срабатывать. Даже не знаю, где там можно ошибиться

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Хотя не ручаюсь точно - ORE это же флаг переполнения входного буфера (то есть туда пришли данные когда еще предыдущие не считали).

Заметил, что если поставить точку останова в IRQ_TX(), то на этот момент ORE уже стоит. И если DMA что-то не успел дописать в буфер, то с этого байта в буфере будет мусор из повторяющегося значения входного регистра. А еще бывает мусор в начале буфера и его на ORE никак не списать, ибо дальше идут данные валидные. Черт знает вообще откуда это все берется

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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() более низкий приоритет и, кажется, это работает.

Насколько надежен и адекватен такой метод или лучше разрешить данную ситуацию как-то иначе через извращения в коде?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Насколько надежен и адекватен такой метод или лучше разрешить данную ситуацию как-то иначе через извращения в коде?

Все-таки для начала желательно посмотреть логическим анализатором на лапки этого чудного SPI.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...