Добрый день. Сегодня я хотел бы поделиться некоторым опытом, касающимся подключения цветных LCD-индикаторов к микроконтроллеру. Эта тема уже поднималась на хабре (http://habrahabr.ru/post/139384/), поэтому данный пост может рассматриваться как дополнение к уже написанному моими уважаемыми коллегами.
В посте, на который приведена ссылка в начале, рассматриваются LCD-индикаторы, подключаемые с помощью интерфейса SPI. Такой способ подключения является простым, но не единственным. Множество индикаторов имеют «на борту» лишь упрощенный контроллер, не имеющий памяти для хранения изображения, и в силу этого, требующий непрерывного обновления изображения. Такие индикаторы применяются повсеместно в электронных гаджетах, и для их функционирования необходим специальный контроллер LCD, встроенный в микроконтроллер. Примером такого индикатора может служить MI0350CT-3 производства фирмы Multi-Inno Technology. Это достаточно типичный представитель цветных LCD, имеющий разрешение 320*240 и резистивный сенсор.
Итак, у нас появляется некоторая проблема. Пусть каждый пиксел представлен в памяти 32-битным значением (из них используется только 24, но данные выровнены по 4-байтным границам). Итого, под одну видеостраницу нам понадобится 320*240*4 = 307200 байт = 300 кбайт RAM. Не в каждом микроконтроллере есть такое количество памяти. Поэтому, как правило, для работы LCD нужна внешняя память, а значит, и контроллер внешней памяти на кристалле микроконтроллера.
Справедливости ради нужно заметить, что требования к объему памяти можно снизить, если использовать не 24-бита на пиксел, а меньшую глубину цвета, но, с другой стороны, мы можем захотеть разместить в памяти системы несколько видеостраниц, чтобы иметь возможность быстрого переключения между ними. Позже мы вернемся к этой теме.
Для примера рассмотрим подключение LCD к микроконтроллеру LPC2478. Это немного устаревший представитель микроконтроллеров фирмы NXP (на ядре ARM7, а не на современном Cortex M3), но для наших целей это несущественно. Этот микроконтроллер содержит всё необходимое: контроллер внешней памяти (с возможностью подключения как SRAM, так и SDRAM), и контроллер LCD с возможностью очень гибкой настройки.
Для экспериментов я использовал плату SK-MLPC2478 имеющую, помимо микроконтроллера LPC2478, микросхему статической памяти (CMOS SRAM) K6R4008V1D-TI10 объемом 512Kx8 bit. К плате подключен вышеупомянутый индикатор MI0350CT-3.
Есть еще один подводный камень: данный индикатор требует для питания подсветки источник с выходным током 20 мА при напряжении около 20 В. В качестве такого источника можно использовать, например, импульсный повышающий преобразователь, который питается входным напряжением +5В, и питает нагрузку стабилизированным током 20 мА. Если нагрузка не подключена, то преобразователь работает в режиме стабилизации выходного напряжения (U вых = 20 В). Схема преобразователя приведена на рис 1. Принцип действия и расчет повышающего преобразователя здесь рассматриваться не будет, интересующиеся этой темой могут обратиться к документации на микросхему MC34063A.
Рис. 1. Преобразователь напряжения.
Рассмотрим вкратце подключение и работу LCD такого типа. LCD использует 24 сигнала для информации о цвете, сигнал CLK для пиксельной синхронизации, сигналы HSINK и VSINC для импульсов горизонтальной и вертикальной синхронизации. Помимо этого, индикатор имеет интерфейс SPI для настройки вспомогательных регистров, сигнал #RESET и сигнал DEN (data enable). Интерфейс SPI в данном случае предназначен не для передачи изображения в контроллер дисплея, а для конфигурирования вспомогательных регистров. Эти регистры позволяют выбрать режим работы индикатора, регулировать яркость, контрастность и другие параметры. Однако регистры управления индикатора по умолчанию устанавливаются в нужное нам состояние, и, если мы не собираемся их изменять, сигналы SPI можно просто проигнорировать. Сигнал #RESET можно просто установить в неактивное (единичное) состояние. В дальнейшем он нам не понадобится.
Итак, основными сигналами, необходимыми для работы данного типа индикатора, являются: сигналы данных, несущие информацию о текущем цвете пиксела в формате RGB, сигнал тактирования CLK, сигналы вертикальной и горизонтальной синхронизации, а также сигнал разрешения данных. Рассмотрим временные диаграммы.
Рис. 2. Временная диаграмма передачи данных в LCD.
На рис. 2 показана самая «крупномасштабная» временная диаграмма передачи данных, изображающая передачу данных одной строки изображения. Передача происходит по фронту сигнала CLK, и сопровождается высоким уровнем сигнала разрешения данных DEN. Передача происходит в формате RGB, при этом биты Data0 — Data7 соответствуют синему цвету, Data8 — Data15 — зеленому, Data16-Data23 — красному.
Перед передачей данных строки и после передачи данных вставляются «пустые», не несущие информации импульсы CLK. Передача строки предваряется импульсом горизонтальной синхронизации HSYNC (LCD_LP).
Тактовая частота индикатора составляет 6,4 МГц (её можно уменьшить, но тогда становится заметным мерцание кадров).
Рис. 3. Временная диаграмма передачи кадра в LCD
Временная диаграмма передачи полного кадра в LCD показана на рис. 3. Передача кадра начинается с 13 «пустых» строк, далее следуют 240 строк с изображением, после которых опять идут «пустые» строки. Передача кадра начинается с импульса вертикальной синхронизации.
Также подключим к микроконтроллеру резистивный сенсор. Описание этого подключения будет приведено ниже.
Полную схему подключения индикатора к микроконтроллеру я приводить не буду, в силу ее очевидности, приведу лишь таблицу соответствия выводов индикатора и пинов контроллера.
Таблица 1. Соответствие сигналов LCD и выводов контроллера
Сигнал | SK-MLPC2478 (connector X6) | LCD pin | Порт микроконтроллера | Примечание |
GND | 1 | 53, 54 | земля | |
+5V | 2 | - | не используется | |
DATA6 | 3 | 18 | данные | |
+3.3 V | 4 | 41, 42 | питание | |
LCD CLK IN | 5 | - | не используется | |
DATA 7 | 6 | 19 | данные | |
DATA 19 | 7 | 31 | данные | |
DATA 18 | 8 | 30 | данные | |
DATA 22 | 9 | 34 | данные | |
DATA 23 | 10 | 35 | данные | |
DATA 20 | 11 | 32 | данные | |
DATA 21 | 12 | 33 | данные | |
DATA 14 | 13 | 26 | данные | |
DATA 15 | 14 | 27 | данные | |
DATA 12 | 15 | 24 | данные | |
DATA 13 | 16 | 25 | данные | |
DATA 11 | 17 | 23 | данные | |
DATA 10 | 18 | 22 | данные | |
DATA 1 | 19 | 13 | данные | |
DATA 0 | 20 | 12 | данные | |
DATA 9 | 21 | 21 | данные | |
DATA 8 | 22 | 20 | данные | |
DATA 17 | 23 | 29 | данные | |
DATA 16 | 24 | 28 | данные | |
LCD LE | 25 | - | не используется | |
LCD PWR | 26 | - | не используется | |
LCD FP | 27 | 37 | Вертикальная синхронизация | |
LCD CLK | 28 | 38 | Тактирование индикатора | |
LCD LP | 29 | 36 | Горизонтальная синхронизация | |
LCD ENAB | 30 | 52 | Разрешение данных | |
DATA 5 | 31 | 17 | данные | |
DATA 4 | 32 | 16 | данные | |
DATA 3 | 33 | 15 | данные | |
DATA 2 | 34 | 14 | данные | |
SPENA | 35 | 43 | P0.20 | интерфейс SPI, serial port data enable |
- | 36 | - | Не используется | |
#RESET | 37 | 6 | P0.19 | сброс |
- | 38 | - | Не используется | |
SPCK | 39 | 49 | P0.17 | интерфейс SPI, serial port clock |
SPDA | 40 | 50 | P0.18 | интерфейс SPI, serial data |
YU | Connector X7.15 | 8 | AD0/P0.23 | Резистивный сенсор |
XR | Connector X7.13 | 9 | AD1/P0.24 | Резистивный сенсор |
YD | Connector X7.10 | 10 | P0.26 | Резистивный сенсор |
XL | Connector X7.12 | 11 | P0.25 | Резистивный сенсор |
Собираем макет устройства (рис. 4 — 6):
Рис. 4. Платы макета устройства
Рис. 5. Макет устройства (частично собран)
Рис. 6. Макет устройства в сборе
Теперь, когда всё подключено, можно приступить к написанию кода. Первое, что нужно сделать, это сконфигурировать и протестировать внешнюю SRAM память:
// Init SDRAM controller
// Enable EMC clock
PCONP_bit.PCEMC = 1;
EMCSTATICWAITRD0 = 0x00000001;
EMCSTATICWAITPG0 = 0x00000000;
EMCSTATICWAITWR0 = 0x00000000;
EMCSTATICWAITTURN0 = 0x00000000;
EMCCONTROL = 1; // enable EMC
Тест SRAM:
char SRAM_Test(void)
{
// 32 bits access
for (Int32U i = 0; i < 0x80000; i+=sizeof(Int32U))
{
*(Int32U*)(((Int32U)SRAM_BASE_ADDR)+i) = i;
}
for (Int32U i = 0; i < 0x80000; i+=sizeof(Int32U))
{
if (*(Int32U*)(((Int32U)SRAM_BASE_ADDR)+i) != i)
{
printf("Verification error on address : 0x%xn",(Int32U)SRAM_BASE_ADDR+i);
return(FALSE);
}
}
return TRUE;
}
Следует понимать, что быстродействие внешней памяти является важным фактором. В самом деле, поток данных в нашем случае составляет Fclk * N, где Fclk — тактовая частота индикатора, N — количество байт в памяти, отведенных под 1 пиксел. Итого получаем 6.4*10^6 * 4 = 25.6 Мбайт/с. Для памяти с 8-битным интерфейсом это означает максимально допустимое время доступа 39 нс. Требования к быстродействию памяти можно снизить, если использовать память с 16 или 32-битным интерфейсом и/или уменьшить используемую глубину цвета.
Конфигурируем LCD в, выставляя длительности сигналов в соответствии с документацией индикатора:
// Init GLCD cotroller
PCONP_bit.PCLCD = 1; // enable LCD controller clock
CRSR_CTRL_bit.CrsrOn = 0; // Disable cursor
LCD_CTRL_bit.LcdEn = 0; // disable GLCD controller
LCD_CTRL_bit.LcdBpp= 5; //2bpp // 24 bpp
LCD_CTRL_bit.LcdTFT= 1; // TFT panel
LCD_CTRL_bit.LcdDual=0; // single panel
LCD_CTRL_bit.BGR = 0; // normal output
LCD_CTRL_bit.BEBO = 0; // little endian byte order
LCD_CTRL_bit.BEPO = 0; // little endian pix order
LCD_CTRL_bit.LcdPwr= 0; // disable power
// init pixel clock
LCD_CFG_bit.CLKDIV = SYS_GetFsclk() / (Int32U)C_GLCD_PIX_CLK;
LCD_POL_bit.BCD = 1; // bypass inrenal clk divider
LCD_POL_bit.CLKSEL = 0; // clock source for the LCD block is HCLK
LCD_POL_bit.IVS = 1; // LCDFP pin is active LOW and inactive HIGH
LCD_POL_bit.IHS = 1; // LCDLP pin is active LOW and inactive HIGH
// LCD_POL_bit.IPC = 1; // data is driven out into the LCD on the falling edge
LCD_POL_bit.IPC = 0; // data is driven out into the LCD on the rising edge
LCD_POL_bit.PCD_HI = 0; // //
LCD_POL_bit.PCD_LO = 0; //
LCD_POL_bit.IOE = 0; // active high
LCD_POL_bit.CPL = C_GLCD_H_SIZE-1;
// init Horizontal Timing
LCD_TIMH_bit.HBP = C_GLCD_H_BACK_PORCH - 1;
LCD_TIMH_bit.HFP = C_GLCD_H_FRONT_PORCH - 1;
LCD_TIMH_bit.HSW = C_GLCD_H_PULSE - 1;
LCD_TIMH_bit.PPL = (C_GLCD_H_SIZE/16) - 1;
// init Vertical Timing
LCD_TIMV_bit.VBP = C_GLCD_V_BACK_PORCH;
LCD_TIMV_bit.VFP = C_GLCD_V_FRONT_PORCH;
LCD_TIMV_bit.VSW = C_GLCD_V_PULSE;
LCD_TIMV_bit.LPP = C_GLCD_V_SIZE - 1;
// Frame Base Address doubleword aligned
LCD_UPBASE = (Int32U)GetVramAddr() & ~7UL;
Теперь записываем в ROM тестовую картинку и копируем ее в RAM:
Рис 7. Тестовая картинка
Картинка записывается в виде массива unsigned int [240][320], и копируется по базовому адресу внешней памяти (0x80000000).
Работа встроенного в микроконтроллер механизма поддержки LCD заключается в том, что данные считываются из RAM контроллером DMA, и подаются на контроллер LCD, где накладывается изображение курсора и данные подаются на вход LCD. Также контроллер LCD может обеспечивать поддержку палитры для режимов от 1 до 8 бит на пиксел. Палитра реализована в микроконтроллере как 256 16-битных регистров и, таким образом, обеспечивает отображение 8-битного цвета в 16-битный (5 бит/цвет + 1 бит общей интенсивности).
Курсор имеет отдельную палитру из двух 32-битных регистров, она обеспечивает отображение монохромного изображения курсора в 24-битное цветовое пространство. Курсор может быть размером 64x64 пиксела либо 32х32 пиксела, и содержит как само изображение курсора, так и маску инверсии изображения.
Теперь можно запустить тестовою программу и посмотреть на результат.
Рис. 8. Результат работы.
Подключение резистивного сенсора
Принцип действия резистивного сенсора широко известен (те, кому он неизвестен, могут ознакомиться с ним, например, здесь http://ru.wikipedia.org/wiki/Сенсорный экран ). Поэтому перейдем сразу к описанию подключения сенсора к микроконтроллеру. В данном устройстве используется сенсор с четырехпроводной схемой подключения. Схема подключения показана на рис. 9.
Рис. 9. Схема подключения резистивного сенсора к микроконтроллеру.
Идея, лежащая в основе такого подключения, состоит в следующем. Каждый вывод микроконтроллера может находиться в одном из следующих состояний: выход с логическим 0, выход с лог. 1, вход (т. е., фактически, неподключенный вывод), и вход, подтянутый к напряжению питания через резистор. Выводы YU и XL могут также служить входами АЦП.
Вся процедура определения координат нажатия может быть условно разделена на три фазы. В первой фазе выводы, к которым подключен сенсор, находятся в следующем состоянии: (рис. 10).
Рис. 10. Определение координат нажатия. Фаза 1.
При замыкании пленок сенсора на вход микроконтроллера поступает сигнал лог. 0, и программа переходит к фазе 2, определению x-координаты нажатия (рис. 11). Затем следует фаза 3, определение y-координаты. (рис. 12).
Рис. 11. Определение х-координаты нажатия.
Рис. 12. Определение y-координаты нажатия.
Разумеется, алгоритм вычисления координат нажатия должен включать в себя подавление дребезга «контактов» (плёнок сенсора).
При однократном измерении точность определения координат будет слишком низкая, шумы измерений приведут к тому, что курсор будет сильно колебаться в окрестностях точки касания. Поэтому весьма желательным является применение какого-либо алгоритма фильтрации, например медианного фильтра (http://ru.wikipedia.org/wiki/Медианный_фильтр).
После получения результата в кодах АЦП он пересчитывается в координаты экрана с помощью обычных линейных преобразований (ax + b), где коэффициенты a и b должны быть найдены путем калибровки сенсора и сохранены в энергонезависимой памяти контроллера. На рис. 13. приведен пример экран калибровки.
Рис. 13. Экран калибровки.
Рис. 14. Экран калибровки на LCD.
Возможна также реализация подключения сенсорного экрана с помощью внешнего контроллера, например, XPT2046.
Полный исходный код примера программы можно скачать отсюда: https://bitbucket.org/arktur04/lcd-demo (среда разработки IAR).
Ссылки на упомянутую в посте документацию:
1.Дисплей Multi-Inno MI0350CT-3 http://www.displayfuture.com/engineering/specs/TFT/MI0350CT-3.pdf
2.Импульсный преобразователь напряжения MC34063A http://www.fairchildsemi.com/ds/MC/MC33063A.pdf
3.Микроконтроллер LPC2478 http://www.ru.nxp.com/products/microcontrollers/arm7/lpc2400/LPC2478.html
4.Контроллер резистивного сенсора XPT2046 http://www.dzsc.com/uploadfile/company/785710/20111014212155973.pdf
Автор: 32bit_me