Запуск Трансфлективного Tft-Дисплея На Ssd1283A С Использованием Stm32



Введение Модель дисплея называется H016IT01. Этот дисплей интересен прежде всего тем, что он трансфлективный.

Это значит, что изображение на нем должно быть видно даже при ярком солнце.

А еще это чуть ли не единственная доступная модель с такой функцией на известном китайском сайте.

Статью опубликовал потому, что по контроллеру SSD1283A очень мало информации (как в российском, так и в западном сегментах сети), а мануала я нигде не видел.

В интернете можно найти даташит, но там нет информации по инициализации и работе с дисплеем, а полезно только описание регистров.

Хочу подчеркнуть, что этот материал, конечно, не является истиной в последней инстанции.

Я даю только свой опыт взаимодействия с устройством.

Основная цель статьи проста – помочь всем тем, кто решил, хочет или хочет работать с этим дисплеем, не более того.



Запуск трансфлективного TFT-дисплея на SSD1283A с использованием STM32

Дисплей имеет 8 контактов: 1) Земля 2) ВСС - 5 или 3,3В 3) CS — выбор чипа SPI 4) RST – «0» – выключает отображение, «1» – включает. 5) A0/DC — Команда данных («0» — команда, «1» — данные) 6) ПДД - СПИ МОСИ 7) ССК - СПИ ССК 8) LED - выход подсветки, типа VCC, от 3,3 до 5В Мне нужно запрограммировать дисплей через SPI; в этом мне поможет плата Discovery на stm32f407.



СПИ

Хоть я и взаимодействовал с SSD1283A по SPI, стоит отметить, что контроллер также предоставляет параллельный интерфейс, но данная модель дисплея его не поддерживает. SPI у него тоже не обычный, линия данных SDA всего одна.

По сути, это линия MOSI, а значит, мы не можем ничего прочитать с дисплея, о чем нам говорит даташит.

Запуск трансфлективного TFT-дисплея на SSD1283A с использованием STM32

Для начала настроим SPI, для этого будем тактировать SPI1 и GPIO, настроим ножки SDA и SCK как альтернативную функцию (я еще делал MISO, но это не обязательно).

Настраиваем режим работы как однонаправленный ведущий передатчик.

  
  
  
  
  
  
  
  
  
   

BIT_BAND_PER(RCC->AHB1ENR ,RCC_AHB1ENR_GPIOAEN) = true; if(SPI_NUM::_1 == spi) { /*!<-------------Enable spi clocking--------------->!*/ BIT_BAND_PER(RCC->APB2ENR, RCC_APB2ENR_SPI1EN) = true; /*!<----PA5 - SCK, PA6 - MISO, PA7 - MOSI---->!*/ GPIOA->MODER |= (GPIO_MODER_MODE5_1 | GPIO_MODER_MODE6_1 | GPIO_MODER_MODE7_1); GPIOA->OSPEEDR |= (GPIO_OSPEEDR_OSPEED5_1 | GPIO_OSPEEDR_OSPEED7_1); GPIOA->AFR[0] |= (GPIO_AFRL_AFSEL5_0 | GPIO_AFRL_AFSEL5_2 | GPIO_AFRL_AFSEL6_0 | GPIO_AFRL_AFSEL6_2 | GPIO_AFRL_AFSEL7_0 | GPIO_AFRL_AFSEL7_2); /*!<-----customize SPI------>!*/ SPI1->CR1 |= (SPI_CR1_BIDIMODE | SPI_CR1_BIDIOE | SPI_CR1_SSM | SPI_CR1_SSI /*| SPI_CR1_DFF*/ | SPI_CR1_MSTR); BIT_BAND_PER(SPI1->CR1 ,SPI_CR1_SPE) = true; }

Затем напишем простую функцию для передачи байта по SPI:

void stm32f407_spi::stm32f407_spi_send(uint8_t data) { SPI1->DR = data; while((SPI1->SR & SPI_SR_BSY)) continue; }

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



Инициализация дисплея

Теперь пришло время инициализировать дисплей.

Для этого вам необходимо отправить определенную последовательность команд. Команда состоит из 3 байтов, где один байт — номер команды, а два других — данные.

Также необходимо переключить вывод A0 при отправке командного байта на «0», а при отправке данных на «1».

Для удобства я сделал встроенные функции для переключения состояния выводов RST, A0 и CS дисплея.



enum class DC : uint8_t { COMMAND, DATA }; #pragma inline=forced inline void tft_lcd_rst(bool rst) {BIT_BAND_PER(GPIOA->ODR , GPIO_ODR_OD2) = rst;} #pragma inline=forced inline void tft_lcd_dc(DC dc) {BIT_BAND_PER(GPIOA->ODR , GPIO_ODR_OD3) = static_cast<bool>(dc);} #pragma inline=forced inline void tft_lcd_cs(bool cs) {BIT_BAND_PER(GPIOA->ODR , GPIO_ODR_OD4) = cs;}

Тогда сообщение команды будет выглядеть так:

void tft_lcd::tft_lcd_send(uint8_t addr, uint16_t data) { this->tft_lcd_dc(DC::COMMAND); stm32f407_spi_send(addr); this->tft_lcd_dc(DC::DATA); stm32f407_spi_send(static_cast<uint8_t>(data >> 8)); stm32f407_spi_send(static_cast<uint8_t>(data)); }

Для инициализации вам необходимо подать следующую последовательность команд, которую я нашел в библиотеках Arduino для этого экрана: Команды для инициализации

static constexpr uint8_t TFT_DELAY = 0xFF; static constexpr t_tft_regs tft_regs[]= { { 0x10, 0x2F8E }, { 0x11, 0x000C }, { 0x07, 0x0021 }, { 0x28, 0x0006 }, { 0x28, 0x0005 }, { 0x27, 0x057F }, { 0x29, 0x89A1 }, { 0x00, 0x0001 }, { TFT_DELAY, 100 }, { 0x29, 0x80B0 }, { TFT_DELAY, 30 }, { 0x29, 0xFFFE }, { 0x07, 0x0223 }, { TFT_DELAY, 30 }, { 0x07, 0x0233 }, { 0x01, 0x2183 }, { 0x03, 0x6830 }, { 0x2F, 0xFFFF }, { 0x2C, 0x8000 }, { 0x27, 0x0570 }, { 0x02, 0x0300 }, { 0x0B, 0x580C }, { 0x12, 0x0609 }, { 0x13, 0x3100 }, };

где TFT_DELAY означает простой сон на указанное количество мс.

Если покопаться в даташите, то такие данные для некоторых адресов покажутся странными, так как для многих адресов запись идёт в зарезервированные биты.

Для инициализации мы применим «0» к CS, перезагрузим дисплей (вывод RST) и пройдемся по таблице команд.

tft_lcd_rst(false); delay(5, mS); tft_lcd_rst(true); delay(200, mS); this->tft_lcd_cs(false); delay(5, mS); /*!<--------Init display---------->!*/ for(uint8_t i = 0; i < sizeof(tft_regs)/sizeof(tft_regs[0]) ;i++) { (TFT_DELAY != tft_regs[i].

address) ? (this->tft_lcd_send(tft_regs[i].

address, tft_regs[i].

value)): (delay(tft_regs[i].

value, mS)); } delay(5, mS); this->tft_lcd_cs(true);

После этого изображение на дисплее должно измениться с белого на серый цвет телевизионных помех.



Запуск трансфлективного TFT-дисплея на SSD1283A с использованием STM32



Рисование прямоугольника

Контроллер SSD1283A позволяет рисовать изображения прямоугольниками, для чего используются 4 команды.

Команда 0x44 содержит координаты конца и начала прямоугольника по оси X в старшем и младшем байте данных соответственно.

Команда 0x45 аналогична оси Y. Команда 0x21 содержит координату исходного рисунка: в старшем байте — y, в младшем — x. Команда 0x22 содержит цвет текущего пикселя.

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

У дисплея тоже есть особенность, хотя сам он имеет разрешение 130х130, его виртуальная координатная сетка имеет размеры 132х132, а координаты начинаются с точки 2х2. Таким образом, если мы, например, хотим нарисовать черный квадрат 20 на 20, начальная точка которого находится в позиции (30, 45), то нам необходимо передать следующую последовательность команд: 0x44 0x3320 (30+20+2-1, 30+2) 0x45 0x422F (45+20+2-1, 45+2) 0x21 0x2F20 0x22 0x0000, и эту команду необходимо передать 400(20*20) раз.

Тогда функция рисования прямоугольника будет выглядеть так (при условии, что координаты уже сдвинуты на 2):

void tft_lcd::draw_rect(const t_rect& rec) { this->tft_lcd_send(0x44, ((rec.x1 - 1) << 8) | rec.x0); this->tft_lcd_send(0x45, ((rec.y1 - 1) << 8) | rec.y0); this->tft_lcd_send(0x21, (rec.y0 << 8) | rec.x0); for(uint16_t i = 0; i < ((rec.x1 - rec.x0) * (rec.y1 - rec.y0)); i++) { this->tft_lcd_send(0x22, rec.col); } }

Чтобы нарисовать прямоугольник, вам просто нужно указать координаты его углов и цвет. Пример заполнения всего экрана розовым цветом будет выглядеть так:

t_rect rect = {0x02, 0x02, 0x84, 0x84, static_cast<uint16_t>(COLOUR::MAGENTA)}; this->draw_rect(rect);

Результат:

Запуск трансфлективного TFT-дисплея на SSD1283A с использованием STM32



Рисуем буквы и цифры

Создадим список массивов с координатами символов.

Массивы координат букв и цифр

namespace rect_coord_lit { const t_rect_coord comma[] = {{0, 20, 5, 25}, {3, 25, 5, 28}}; const t_rect_coord dot[] = {{0, 20, 5, 25}}; const t_rect_coord space[] = {{0, 0, 0, 0}}; const t_rect_coord _0[] = {{0, 0, 15, 5},{0, 5, 5, 25},{5, 20, 15, 25},{10, 5, 15, 20}}; const t_rect_coord _1[] = {{10, 0, 15, 25}}; const t_rect_coord _2[] = {{0, 0, 15, 5},{10, 5, 15, 15},{0, 10, 10, 15},{0, 15, 5, 25},{5, 20, 15, 25}}; const t_rect_coord _3[] = {{0, 0, 15, 5},{10, 5, 15, 25},{0, 10, 10, 15},{0, 20, 10, 25}}; const t_rect_coord _4[] = {{0, 0, 5, 15},{5, 10, 10, 15},{10, 0, 15, 25}}; const t_rect_coord _5[] = {{0, 0, 15, 5},{0, 5, 5, 15},{0, 10, 15, 15},{10, 15, 15, 25},{0, 20, 10, 25}}; const t_rect_coord _6[] = {{0, 0, 15, 5},{0, 5, 5, 25},{5, 10, 10, 15},{5, 20, 10, 25},{10, 10, 15, 25}}; const t_rect_coord _7[] = {{0, 0, 15, 5},{10, 5, 15, 25}}; const t_rect_coord _8[] = {{0, 0, 15, 5},{0, 5, 5, 25},{5, 20, 15, 25},{10, 5, 15, 20},{5, 10, 10, 15}}; const t_rect_coord _9[] = {{0, 0, 15, 5},{0, 5, 5, 15},{0, 20, 15, 25},{10, 5, 15, 20},{5, 10, 10, 15}}; const t_rect_coord a[] = {{0, 10, 5, 25},{5, 5, 10, 10},{5, 15, 10, 20},{10, 10, 15, 25}}; const t_rect_coord b[] = {{0, 0, 5, 25},{5, 10, 15, 15},{10, 15, 15, 20},{5, 20, 15, 25}}; const t_rect_coord c[] = {{0, 5, 15, 10},{0, 10, 5, 20},{0, 20, 15, 25}}; const t_rect_coord d[] = {{0, 10, 10, 15},{0, 15, 5, 20},{0, 20, 10, 25}, {10, 0, 15, 25}}; const t_rect_coord e[] = {{0, 5, 15, 8}, {0, 12, 15, 15}, {0, 8, 5, 25}, {10, 8, 15, 12}, {5, 20, 15, 25}}; const t_rect_coord f[] = {{5, 5, 10, 25},{5, 0, 15, 5},{0, 10, 15, 15}}; const t_rect_coord g[] = {{0, 5, 5, 20}, {5, 5, 10, 10}, {5, 15, 10, 20}, {10, 5, 15, 30}, {0, 25, 10, 30}}; const t_rect_coord h[] = {{0, 0, 5, 25},{5, 10, 15, 15},{10, 15, 15, 25}}; const t_rect_coord i[] = {{5, 3, 10, 8},{5, 10, 10, 25}}; const t_rect_coord j[] = {{5, 3, 10, 8},{5, 10, 10, 30}, {0, 25, 5, 30}}; const t_rect_coord k[] = {{0, 0, 5, 25},{5, 15, 10, 20}, {10, 10, 15, 15}, {10, 20 , 15, 25}}; const t_rect_coord l[] = {{5, 0, 10, 25}}; const t_rect_coord m[] = {{0, 10, 4, 25},{7, 10, 10, 25}, {13, 10, 17,25}, {0, 5 , 12, 10}}; const t_rect_coord n[] = {{0, 10, 5, 25},{10, 10, 15, 25}, {0, 5 , 10, 10}}; const t_rect_coord o[] = {{0, 5, 5, 25}, {10, 5, 15, 25}, {5, 5, 10, 10}, {5, 20, 10, 25}}; const t_rect_coord p[] = {{0, 5, 5, 30}, {5, 5, 15, 10}, {5, 15, 15, 20}, {10, 10, 15, 15}}; const t_rect_coord q[] = {{0, 5, 5, 20}, {5, 5, 15, 10}, {5, 15, 15, 20}, {10, 10, 15, 30}}; const t_rect_coord r[] = {{0, 10, 5, 25},{5, 5, 15, 10}}; const t_rect_coord s[] = {{3, 5, 15, 10}, {0, 8, 5, 13}, {3, 13, 12, 17}, {10, 17, 15, 22}, {0, 20, 12, 25}}; const t_rect_coord t[] = {{5, 0, 10, 25},{0, 5, 15, 10},{10, 20, 15, 25}}; const t_rect_coord u[] = {{0, 5, 5, 25},{10, 5, 15, 25},{5, 20, 10, 25}}; const t_rect_coord v[] = {{0, 5, 5, 15}, {10, 5, 15, 15}, {1, 15, 6, 20}, {9, 15, 14, 20}, {5, 20, 10, 25}}; const t_rect_coord w[] = {{0, 5, 4, 20},{7, 5, 10, 20}, {13, 5, 17, 20}, {4, 20 , 7, 25}, {10, 20 , 13, 25}}; const t_rect_coord x[] = {{0, 5, 5, 10},{10, 5, 15, 10}, {0, 20, 5, 25}, {10, 20 , 15, 25}, {5, 10 , 10, 20}}; const t_rect_coord y[] = {{0, 5, 5, 20}, {5, 15, 10, 20}, {10, 5, 15, 30}, {0, 25, 10, 30}}; const t_rect_coord z[] = {{0, 5, 15, 10}, {10, 10, 15, 13}, {5, 12, 10, 18}, {0, 17, 5, 20}, {0, 20, 15, 25}}; }

Создадим таблицу, в которой соотнесем сам символ (в ascii), количество его прямоугольников и его координаты: Таблица указателей координат

typedef struct {

Теги: #Программирование микроконтроллеров #stm32 #arm #C++ #SPI #embedded #cortex-m4 #lcd #microcontrollers #ssd1283A

Вместе с данным постом часто просматривают: