Кто любит RISC в жизни, заходим, не стесняемся.
Ответить

STM32 ILI9341 SPI+DMA GRAM R/W

Пн фев 29, 2016 22:16:31

Здравствуйте! =)

Пишу библиотеку под дисплей, встала задача производить чтение из графического буфера для последующей обработки.

Запись производится хорошо, чтение работает, но только один раз.
После чтения не хочет работать запись (команды с данными шлются, дисплей не отображает).
Отдебажил по даташиту https://www.adafruit.com/datasheets/ILI9341.pdf - вроде всё ок, результат дебага тут:
http://pastebin.com/tGue9hkk

Сами исходники можно посмотреть тут:
https://github.com/fagcinsk/stm-ILI9341 ... LI9341_lib

graph.c - LCD_readPixels - метод чтения пикселей с экрана;
dma.c - методы для чтения из памяти.

Есть подозрения, что работа с DMA организована неправильно.

Запись заработала после реинициализации CS пина, но после этого чтение не работает, да и это грязный хак.

К сожалению, анализатра логики, осциллографа нет, только дебаг по USART...

Re: STM32 ILI9341 SPI+DMA GRAM R/W

Ср мар 02, 2016 03:18:59

CS дергать не нужно. При отправке команды выставляется DC, и при отправке данных наоборот. Этого хватает.

Еще у вас слишком запутана работа DMA, размазана по коду слишком тяжело отслеживать настройки периферии.
Отправку данных и команд с помощью DMA для служебной настройки пользы дают ноль, быстрее через SPI сливать.
DMA имеет резон на потоках. Лучше всего это иметь фреймбуфер в памяти ОЗУ, строить всю графику и сливать одним разом.
В общем случае чтение вообще не требуется.
Драйвер никуда не годиться, переписывать однозначно.


Посмотрите сравните работу заливки области, как это сделано у меня одной функцией.
Шрифт я у себя тоже ускорил, шрифт рисуется в буфере, а потом буфер сливается в нужный квадрат.
Шрифты у меня пропорциональные. Для генерации виндовых шрифтов написал утилиту для генерации подключаемого файла шрифта.
Так же можно и строки буферизировать, это дает хороший выигрыш в скорости.


Код:
void GUI_FILL_RECT(uint16_t X1, uint16_t X2, uint16_t Y1, uint16_t Y2, uint16_t COLOR)
{
   uint32_t COUNT_PIXEL = 0;

    DMA_InitTypeDef DMA_INI;

    DMA_DeInit(DMA1_Channel3);
    DMA_INI.DMA_PeripheralBaseAddr = (uint32_t)&(SPI1->DR);  //Адрес регистра записи для периферии SPI1
    DMA_INI.DMA_MemoryBaseAddr = (uint32_t)&COLOR; //Указатель на буфер с цветом
    DMA_INI.DMA_DIR = DMA_DIR_PeripheralDST; //Направление: Периферия принимает.

    COUNT_PIXEL = (X2 - X1 + 1) * (Y2 - Y1 + 1);

    if (COUNT_PIXEL > 65535)
    DMA_INI.DMA_BufferSize = 65535;  //Количество байт для передачи
    else
    DMA_INI.DMA_BufferSize = COUNT_PIXEL;  //Количество байт для передачи

    DMA_INI.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //Автоинкремент периферии выключен, у нас фиксированный адрес
    DMA_INI.DMA_MemoryInc = DMA_MemoryInc_Disable; //Аналогично предыдущему.
    DMA_INI.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;  //Ширина SPI 16 бит
    DMA_INI.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;  //Ширина буфера 16 бит
    DMA_INI.DMA_Mode = DMA_Mode_Normal; //Нормальный режим DMA
    DMA_INI.DMA_Priority = DMA_Priority_VeryHigh; //Приоритет
    DMA_INI.DMA_M2M = DMA_M2M_Disable; //Память-память выключено.
    DMA_Init(DMA1_Channel3, &DMA_INI);
      

    TFT_AREA(X1, X2, Y1, Y2); //Установка области заливки
    TFT_DC_HIGH; //Погнали данные

    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE); //Разрешаем запросы DMA от SPI1
    SPI_DataSizeConfig(SPI1, SPI_DataSize_16b); //Переводим SPI в 16 бит

    DMA_Cmd(DMA1_Channel3, ENABLE); // СТАРТ ЗАЛИВКИ

    //С использованием программного опроса флага
    while(!DMA_GetFlagStatus(DMA1_FLAG_TC3)); //Первая половина экрана окрашена
    DMA1->IFCR = DMA1_FLAG_TC3;

    if (COUNT_PIXEL > 65535)
    {
        DMA_Cmd(DMA1_Channel3, DISABLE);
        DMA1_Channel3->CNDTR = COUNT_PIXEL - 65535;
        DMA_Cmd(DMA1_Channel3, ENABLE);
       while(!DMA_GetFlagStatus(DMA1_FLAG_TC3)); //Остальная часть  экрана окрашена
       DMA1->IFCR = DMA1_FLAG_TC3;
    };


    DMA_Cmd(DMA1_Channel3, DISABLE);  //Выключаем все.
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, DISABLE);
    SPI_DataSizeConfig(SPI1, SPI_DataSize_8b);
}

#define TFT_WIDTH   239
#define TFT_HEIGHT   319

GUI_FILL_RECT(0, TFT_WIDTH, 0, TFT_HEIGHT, color);

Re: STM32 ILI9341 SPI+DMA GRAM R/W

Ср мар 02, 2016 05:23:13

CS нет необходимости дёргать, когда нет другой переферии на SPI. Зачем нужна библиотека, если работать будет только дисплей?
Хотя если работать с прерываниями, например от тача - CS передёргивается на него, пока читаем только.

DMA инициализируется в dma.c, пины для SPI и его параметры устанавливаются в core.c. - тут должно быть всё прозрачно, лучше - только через классы, но c++ = жир, пробовал =)

Да, есть одно потенциальное место, где можно не понять, что происходит с настройками DMA - это процедуры передачи/приёма.
Сначала при инициализации применяется функция определения параметров DMA по умолчанию, затем при передаче на 16бит данных например устанавливается битность, направление работы DMA и остальные параметры, меняющиеся в пределах работы библиотеки.
Сделано это таким образом, чтобы не делать при каждой транзакции повторную инициализацию - лишние такты процессора на дёргание полей структуры, а при DMA_DeInit() - не только это.

Про отправку команд согласен, через DMA делать - нет смысла, но отправка данных начиная с > 1 единицы уже должна давать прирост в скорости (инициализацию тоже ускорит).

Про буферизацию:
хранить весь фрейм не получится, RAMы не хватит.
Да и оно не особо нужно, по кускам отправляется вполне быстро.
Шрифт да, тоже планирую буферизовать построчно, отображать, если получится, максимально большими регионами.

Для чего нужно использованить чтение:
* отрисовка сложных и затратных примитивов - круг. Можно взять регион, на котором нужно будет сделать отрисовку, затем забить в нуужном месте буфер нужными пикселями, а потом пачкой отдать дисплею
* отрисовка PNGшек например с прозрачностью, полупрозрачностью
* отрисовка шрифта поверх сложного фона (без заливки фоном).

Ну и в итоге получаем, что переписывать нужно только отправку команд, а инициализацию режимов DMA отладить.
Ну и да, для отладки попробовать использовать SPI без DMA, чтобы исключить неправильность работы с DMA.

Re: STM32 ILI9341 SPI+DMA GRAM R/W

Ср мар 02, 2016 06:46:13

Тут дело еще в том что сильно не разбежишся с фантазиями, типо PNG, прозрачность и прочее. Упретесь в быстродействие, ОЗУ, ФЛЕШ микроконтроллера. Это не компьютер.
Ставить STM32F4 с 1мб? :kill: Так там уже дисплеи другие, параллельные и с аппаратным ускорением.

Поэтому на этом дисплее нужен как можно меньше размером драйвер, высоким быстродействием и самым необходимым API.



Самый танец пойдет когда нужно будет связывать с ОС все это в параллельных задачах.

Re: STM32 ILI9341 SPI+DMA GRAM R/W

Ср мар 02, 2016 07:15:24

Да, оптимизировать неперевыоптимизировать =)

С PNG вроде как проблем не должно быть, если там не рисовать картины, так, иконки..
Формат PNG как раз предполагает построчное чтение. Даже отображение, если например схемы отображать, можно фигачить прям поцветно - быстро, правда не так эффективно =)

ОЗУ мониторить придётся, с инлайнами играться тоже..

Пока с включённой оптимизацией -O3 "ничего не работает" =)

Ещё вопрос: почему может не проходить инициализация дисплея на больших прескалерах?

На прескалере 2 всё норм, на прескалере 16 инициализация не проходит, а если врубить дебаг по USART - заводится :?

Может флаги обрабатываю не так? Или отправку делать нужно с проверкой флага до неё самой?

Re: STM32 ILI9341 SPI+DMA GRAM R/W

Ср мар 02, 2016 12:57:34

Т.е. инит не проходит на прексалере 16? На всех не работает? Только прескалер 2 работает?
Не правильный обмен с дисплеем.

Re: STM32 ILI9341 SPI+DMA GRAM R/W

Ср мар 02, 2016 13:08:29

  • Прескалер 2: всё отлично
  • Прескалер 4: заливка более медленная
  • Прескалер 8 : заливка ещё медленнее, иногда инициализация не проходит
  • Прескалер 16 и дальше: инициализация не проходит.

Re: STM32 ILI9341 SPI+DMA GRAM R/W

Ср мар 02, 2016 13:11:52

У меня такая же ботва с дисплеем, повышаю прескалер гонит.

2 и 4 работает, 8 инит проходит, но DMA пургу рисует, 16 ничего не работает.

Оформил тест для STM32F103
ПРЕСКАЛЕРЫ.zip
(42.67 KiB) Скачиваний: 226

Re: STM32 ILI9341 SPI+DMA GRAM R/W

Ср мар 02, 2016 16:25:35

2: всё ок, жёлтый фон, абвгд...
4: жёлтый фон, затем перекрывается белым
8: после 65535 где-то мусор идёт
16: инициализация не проходит..

Всё так же=)

Re: STM32 ILI9341 SPI+DMA GRAM R/W

Ср мар 02, 2016 19:17:00

И почему так есть идеи?

Re: STM32 ILI9341 SPI+DMA GRAM R/W

Ср мар 02, 2016 20:16:08

Думается, что надо ждать отправки/приёма по-другому...
Сейчас переделываю на чисто SPI, посмотрим, что из этого выйдет

Вот инфа по spi с прерываниями
http://we.easyelectronics.ru/STM32/hak- ... aster.html

Re: STM32 ILI9341 SPI+DMA GRAM R/W

Ср мар 02, 2016 21:35:03

После дебага выяснилась не правильная работа SPI.

Правильная работа выглядит так:

Код:
uint16_t TFT_SPI_SEND(uint16_t Data)
{
      SPI_I2S_SendData(SPI1, data);
     while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
      while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) != RESET);
      if (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == SET)
       { return  SPI_I2S_ReceiveData(SPI1); }  else return 0;
}


Все проблемы ушли, работает стабильно на всех прескалерах.
Последний раз редактировалось Oxford Чт мар 03, 2016 18:41:45, всего редактировалось 3 раз(а).

Re: STM32 ILI9341 SPI+DMA GRAM R/W

Чт мар 03, 2016 16:09:08

Только конструкция с if не будет полезна -> что-то здесь не так=)

Re: STM32 ILI9341 SPI+DMA GRAM R/W

Чт мар 03, 2016 17:49:28

fagci писал(а):Только конструкция с if не будет полезна -> что-то здесь не так=)

Исправил, ";" забыл убрать. :kill:

Re: STM32 ILI9341 SPI+DMA GRAM R/W

Вс мар 06, 2016 08:24:16

Hardware NSS некорректно работает с чтением. Нужно использовать software.
Для DMA вместо ожидания сброса переменной из прерывания нужно до сброса CS ждать, пока SPI воркает =)
Код:
#define dmaWait() while(SPI_I2S_GetFlagStatus(SPI_MASTER,SPI_I2S_FLAG_BSY) == SET);

Тогда будет работать на всех прескалерах.

Re: STM32 ILI9341 SPI+DMA GRAM R/W

Вт авг 13, 2019 15:21:03

Понекропостим =)

Долго возился с подключением DMA на STM32f103, в итоге удалось завести следующим образом:
https://mikhail-yudin.ru/hardware/stm32 ... -with-dma/

Re: STM32 ILI9341 SPI+DMA GRAM R/W

Сб авг 17, 2019 05:03:01

Норм либа, более менее нормально все написано.
Ответить