Введение Модель дисплея называется H016IT01. Этот дисплей интересен прежде всего тем, что он трансфлективный.
Это значит, что изображение на нем должно быть видно даже при ярком солнце.
А еще это чуть ли не единственная доступная модель с такой функцией на известном китайском сайте.
Статью опубликовал потому, что по контроллеру SSD1283A очень мало информации (как в российском, так и в западном сегментах сети), а мануала я нигде не видел.
В интернете можно найти даташит, но там нет информации по инициализации и работе с дисплеем, а полезно только описание регистров.
Хочу подчеркнуть, что этот материал, конечно, не является истиной в последней инстанции.
Я даю только свой опыт взаимодействия с устройством.
Основная цель статьи проста – помочь всем тем, кто решил, хочет или хочет работать с этим дисплеем, не более того.
Дисплей имеет 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, а значит, мы не можем ничего прочитать с дисплея, о чем нам говорит даташит.
Для начала настроим SPI, для этого будем тактировать SPI1 и GPIO, настроим ножки SDA и SCK как альтернативную функцию (я еще делал MISO, но это не обязательно).
Настраиваем режим работы как однонаправленный ведущий передатчик.
Затем напишем простую функцию для передачи байта по SPI: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; }
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);
После этого изображение на дисплее должно измениться с белого на серый цвет телевизионных помех.
Рисование прямоугольника
Контроллер 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);
Результат:
Рисуем буквы и цифры
Создадим список массивов с координатами символов.
Массивы координат букв и цифр
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
-
Насекомоядные
19 Oct, 24 -
Статика, Динамика И Дофамин
19 Oct, 24 -
Openvpn С Двухуровневой Иерархией Ca
19 Oct, 24