Как отобразить изображение на ЖК-дисплее с помощью микроконтроллера EFM8
Узнайте, как использовать функциональность USB EFM8 для переноса изображения с ПК на ЖК-дисплей.
Рекомендуемый уровень
промежуточный
Предыдущие статьи в этой серии
- Серия EFM8 от Silicon Laboratories: мощная новая встроенная платформа разработки
- Управление ЖК-дисплеем через SPI: введение в разработку проекта с помощью микроконтроллера EFM8
- Отображение символов на ЖК-дисплее с помощью микроконтроллера EFM8
- Связь с микроконтроллером EFM8 через USB
Требуемое оборудование / программное обеспечение
- Плата оценки SLFK2000A EFM8
- Интегрированная среда разработки Simplicity Studio
- Scilab
Обзор проекта
В предыдущих проектах мы изучили реализацию функциональных возможностей SPI EFM8, связь с ЖК-модулем, форматирование и печать 10-на-8-пиксельных символов и установление соединения USB между EFM8 и SciLab с использованием библиотеки VCPXpress. Текущий проект объединяет эти возможности для эффективного и удобного отображения изображения на 128 на 128 пикселей на ЖК-дисплее. Цель состоит в том, чтобы начать с любого стандартного файла изображения в формате grayscale.bmp и использовать Scilab для его обработки, а затем передать его через USB в микроконтроллер EFM8, чтобы мы могли отображать его на 128-дюймовом ЖК-дисплее. Этот проект обрабатывает только одно изображение, но представленные здесь методы могут быть легко адаптированы к отображению простой анимации, состоящей из серии похожих изображений.
Процесс начинается с создания изображения с помощью Paint. NET или другого приложения для редактирования изображений. Это изображение загружается в Scilab, обрабатывается в формате, совместимом с ЖК-дисплеем, преобразуется в матрицу пиксельных данных и передается через 64-байтные USB-пакеты в EFM8. Обновленный конечный автомат SPI затем используется для передачи данных пикселя по четыре строки за один раз на ЖК-модуль.
Порт ввода-вывода
Конфигурация ввода-вывода портов идентична той, что мы использовали в предыдущей статье.

Сигналы SPI отображаются на соответствующие штырьки портов, за исключением сигнала выбора микросхемы, который мы приводим вручную через P0.1. Нам не нужно напрямую настраивать контакты портов для линий данных USB; вся инициализация периферийных устройств USB осуществляется через библиотеку VCPXpress.
Периферийные устройства и прерывания
Настройка периферийных устройств и прерываний идентична используемой в предыдущей статье: SPI настроен для связи с ЖК-модулем, а Timer4 используется для коротких задержек. Мы не используем Timer2 в этом проекте, потому что нам не нужна частота кадров. Скорее, ЖК-дисплей обновляется последовательно, поскольку пакеты данных пикселей принимаются с ПК.
Прошивка
Конфигурация VCP для этого проекта идентична той, что мы использовали в предыдущем проекте. Однако есть некоторые дополнительные функции USB: ранее EFM8 только получал данные от Scilab, тогда как EFM8 также передает данные.
void USBTxByte(unsigned char BytetoSend) { Block_Write(&BytetoSend, 1, &USBBytesTransmitted); }
Как видно из названия функции Block_Write () выше, библиотека VCPXpress может передавать массив байтов одним вызовом функции. Однако в этом проекте USB-передачи от EFM8 используются только для управления потоком: EFM8 отправляет один байт, чтобы сообщить Scilab, что пришло время отправить больше данных. Таким образом, функция USBTxByte () - это просто удобный способ использования Block_Write () для передачи одного байта.
Полученные USB-пакеты обрабатываются следующим кодом:
if (API_InterruptCode & RX_COMPLETE) // USB read complete { if(USBBytesReceived == 1 & USBRxPacket(0) == NEW_IMAGE_FLAG) { CLEAR_LCD = TRUE; NextLinetoWrite = 0; //return the new image flag byte to the PC for flow control USBTxByte(NEW_IMAGE_FLAG); //continue with the next USB read procedure Block_Read(USBRxPacket, USB_PACKET_SIZE, &USBBytesReceived); } else if(USBBytesReceived == USB_PACKET_SIZE) { /*this flag tells the while loop in ImagetoLCD_main.c to process a received USB pixel data packet*/ USB_PACKET_RECEIVED = TRUE; //continue with the next USB read procedure Block_Read(USBRxPacket, USB_PACKET_SIZE, &USBBytesReceived); } }
Когда скрипт Scilab завершил преобразование файла изображения в пиксельные данные ЖК-дисплея, он отправляет однобайтовый пакет со значением, определенным в ImagetoLCD_Defs.h, как NEW_IMAGE_FLAG. Таким образом, если полученная длина пакета равна единице, а один полученный байт имеет значение NEW_IMAGE_FLAG, микроконтроллер знает, что новое изображение уже в пути. Он очищает ЖК-дисплей, передает NEW_IMAGE_FLAG на ПК и загружает нуль в NextLinetoWrite, который является переменной, которая содержит первый адрес строки, который будет обновляться, когда микроконтроллер получает следующий пакет данных пикселей. Если полученная длина пакета составляет 64 байта вместо одного байта, пакет передает фактические данные пикселей. В этом случае мы просто устанавливаем флаг USB_PACKET_RECEIVED в значение true; байт управления потоком будет передан после завершения обновления ЖК-дисплея.
Когда бесконечный цикл в ImagetoLCD_main.c понимает, что для USB_PACKET_RECEIVED установлено значение true, он вызывает ProcessUSBRxPacket ():
void ProcessUSBRxPacket() { unsigned char n = 0, row, column; //copy the received pixel data to the LCD display data array for(row = 0; row < LINES_PER_PACKET; row+) { for(column = 0; column < NUM_LINE_DATA_BYTES; column+) { LCDDisplayData(row)(column) = USBRxPacket(n); n+; } } //wait until the SPI state variable indicates that the bus is available for a new transfer while(LCDTxState != IDLE); UpdateLCDLines(); }
Здесь мы переносим данные пикселя в соответствующий двумерный массив. В этом проекте LCDDisplayData () () представляет собой 4 строки по 16 столбцов: нам по-прежнему нужно 16 столбцов для хранения 128 бит горизонтальных данных, но нам нужно всего 4 строки, потому что данные пикселей передаются с ПК в 64 байтовых пакетах, и 64 байта, деленные на 16 байт на строку, равны 4 строкам. После обновления массива программа ждет, пока коммуникационный интерфейс ЖКД не будет работать, а затем вызовет UpdateLCDLines ().
Этот проект требует некоторых изменений в конечной машине, которая регулирует передачу SPI на ЖК-дисплей. Раньше у нас была функция UpdateAllLCDLines (), которая (как вы могли догадаться из названия) инициирует процесс, который обновляет все ЖК-линии в одной передаче SPI. Но теперь мы обновляем только четыре строки во время одной передачи SPI, а две дополнительные задачи выполняются в конце процедуры:

Скачать код
Scilab
Сценарий Scilab начинается с раздела обработки изображений:

Входное изображение должно быть черно-белым, 128-бит-128-пиксельным.bmp-файлом. Функция SegmentByThreshold () преобразует изображение из оттенков серого в черно-белое, так как на нашем ЖК-дисплее пикселы включены или выключены - серый не разрешен. Ряд операций bitet () преобразует данные изображения в пиксельные данные, которые могут быть отправлены в EFM8 и переданы непосредственно на ЖК-дисплей. Обратите внимание, что сложные вычислительные приложения, такие как Scilab, не точно оптимизированы для рода неудобных побитовых операций, которые мы здесь используем. Другими словами, блок double for-loop в вышеуказанном коде занимает много времени (например, около 23 секунд с процессором 2, 5 ГГц, работающим под управлением Windows 8.1). Таким образом, если вы хотите адаптировать этот код для отображения последовательности анимации, вам нужно будет преобразовать все изображения в формат пикселей на ЖК-дисплее, прежде чем вы начнете отправлять данные в EFM8.
Другой основной раздел сценария Scilab - это цикл for, который отправляет данные пикселя в EFM8 через соединение VCP:

Четыре строки пиксельных данных преобразуются в одномерный массив и передаются как один 64-байтовый пакет, используя функцию slSendArray (). Затем скрипт считывает один байт подтверждения из EFM8 перед отправкой следующих четырех строк данных пикселя. Важное примечание: вызовы slReadByte () в этом скрипте имеют «1» для второго параметра, т. Е. Response = slReadByte (EFM8Port, 1). Этот «1» указывает на то, что функция будет блокироваться, а это означает, что Scilab ничего не сделает, пока не появится хотя бы один байт. Преимущество здесь заключается в том, что сценарий работает как можно быстрее, поскольку выполнение будет продолжаться, как только EFM8 отправит байт подтверждения. Проблема, однако, в том, что если что-то пойдет не так, и байт никогда не наступит, Scilab будет в коме, пока вы не закроете программу и не откроете ее снова. Следовательно, лучший способ сделать это во время фазы отладки - использовать функцию sleep (), чтобы дать время EFM8 ответить, а затем прочитать байт без блокировки, то есть slReadByte (EFM8Port, 0).
Сценарий Scilab также вызывает tic () и toc () для измерения и отображения времени, необходимого для передачи и отображения одного изображения. С той же машиной Windows 2, 5 ГГц, о которой говорилось выше, процесс занимает всего около 50 мс, что означает, что эта система должна иметь возможность комфортно поддерживать частоту кадров анимации 10 изображений в секунду.
Скачать код
Попробуйте этот проект сами! Получить спецификацию.