Перейти к содержимому

#23. SPI: LCD Display

Рассмотрим использование интерфейса SPI STM32 для связи с модулем ЖК-дисплея. ЖК-дисплей представляет собой 1,8-дюймовый TFT-дисплей с 18-битными цветными пикселями 128×160, поддерживаемыми обычной (ST7735R) микросхемой драйвера.

В рассматриваемом нами ЖК-модуле используется контроллер ST7735R. Мы называем дисплей 7735 LCD. 7735 LCD — это адресно-пиксельный дисплей; каждый пиксель требует нескольких байтов для определения цвета — внутренняя память дисплея использует 18 бит / пиксель (по 6 бит каждый для красного, синего и зеленого). Мы будем использовать этот дисплей в 16-битном режиме с 5 битами, определяющими красный, 6 битами, определяющими зеленый, и 5 битами, определяющими синий. Контроллер дисплея автоматически экстраполирует от 16 до 18 бит, когда пиксели записываются на дисплей. Эта цветовая модель показана на рисунке ниже. Этот рисунок состоит из двух частей: расположение отдельных цветов в 16-битном слове и цвета, полученные из различных 16-битных констант.

Color Encoding

Чтобы понять необходимый интерфейс к ЖК-дисплею 7735, рассмотрим модель на рисунке. Необходимо рассмотреть три основных компонента — контроллер, ЖК-панель и дисплей RAM. Оперативная память дисплея содержит 18 бит информации о цвете для каждого из (128 x 160) пикселей на панели. Данные на дисплее ОЗУ непрерывно передаются на панель — мы настраиваем устройство для развертки снизу вверх. Модель для записи пиксельных (цветных) данных является несколько косвенной. Во-первых, используя отдельный интерфейс управления, настраивается прямоугольник рисования. Затем записываются пиксельные (цветные) данные. Местоположение, в которое записываются данные, определяется парой внутренних счетчиков адресов RAC (для счетчика адресов строк) и CAC (для счетчиков адресов столбцов). Каждая последующая запись в пиксель приводит к обновлению этих счетчиков адресов. 7735 может быть сконфигурирован так, чтобы «sweep» этот прямоугольник в любом левом / правом верхнем / нижнем порядке. Есть три внутренних контрольных бита (которые мы раскрываем в интерфейсе, описанном ниже). В дополнение к порядку развертки можно «обмениваться» адресами строк и столбцов и, таким образом, поддерживать «ландшафтный» режим. По умолчанию мы используем режим 0x6 (MY = 1, MX = 1, MV = 0). Они полностью описаны в руководстве по эксплуатации ST7735.

MY Row address order: 1 (bottom to top), 0 (top to bottom)

MX Column address order: 1 (right to left), 0 (left to right)

MV Column/row exchange: 1 (landscape mode), 0 (portrait mode)

Как упоминалось ранее, мы настраиваем панель на прием 16-битного цвета, поэтому каждая запись данных состоит из пары байтов, отправляемых интерфейсом SPI. Эти 16-разрядные данные о цвете автоматически переводятся в 18-разрядные с помощью внутренней таблицы поиска (LUT); фактические пиксели оперативной памяти дисплея — 18 бит.

7735 имеет отдельный управляющий сигнал, чтобы отличать «управляющую» информацию от «данных». Управляющая информация (будет обсуждаться позже) используется для настройки отображения и установки текущего прямоугольника рисования. Преимущество этого подхода состоит в том, что после настройки прямоугольника рисования данные могут отправляться в пакете без какой-либо дополнительной управляющей информации.

Model of 7735 Display

Простой «драйвер» для 7735 LCD требует только трех подпрограмм: одну для инициализации контроллера, одну для установки прямоугольника рисования и одну для записи цветовых данных в текущий прямоугольник. Обратите внимание, что этот интерфейс устанавливает направление рисования при установке прямоугольника (как описано выше, значение madctl 0x6 соответствует развертке прямоугольника рисования сверху вниз / влево-вправо. Значение madctl 0x02 соответствует восходящему вверх / развертка влево-вправо и полезна для отображения файлов изображений BMP, в которых данные хранятся в восходящем порядке. Можно добавить отдельную подпрограмму для управления яркостью подсветки — пока мы реализуем это как вкл / выкл.

#define MADCTLGRAPHICS 0x6
#define MADCTLBMP 0x2
#define ST7735_width 128
#define ST7735_height 160
void ST7735_setAddrWindow(uint16_t x0, uint16_t y0,
uint16_t x1, uint16_t y1, uint8_t madctl);
void ST7735_pushColor(uint16_t *color , int cnt);
void ST7735_init();
void ST7735_backLight(uint8_t on);

В следующем примере экран 7735 заполняется одним цветом фона.

void fillScreen(uint16_t color)
{
uint8_t x,y;
ST7735_setAddrWindow(0, 0, ST7735_width -1, ST7735_height -1,
,→MADCTLGRAPHICS);
for (x=0; x < ST7735_width; x++) {
for (y=0; y < ST7735_height; y++) {
ST7735_pushColor(&color ,1);
}
}
}

Хотя этот интерфейс можно сделать более сложным, его достаточно для отображения текста, изображений и графики.

Одна сложность, с которой нужно бороться — это бесконечность. Память STM32 имеет младший порядок, что означает, что 16-битное количество сохраняется в последовательных ячейках памяти с младшим байтом (биты 0-7) по младшему адресу памяти. Напротив, интерфейс 7735 предполагает, что данные принимаются в бигендовом порядке (сначала старший байт). Поэтому важно, чтобы данные о цвете передавались с использованием 16-битных передаточных функций; передача блоков данных цвета с 8-битной передачей приведет к замене старшего и младшего байтов!

Реализация базовой функциональности для ST7735 основывается на небольшом наборе внутренних примитивов для обеспечения доступа к интерфейсу SPI и управления контролем «выбора микросхемы». Это показано в листинге ниже. Существует две команды данных для 8-битной и 16-битной передачи и команда управления. Такие константы, как ST7735_CASET (набор адресов столбцов) и ST7735_RASET (набор адресов строк) определены в таблице данных ST7735.

static void LcdWrite(char dc, const char *data, int nbytes)
{
GPIO_WriteBit(LCD_PORT ,GPIO_PIN_DC , dc); // dc 1 = data, 0 =
,→control
GPIO_ResetBits(LCD_PORT ,GPIO_PIN_SCE);
spiReadWrite(SPILCD , 0, data, nbytes , LCDSPEED);
GPIO_SetBits(LCD_PORT ,GPIO_PIN_SCE);
}
static void LcdWrite16(char dc, const uint16_t *data, int cnt)
{
GPIO_WriteBit(LCD_PORT ,GPIO_PIN_DC , dc); // dc 1 = data, 0 =
,→control
GPIO_ResetBits(LCD_PORT ,GPIO_PIN_SCE);
spiReadWrite16(SPILCD , 0, data, cnt, LCDSPEED);
GPIO_SetBits(LCD_PORT ,GPIO_PIN_SCE);
}
static void ST7735_writeCmd(uint8_t c)
{
LcdWrite(LCD_C , &c, 1);
}

Инициализация ST7735 несколько сложна из-за необходимости инициализации внутренних регистров в дополнение к контактам и источникам синхронизации. Последовательность команд инициализации лучше всего скопировать из существующего программного обеспечения. Базовый процесс инициализации требует отправки ряда команд, чередующихся с задержками. Мы определили структуру данных для хранения этих шагов инициализации.

Font Fragment

Ниже в примере будет показано подключение дисплея к плате stm32-discovery, подключение к плате с МК STM32F103 проводится по аналогии…

Назначение контактов TFT

Драйвер ST7335 по ссылке на https://github.com/adafruit/Adafruit-ST7735-Library. Необходимо заполнить структуру данных кода инициализации.

Важным применением ЖК-дисплея является отображение сообщений из нашего кода. Первым требованием является отображение символов. Приведенный ссылочный код включает в себя простой растровый шрифт glcdfont.c, который определяет символы ASCII как карты размером 5×7 (каждый символ помещается в прямоугольник 6×10, оставляя пространство между строками (3 пикселя) и символами (1 пиксель). Фрагмент этого шрифта показано на рисунке. ASCII 0 является символом NULL и, следовательно, не отображается. Многие из символов ASCII с низким номером непечатаемы и, следовательно, либо оставлены пустыми, либо заполнены глифом по умолчанию.

Подключение TFT модуля

ST7735 Interface:

#define LOW 0
#define HIGH 1
#define LCD_C LOW
#define LCD_D HIGH
#define ST7735_CASET 0x2A
#define ST7735_RASET 0x2B
#define ST7735_MADCTL 0x36
#define ST7735_RAMWR 0x2C
#define ST7735_RAMRD 0x2E
#define ST7735_COLMOD 0x3A
#define MADVAL(x) (((x) << 5) | 8)
static uint8_t madctlcurrent = MADVAL(MADCTLGRAPHICS);
void ST7735_setAddrWindow(uint16_t x0, uint16_t y0,
uint16_t x1, uint16_t y1, uint8_t madctl)
{
madctl = MADVAL(madctl);
if (madctl != madctlcurrent){
ST7735_writeCmd(ST7735_MADCTL);
LcdWrite(LCD_D , &madctl , 1);
madctlcurrent = madctl;
}
ST7735_writeCmd(ST7735_CASET);
LcdWrite16(LCD_D , &x0, 1);
LcdWrite16(LCD_D , &x1, 1);
ST7735_writeCmd(ST7735_RASET);
LcdWrite16(LCD_D , &y0, 1);
LcdWrite16(LCD_D , &y1, 1);
ST7735_writeCmd(ST7735_RAMWR);
}
void ST7735_pushColor(uint16_t *color , int cnt)
{
LcdWrite16(LCD_D , color , cnt);
}
void ST7735_backLight(uint8_t on)
{
if (on)
GPIO_WriteBit(LCD_PORT_BKL ,GPIO_PIN_BKL , LOW);
else
GPIO_WriteBit(LCD_PORT_BKL ,GPIO_PIN_BKL , HIGH);
}

ST7735 Initialization Commands (Abbreviated):

struct ST7735_cmdBuf {
uint8_t command; // ST7735 command byte
uint8_t delay; // ms delay after
uint8_t len; // length of parameter data
uint8_t data[16]; // parameter data
};
static const struct ST7735_cmdBuf initializers[] = {
// SWRESET Software reset
{ 0x01, 150, 0, 0},
// SLPOUT Leave sleep mode
{ 0x11, 150, 0, 0},
// FRMCTR1 , FRMCTR2 Frame Rate configuration -- Normal mode, idle
// frame rate = fosc / (1 x 2 + 40) * (LINE + 2C + 2D)
{ 0xB1, 0, 3, { 0x01, 0x2C, 0x2D }},
{ 0xB2, 0, 3, { 0x01, 0x2C, 0x2D }},
// FRMCTR3 Frame Rate configureation -- partial mode
{ 0xB3, 0, 6, { 0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D }},
// INVCTR Display inversion (no inversion)
{ 0xB4, 0, 1, { 0x07 }},
/* ... */

ST7735 Initialization:

void ST7735_init()
{
GPIO_InitTypeDef GPIO_InitStructure;
const struct ST7735_cmdBuf *cmd;
// set up pins
/* ... */
// set cs, reset low
GPIO_WriteBit(LCD_PORT ,GPIO_PIN_SCE , HIGH);
GPIO_WriteBit(LCD_PORT ,GPIO_PIN_RST , HIGH);
Delay(10);
GPIO_WriteBit(LCD_PORT ,GPIO_PIN_RST , LOW);
Delay(10);
GPIO_WriteBit(LCD_PORT ,GPIO_PIN_RST , HIGH);
Delay(10);
// Send initialization commands to ST7735
for (cmd = initializers; cmd->command; cmd++)
{
LcdWrite(LCD_C , &(cmd->command), 1);
if (cmd->len)
LcdWrite(LCD_D , cmd->data, cmd->len);
if (cmd->delay)
Delay(cmd->delay);
}
}

ST7735 Initialization Commands (Remainder):

// PWCTR1 Power control -4.6V, Auto mode
{ 0xC0, 0, 3, { 0xA2, 0x02, 0x84}},
// PWCTR2 Power control VGH25 2.4C, VGSEL -10, VGH = 3 * AVDD
{ 0xC1, 0, 1, { 0xC5}},
// PWCTR3 Power control , opamp current smal, boost frequency
{ 0xC2, 0, 2, { 0x0A, 0x00 }},
// PWCTR4 Power control , BLK/2, opamp current small and medium low
{ 0xC3, 0, 2, { 0x8A, 0x2A}},
// PWRCTR5 , VMCTR1 Power control
{ 0xC4, 0, 2, { 0x8A, 0xEE}},
{ 0xC5, 0, 1, { 0x0E }},
// INVOFF Don't invert display
{ 0x20, 0, 0, 0},
// Memory access directions. row address/col address , bottom to
,→top refesh (10.1.27)
{ ST7735_MADCTL , 0, 1, {MADVAL(MADCTLGRAPHICS)}},
// Color mode 16 bit (10.1.30
{ ST7735_COLMOD , 0, 1, {0x05}},
// Column address set 0..127
{ ST7735_CASET , 0, 4, {0x00, 0x00, 0x00, 0x7F }},
// Row address set 0..159
{ ST7735_RASET , 0, 4, {0x00, 0x00, 0x00, 0x9F }},
// GMCTRP1 Gamma correction
{ 0xE0, 0, 16, {0x02, 0x1C, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2D,
0x29, 0x25, 0x2B, 0x39, 0x00, 0x01, 0x03, 0x10 }},
// GMCTRP2 Gamma Polarity corrction
{ 0xE1, 0, 16, {0x03, 0x1d, 0x07, 0x06, 0x2E, 0x2C, 0x29, 0x2D,
0x2E, 0x2E, 0x37, 0x3F, 0x00, 0x00, 0x02, 0x10 }},
// DISPON Display on
{ 0x29, 100, 0, 0},
// NORON Normal on
{ 0x13, 10, 0, 0},
// End
{ 0, 0, 0, 0}
};

Добавить комментарий