• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Esp8266->spi->tft (ili9341) без внешнего mcu

Perfer

New member
Пример работы ESP8266 с 2,2" дисплеем на чипе ili9341 по SPI без внешнего MCU
Вращающийся 3D кубик (зеленая плата на заднем плане FT2232H Board используется для перепрошивки):
Проект на GitHub: https://github.com/Perfer/esp8266_ili9341
В проекте расчет sin(x)/cos(x) производиться cordic методом, подробнее про метод: http://bsvi.ru/cordic-dlya-chajnikov/
 

Sermus

New member
Уважаемый Gerber, смотрю код hspi.c и вот чего не понимаю. Функция записи в SPI-буфер разве не может быть реализована вот так:
memcpy((uint8_t*)SPI_FLASH_C0(HSPI), data, numberByte)? Ведь, насколько я понимаю, камень работает в little-endian?

Я еще не знаю, есть ли в поставляемых библиотеках memcpy, но сути вопроса это не меняет, почему нельзя заменить процедуру тупым побайтным копированием?
 

Perfer

New member
Есть мнение что работать не будет, по вот таким соображениям: если почитать содержимое http://bbs.espressif.com/download/file.php?id=109&sid=8dae87d845a00f473dba015b7265be2e, а именно spi_reg.xls то там вот что написано:
spi_w0~spi_w15 - R/W the data buffer inside SPI module. There are 64byte, i.e., 16 words. Note that only 32bit accessing are supported.
а значит memcpy точно должна обеспечивать доступ к регистрам как к 32 битным ячейкам памяти, т.е. просто так адресоваться к одному байту регистра нельзя, по этому большой вопрос по реализации memcopy.
Также я пробовал сделать прямое обращение к регистра HSPI как к однобайтным ячейкам памяти - ничего не вышло, записывался только первый байт, остальные не устанавливались
 

Sermus

New member
Спасибо за наводку, интересно. Ну хорошо, допустим, что оно действительно допускает установку слова одномоментно, и попытка установить отдельный байт, например, сбрасывает три оставшихся байта в рамках слова. Тем не менее, почему тогда не сделать так (код для количества байт, выровненного по границе 4, чтобы не заморачиваться с определением хвоста, для сути вопроса это небольшой урон):
Код:
for (int wi = 0; wi < numBytes/4; wi++)
    ((uint32_t*)SPI_FLASH_C0(HSPI)) [i]= ((uint32_t*)data) [i];
Зачем здесь необходимы сложности с перепаковкой. Пытаюсь осознать чего я не понимаю.

И второй вопрос. Если размер SPI-буфера 16 слов, почему у Вас размер промежуточного буфера 32 байта? Это с какой-то целью сделано?
 

Perfer

New member
1. А если numBytes % 4 != 0 то тогда часть данных не отправится, правильно?
2. Подождите: dword = 32 бита, word = 16 бит. Регистры HSPI у нас 32 битные поэтому 8 * 32 = 256 бит = 32 байта - кажется все корректно. Да и если посмотреть то MAX_SIZE_BUFFER нигде по делу не используется, а объявление локальной переменной (uint8_t dataBuffer[MAX_SIZE_BUFFER];) можно выкинуть
 

Sermus

New member
1. Да, это так, но к сути вопроса это не имеет отношения, поэтому я не стал усложнять пример для учета хвоста, который на выровнен по границе 4.
2. Почему слово - 16 бит? Для этой архитектуры слово 32-битное. В приведенной Вами же выше цитате, написано, что размер SPI-буфера 64 байта или 16 слов. Отсюда мой вопрос, почему у Вас в коде размер буфера 32 байта?
 

Perfer

New member
1. Я сразу решил написать общий случай, а не дописывать потом обработку хвоста. За максимальной производительностью не гнался. Если вы проведете тесты и покажете что вами модифицированный код работает быстрее, то - wellcome, кидайте pull-request приму изменения
2. Хмм.... настойчивость это хорошо. Читаем spi_register.h:
Код:
#define SPI_FLASH_C0(i)                             (REG_SPI_BASE(i) +0x40)
#define SPI_FLASH_C1(i)                             (REG_SPI_BASE(i) +0x44)
#define SPI_FLASH_C2(i)                             (REG_SPI_BASE(i) +0x48)
#define SPI_FLASH_C3(i)                             (REG_SPI_BASE(i) +0x4C)
#define SPI_FLASH_C4(i)                             (REG_SPI_BASE(i) +0x50)
#define SPI_FLASH_C5(i)                             (REG_SPI_BASE(i) +0x54)
#define SPI_FLASH_C6(i)                             (REG_SPI_BASE(i) +0x58)
#define SPI_FLASH_C7(i)                             (REG_SPI_BASE(i) +0x5C)
Считаем:
(REG_SPI_BASE(i) + 0x5C + 0x04) - ( REG_SPI_BASE(i) +0x40 ) + 0x01 = 0x20 = 32
или я вас не правильно понял?
PS вы сами себя запутали с размерами и понятием слова
PSS прошу обратить внимание что MAX_SIZE_BUFFER на самом деле не нужен в текущей реализации
 

Sermus

New member
А где путаница? Слова 4-ехбайтные, вопрос в их количестве. Опираясь на первую выдержку из документа, которую Вы привели, размер буфера - 64 байта, т.е. 16 слов по 4 байта, что явно написано в выдержке. Это и породило вопрос про размер буфера у Вас в hspi.c.
И кстати, размер используется например так: tft_setPixel -> transmitData(uint8_t *data, uint8_t numByte, uint32_t numRepeat) -> hspi_Tx(uint8_t * data, uint8_t numberByte, uint32_t numberRepeat), а там уж по крайней мере стоит выход без передачи по превышению порога MAX_SIZE_BUFFER.

Я понимаю, что наверняка в коде складываются условия, в которых эта проверка никогда не стреляет, но... Это только потому что трансфер, видимо, неоптимален. Ну, например, закрашивать прямоугольник можно было бы не двухбайтными посылками, а 64-хбайтными, что было бы эффективнее.
Собственно, это утверждение и есть корень вопроса про размер spi-буфера.
 

Sermus

New member
Ладно, разговор, видимо, перейдет в стадию гадания на кофейной гуще на почве недосказанности и противоречивости в китайской документации. В одном месте действительно написано, что 16 слов, в другом - 8. Придет ко мне железка, посмотрю. Заодно и проверим теорию про скорость трансфера пачками большего размера.
 

Perfer

New member
Я понимаю, что наверняка в коде складываются условия, в которых эта проверка никогда не стреляет, но... Это только потому что трансфер, видимо, неоптимален. Ну, например, закрашивать прямоугольник можно было бы не двухбайтными посылками, а 64-хбайтными, что было бы эффективнее.
В hspi.c не просто так оставлен закомментированный код - разберитесь что он делает, помниться он был рабочим, ток что-то до определить придется.
Про слова и их кол-во почитайте еще описание в ESP8266 HSPI透传协议(2中断线).doc лежит в архиве по ссылке выше (переводить лучше на английский). Если кратко, то получается не очевидное использование регистров spi_w8~spi_w15 в режиме spi-master
Заодно и проверим теорию про скорость трансфера пачками большего размера
Все уже проверенно, если честно :) Правда я за сверх производительностью не гнался.
А так можно еще много чего с оптимизировать - только вопрос ЗАЧЕМ? :)
 
Последнее редактирование:

Sermus

New member
Так закомментированный код работает или нет? Как зачем? Чтобы кубик на Вашем видео не моргал.
А если серьезно, то нарисовать более или менее адекватный UI при малой скорости рисования просто нереально. У меня есть библиотека от Adafruit для ILI9341 для Ардуины, страсть какая медленная, но она мне функционально нравится. Я ее для Atmega ускорил на 1.5 порядка (не в полтора раза, а в 16) за счет переезда с bitbanding на аппаратный SPI. После этого даже на убогой восьмибитной атмельке стало возможно рисовать полноценный UI.
Хочу портировать (на самом деле уже портировал, но не железки пока для проверки) эту библиотеку на ESP8266. И хочется, чтобы она сразу быстро работала.

Еще из замечаний - ожидание конца передачи в hspi_Tx. Зачем? Лучше ведь перед передачей убедиться, что предыдущая передача закончилась. А в конце не ждать - отпустить процессор, пусть он в это же время готовит данные для следующей передачи, а не ждет конца текущей.
 

Perfer

New member
Еще из замечаний - ожидание конца передачи в hspi_Tx. Зачем? Лучше ведь перед передачей убедиться, что предыдущая передача закончилась. А в конце не ждать - отпустить процессор, пусть он в это же время готовит данные для следующей передачи, а не ждет конца текущей.
А это привычка :) Согласен надо поправить
Можно вообще от while уйти в сторону конструкций case/ifelse - получиться некий конечный автомат
Я вот это портировал https://github.com/gmtii/ili9341-arduino
 

Sermus

New member
Эту я тоже видел, но мне там не хватает шрифтов. В общем, железка приедет - опробую то, что понаписал.
 

pvvx

Активный участник сообщества
Ладно, разговор, видимо, перейдет в стадию гадания на кофейной гуще на почве недосказанности и противоречивости в китайской документации. В одном месте действительно написано, что 16 слов, в другом - 8. Придет ко мне железка, посмотрю. Заодно и проверим теорию про скорость трансфера пачками большего размера.
У SPI на ESP8266 есть "FIFO":
Код:
60000100: 00000000 00000000 0028b313 5fff0120  ........._(. .я_
60000110: 00000000 00000011 80003043 80000044  ........C0._D.._
60000120: 5c000000 70000000 00000000 0000001e  ...\...p........
60000130: 00000200 02000000 00000000 00000000  ................
60000140: a7d40e60 f481f81d 2c3034c3 be1b9b31  `.Ф.ш_фГ40,1>._
60000150: 2352dda8 04960cf7 4c422943 7d60ce8d  ЁЭR#ч.-.C)BL_О`}
60000160: 0b08d192 bf3651d2 2de1b158 45dae09d  'С..ТQ6їX+б-_аЪE
60000170: 0b31b142 61c64f30 e9415914 8afa1be5  B+1.0OЖa.YAйе.ъ_
60000180: 00000000 00000000 00000000 00000000  ................
60000190: 00000000 00000000 00000000 00000000  ................
600001a0: 00000000 00000000 00000000 00000000  ................
600001b0: 00000000 00000000 00000000 00000000  ................
600001c0: 00000000 00000000 00000000 00000000  ................
600001d0: 00000000 00000000 00000000 00000000  ................
600001e0: 00000000 00000000 00000000 00000000  ................
600001f0: 800a0050 800f0258 00000000 00000000  P.._X.._........
60000200: 00000000 0801799a 016ab000 5fff0120  ...._y...°j. .я_
60000210: 00000000 00000011 80000000 80000064  ..........._d.._
60000220: 5c7e3f1f 70000000 00000000 0000001e  .?~\...p........
60000230: 00000210 02000000 00000000 00000000  ................
60000240: 64786568 7e64706d 7463657e 6d61722f  hexdmpd~~ect/ram
60000250: 6e69622e 6c627e00 6d61726b 0000297e  .bin.~blkram~)..
60000260: 5d841651 49ddae04 ef2baf7f b2b07b5c  Q."].RЭI.Ї+п\{°_
60000270: 261fa13c 800b0c75 439152b1 bc1e7b8e  <Ў.&u.._+R'C_{._
60000280: 00000000 00000000 00000000 00000000  ................
60000290: 00000000 00000000 00000000 00000000  ................
600002a0: 00000000 00000000 00000000 00000000  ................
600002b0: 00000000 00000000 00000000 00000000  ................
600002c0: 00000000 00000000 00000000 00000000  ................
600002d0: 00000000 00000000 00000000 00000000  ................
600002e0: 00000000 00000000 00000000 00000000  ................
600002f0: 800a0050 800f0258 00000000 00000000  P.._X.._........
0x60000140 и 0x60000240...
 
Последнее редактирование:

pvvx

Активный участник сообщества
Спасибо за дамп памяти!
Наличие FIFO у SPI известный факт, подробности неплохо описаны вот здесь http://bbs.espressif.com/download/file.php?id=109&sid=8dae87d845a00f473dba015b7265be2e
Это всё известно, но тут пытались спорить о размерах...
Разработка ‘библиотеки’ малого webсервера на esp8266 для этих целей в основном и делалась - чтобы отлаживать на живую и смотреть "что тама". Для более глубоких исследований надо писать полный отладчик с break-points.
 

Sermus

New member
Просветите, пжлста, меня, убогого, а как из дампа видно, что это fifo для spi?
 

Perfer

New member
Просветите, пжлста, меня, убогого, а как из дампа видно, что это fifo для spi?
Товарищ pvvx говорит не про факт наличия FIFO, а про его размер.
Сегодня проверил что с значением MAX_SIZE_BUFFER = 64 тоже все работает и корректно отображается - правда кубик быстрее (с меньшим количеством рывков) крутиться не стал, а вот с оптимизацией функций setCol и setPos - стал. Обновления в репозитории
 

pvvx

Активный участник сообщества
Просветите, пжлста, меня, убогого, а как из дампа видно, что это fifo для spi?
Там отображается то, что читалось. А это не память, a область регистров.
-------
Перед вычислением включите CPU на 160MHz:
REG_SET_BIT(0x3ff00014, BIT(0));
os_update_cpu_frequency(160);
По окончанию можно обратно на 80MHz:
REG_CLR_BIT(0x3ff00014, BIT(0));
os_update_cpu_frequency(80);
Но SDK сама поправит через время на 80MHz...
Это ускорит исполнение ваших расчетов, но не I/O...
Бояться не стоит, так штатно поступает SDK при включении SSL и общий ток и нагрев сильно не растет (больше греет WiFi и I/O).
 
Последнее редактирование:

Sermus

New member
правда кубик быстрее (с меньшим количеством рывков) крутиться не стал, а вот с оптимизацией функций setCol и setPos - стал. Обновления в репозитории
Я подозреваю, что он стал крутиться после ампутации магии с перекладыванием байтиков во writeDataToBuffer (зуб даю, она теперь выполняется в 5 раз быстрее), а не оптимизации setCol и setPos. Не понятно, как не посылка команд установки окна в случае, если перед этим было установлено то же самое окно, может помочь, если окно выставляется все время разное, т.е. фактически, "экономный код" не работает. Или я не прав?

А быстрее крутиться из-за увеличения размера буфера он скорее всего не стал потому что:
1) Есть лишний промежуточный буфер (который как раз размера MAX_BUFFER_SIZE). Почему бы не писать из пользовательского буфера сразу в SPI FIFO?
2) Ждите окончания посылки не после начала передачи, а перед началом следующей
 
Сверху Снизу