Рассмотрено подключение знакосинтезирующего ЖК-индикатора с параллельным интерфейсом MT-20S2M российского производителя МЭЛТ к микроконтроллеру STM32F405RGT6.
Такой выбор не случаен. Индикатор имеет символы большого размера при достаточном их количестве (20 знакомест в 2 строках). 51 доступный вход/выход микроконтроллера в корпусе LQFP-64 дает возможность не экономить и реализовать к индикатору параллельный 8-битный интерфейс. А еще это недорогой и быстрый чип с множеством блоков периферии. В общем, тот же STM32F407, только в меньшем корпусе.
О подключении.
Я вас обрадую – для параллельного интерфейса выбираем самые обычные выводы микроконтроллера, то есть те, которые по умолчанию не являются выводами для UART, I2C, SPI, ADC и других важных интерфейсов. Более того, не важно будут ли это близко расположенные выводы или разбросанные по всем углам корпуса. Алгоритм, который мы положим в основу работы с экраном, не чувствителен к скоростям передачи информации по сигнальным проводникам.
На Рисунке 1 изображен фрагмент схемы с подключением экрана к микроконтроллеру.
Рисунок 1. | Принципиальная схема подключения экрана MT-20S2M к микроконтроллеру STM32F405. |
Тут необходимо отметить одну важную особенность. Если питать экран напряжением 5 В, при чтении информации он выдает единицы, соответствующие его напряжению питания, то есть чуть ниже 5 В. Для микроконтроллера STM32, который питается от 3.3 В, это не опасно, но некоторое выводы не выдерживают 5 В на входе. Поэтому при использовании пятивольтового индикатора MT-20S2M-2YLG мы будем только записывать в него информацию (данные и команды). Уровень логики в 3.3 В он отлично воспринимает. При этом вывод R/W «подтягиваем к земле» (это установит режим «только запись»).
Самое интересное – программный алгоритм. Для начала открываем «Общее описание» работы c индикатором MT-20S2M и смотрим диаграммы сигналов и описание допустимых задержек. На Рисунке 2 представлена такая диаграмма с подписанными значениями. Порядок цифр – это десятки наносекунд и более. Больше можно, меньше нельзя.
Рисунок 2. | Временная диаграмма записи жидкокристаллического индикатора MT–20S2M. |
Основная идея заключается в том, что при формировании на выводах микроконтроллера нулей и единиц мы будем использовать его естественные задержки посредством стандартной библиотеки HAL (команда HAL_GPIO_WritePin). Когда мы используем эту команду, в недрах программного кода происходят различные проверки, пока не дойдет до установки нужной единицы или нуля в нужном регистре. На это уходит время. И чем больше частота работы микроконтроллера, тем короче это время. Нам надо измерить минимальное время для применяемого микроконтроллера.
Сразу оговорюсь, что данный подход справедлив для обычного функционального программирования (не для FreeRTOS и других ОС).
Для измерения подключим осциллограф к выводу микроконтроллера, который будет выводить на индикатор тактовый сигнал «Е».
В основном цикле while (1) пишем код:
while (1)
{
HAL_GPIO_WritePin(E_GPIO_Port, E_Pin, 1);
HAL_GPIO_WritePin(E_GPIO_Port, E_Pin, 0);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
На Рисунке 3 показана осциллограмма переключения для микроконтроллера STM32F405RGT6, работающего на максимальной для него частоте 168 МГц. На осциллограмме видно, что от подачи команды до момента самого переключения проходит около 230…250 нс. То есть расстояния между фронтами импульса соизмеримы с минимально допустимыми для работы индикатора.
Рисунок 3. | Осциллограмма циклического переключения сигнала в цикле while (1). |
А значит, мы можем формировать импульс синхронизации просто двумя последовательными командами HAL_GPIO_WritePin. Это сильно упрощает жизнь. Не нужно программировать работу с таймерами, прерываниями и прочими благами микроконтроллера. А если чип более быстрый, например 200 МГц, то ничто не мешает делать последовательно по две однотипные команды, как бы растягивая таким образом длительность сигнала в два раза.
Например:
HAL_GPIO_WritePin(E_GPIO_Port, E_Pin, 1);
HAL_GPIO_WritePin(E_GPIO_Port, E_Pin, 1);
HAL_GPIO_WritePin(E_GPIO_Port, E_Pin, 0);
HAL_GPIO_WritePin(E_GPIO_Port, E_Pin, 0);
Переключение всех других сигналов (DB0…B7 и A0) происходит вне действия импульса синхронизации и не нормируется по времени (мы сейчас говорим исключительно о режиме записи). Индикатор воспринимает информацию в момент, когда импульс синхронизации переходит в 0 (по заднему фронту сигнала).
Алгоритм работы следующий:
- Устанавливаем сигнал А0 (данные или команды).
- Устанавливаем сигналы DB0…DB7.
- Подаем синхроимпульс.
- Формируем задержку.
Для формирования задержки в 40 мкс перед передачей очередного байта (так прописано в инструкции к индикатору) можно использовать тот же принцип – однотипными командами HAL_GPIO_WritePin в цикле for.
На Рисунке 4 показан алгоритм работы с экраном, разложенный во времени. В нем последовательно передаются две команды (FFh и 00h) и один байт данных (00h). Такие команды и данные бессмысленны с точки зрения индикатора и приведены исключительно для наглядности. По оси абсцисс – время в микросекундах.
Рисунок 4. | Временная диаграмма алгоритма формирования сигнала для работы с жидкокристаллическим индикатором MT–20S2M. |
Код эквивалентный данному алгоритму следующий. В точке «А» устанавливаем сигнал А0 (данные = 1, команды = 0).
HAL_GPIO_WritePin(A0_GPIO_Port, A0_Pin, 0);
Далее последовательная установка бит от младшего DB0 к старшему DB7. Однако можно и в обратном порядке.
HAL_GPIO_WritePin(DB0_GPIO_Port, DB0_Pin, 1);
HAL_GPIO_WritePin(DB1_GPIO_Port, DB1_Pin, 1);
HAL_GPIO_WritePin(DB2_GPIO_Port, DB2_Pin, 1);
HAL_GPIO_WritePin(DB3_GPIO_Port, DB3_Pin, 1);
HAL_GPIO_WritePin(DB4_GPIO_Port, DB4_Pin, 1);
HAL_GPIO_WritePin(DB5_GPIO_Port, DB5_Pin, 1);
HAL_GPIO_WritePin(DB6_GPIO_Port, DB6_Pin, 1);
HAL_GPIO_WritePin(DB7_GPIO_Port, DB7_Pin, 1);
И наконец сигнал «E» – импульс синхронизации.
HAL_GPIO_WritePin(E_GPIO_Port, E_Pin, 1);
HAL_GPIO_WritePin(E_GPIO_Port, E_Pin, 0);
Завершаем все паузой в 40 мкс или более.
for(int i=0; i<175; i++) HAL_GPIO_WritePin(E_GPIO_Port, E_Pin, 0);
Расчет паузы следующий:
175 × 230 мкс = 40,250 мкс = 40.2 мс.
После паузы цикл повторяется — точка «В», «Д» и так далее.
Таким образом, на один байт данных либо команды уходит около 43 мкс процессорного времени. Из практики скажу, что для того чтобы с помощью этого алгоритма отобразить на экране индикатора все 40 символов, у микроконтроллера STM32F405RGT6, работающего на частоте 168 МГц, уходит 2.5 мс. А учитывая, что обновлять экран непрерывно нет смысла, и делать это надо в лучшем случае 4 раза в секунду (то есть с интервалом в 250 мс), то можно сказать, что затраты процессорного времени на работу с экраном составляют не более 1%.
Если же надо еще сократить время, расходуемое процессором, то можно обновлять только часть символов (например, те, которые изменились). Индикатор это позволяет, так как имеет свое ОЗУ. Однако про структуру команд и разные варианты работы с индикатором с точки зрения программирования я пока писать не буду. Это логично поместить в отдельную заметку (если будет интерес к этой теме – пишите в комментариях). Поскольку написать код – одно, а толково рассказать – другое; разные затраты процессорного времени .