Подключение цветного LCD с сенсорным экраном к микроконтроллеру

    Добрый день, хабр. Сегодня я хотел бы поделиться некоторым опытом, касающимся подключения цветных 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%x\n",(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
    Метки:
    • +55
    • 46k
    • 9
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 9
    • +3
      а сколько стоит данный мк?
      • +2
        расширю вопрос. во сколько все это обошлось? и сколько времени заняла сборка?
        • +2
          Как я понимаю, в настоящее время плата (SK-MLPC2478) стоит 2300 рублей, но я покупал плату давно, и у меня более старая версия.
          Индикатор стоит 858 рублей (промэлектроника).
          Остальные детали копеечные.

          Собрать всё это недолго, долго разбираться с документацией и отлаживать программу.
          • 0
            Спасибо
            • 0
              пасибо
              • 0
                > Собрать всё это недолго, долго разбираться с документацией и отлаживать программу.

                Одна из основных причин, почему мы в разработках железа перешли на варисайтовские содимовские процессорные модули — весь гемор по отладке отпадает.
                • 0
                  Можно узнать, какие средства разработки и какую операционную систему вы используете?
                  • 0
                    Модули разные встраиваемые оси поддерживают.
                    Используем в зависимости от задач, предпочитаем Линукс.
                    • 0
                      про средства разработки: к примеру, в текущем проекте это вот такой отладочный кит VAR-DVK-AM35

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.