avg33 0 7 августа, 2018 Опубликовано 7 августа, 2018 · Жалоба Всем привет. Пытаюсь прочитать 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 131 7 августа, 2018 Опубликовано 7 августа, 2018 (изменено) · Жалоба Всем привет. Пытаюсь прочитать SPI флэшку по DMA. Работаю через USART. Что за контроллер-то? Не забывайте, что контроллер DMA выставит флаг "транзакция завершена" после перекладывания последней порции данный лишь в сдвиговый регистр. Поэтому void IRQ_TX() { //сюда придем по завершению приема //здесь выполняем всякие служебные вещи, чистим флаги и тд //и снимаем CS FLASH_CS = FLASH_OFF; } не придете Вы сюда по завершению приема. Вы должны в прерывании этом выставить флажок (либо настроить прерывание по Transfer Complete), чтобы сигнализировать о реальном завершении транзакции. Иначе снимите CS до истинной передачи последнего слова. Либо Вы не правильно настроили связку сигналов DMA-контроллера с модулем USART по передаче. Изменено 7 августа, 2018 пользователем Arlleex Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
avg33 0 7 августа, 2018 Опубликовано 7 августа, 2018 · Жалоба Arlleex, благодарю за оперативный ответ. Что за контроллер-то? F2MC-16FX MB96600 Series Иначе снимите CS до в процессе передачи последнего слова. И вправду. А если снять его в IRQ_RX() ? По идее прерывание по приему должно сработать после того, как DMA переложит последний байт в буфер. Или там тоже нюансы со сдвиговым регистром? Либо Вы не правильно настроили связку сигналов DMA-контроллера с модулем USART по передаче. По передаче или приему? Или и то и другое?) Я просто указал DMA сколько и откуда писать, куда читать, по каким перываниям срабатывать. Даже не знаю, где там можно ошибиться Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 131 7 августа, 2018 Опубликовано 7 августа, 2018 · Жалоба F2MC-16FX MB96600 Series Не работал, но логика периферийных узлов во всех МК, в общем-то не глобально отличается... И вправду. А если снять его в IRQ_RX()? Уже теплее. Так и нужно, да. По передаче или приему? Или и то и другое?) Я просто указал DMA сколько и откуда писать, куда читать, по каким перываниям срабатывать. Даже не знаю, где там можно ошибиться Ну если прием у Вас работает при передаче "ручками", то, очевидно, исходя от противного - проблема в передаче :laughing: Хотя не ручаюсь точно - ORE это же флаг переполнения входного буфера (то есть туда пришли данные когда еще предыдущие не считали). Поэкспериментируйте, решение где-то рядом. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
avg33 0 7 августа, 2018 Опубликовано 7 августа, 2018 · Жалоба Хотя не ручаюсь точно - ORE это же флаг переполнения входного буфера (то есть туда пришли данные когда еще предыдущие не считали). Заметил, что если поставить точку останова в IRQ_TX(), то на этот момент ORE уже стоит. И если DMA что-то не успел дописать в буфер, то с этого байта в буфере будет мусор из повторяющегося значения входного регистра. А еще бывает мусор в начале буфера и его на ORE никак не списать, ибо дальше идут данные валидные. Черт знает вообще откуда это все берется Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
avg33 0 9 августа, 2018 Опубликовано 9 августа, 2018 · Жалоба 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 131 10 августа, 2018 Опубликовано 10 августа, 2018 · Жалоба Насколько надежен и адекватен такой метод или лучше разрешить данную ситуацию как-то иначе через извращения в коде? Все-таки для начала желательно посмотреть логическим анализатором на лапки этого чудного SPI. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться