Нестандартный способ управления дисплеем на контроллере HD44780 для отрисовки русских шрифтов при любой собственной кодовой таблице дисплея.
Сама идея такого управления родилась из задачи русификации устройства с кастомным дисплеем на основе контроллера HD44780. Рассмотрев вариант с подменой русских символов на похожие по начертанию латинские, решил с этим не связываться, ибо выглядит это нетоварно, а устройство коммерческое.
В отличие от OLED дисплеев на базе драйвера WS0010 от Winstar, дисплеи на драйверах HD44780 или их аналогах не имеют графического режима работы. Но имеют так называемую CGRAM память, в которую можно записать до 8 символов в графическом представлении.
Сам алгоритм работы получается довольно простым:
В данном случае очень важно правильно настроить управление дисплеем, и использовать близкие к минимальным тайминги обмена, а также по возможности 8-битный интерфейс к дисплею(хотя по тестам и 4хбитный не сильно нагружает контроллер). Также необходимо использовать чтение сигнала BUSY по линии D7 из дисплея, для сокращения времени ожидания его реакции.
Как это выглядит на реальном дисплее можно посмотреть на видео ниже:
В верхняя строка управляется методом быстрой перерисовки и в ней отображается информация из CGRAM. В строке проходят графически отрисованные символы из кодовой таблицы CP866 с кодами 0x80-0xFF, псевдографика заменена на шахматные ромбики. В нижней строке символы встроенного знакогенератора дисплея и рядом их коды.
Недоста��ки способа:
При частоте обновления ниже 25Гц, на экране при взгляде под углом заметно мерцание, при прямом взгляде вертикально его не видно.
Также чуть падает контрастность картинки с дисплея, но это почти не заметно.
Сама идея такого управления родилась из задачи русификации устройства с кастомным дисплеем на основе контроллера HD44780. Рассмотрев вариант с подменой русских символов на похожие по начертанию латинские, решил с этим не связываться, ибо выглядит это нетоварно, а устройство коммерческое.
В отличие от OLED дисплеев на базе драйвера WS0010 от Winstar, дисплеи на драйверах HD44780 или их аналогах не имеют графического режима работы. Но имеют так называемую CGRAM память, в которую можно записать до 8 символов в графическом представлении.
Сам алгоритм работы получается довольно простым:
- Запускаем таймер отрисовки на приемлемую по нагрузке на процессор частоту прерываний.
- В прерывании таймера:
- стираем(заполняем пробелами) всё что было на дисплее;
- загружаем требуемую графическую информацию в первые 8 знакомест CGRAM памяти контроллера;
- выводим эти символы на дисплей по заданному смещению и выходим из прерывания.
Код выглядит приблизительно вот так:
// Cham map 0x80 - 0xFF, with filler uint8_t cmap[] = { 0x04, 0x0a, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x00, // russian A 0x1f, 0x10, 0x10, 0x1e, 0x11, 0x11, 0x1e, 0x00, // russian Be 0x1e, 0x11, 0x11, 0x1e, 0x11, 0x11, 0x1e, 0x0, // russian Ve 0x1f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, // russian Ge 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x1f, 0x11, 0x00, // russian De 0x1f, 0x10, 0x10, 0x1e, 0x10, 0x10, 0x1f, 0x00, // russian E 0x15, 0x15, 0x15, 0x0e, 0x15, 0x15, 0x15, 0x00, // russian Zhe 0x0e, 0x11, 0x01, 0x06, 0x01, 0x11, 0x0e, 0x00, // russian Ze 0x11, 0x11, 0x11, 0x13, 0x15, 0x19, 0x11, 0x00, // russian I 0x0e, 0x00, 0x11, 0x13, 0x15, 0x19, 0x11, 0x00, // russian Ij 0x11, 0x12, 0x14, 0x18, 0x14, 0x12, 0x11, 0x00, // russian Ka 0x07, 0x09, 0x09, 0x09, 0x09, 0x09, 0x11, 0x00, // russian L 0x11, 0x11, 0x1b, 0x15, 0x15, 0x11, 0x11, 0x00, // russian M 0x11, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11, 0x00, // russian N 0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e, 0x00, // russian O 0x1f, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00, // russian Pe 0x1e, 0x11, 0x11, 0x11, 0x1e, 0x10, 0x10, 0x00, // russian Re 0x0e, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0e, 0x00, // russian Se 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, // russian Te 0x11, 0x11, 0x11, 0x0f, 0x01, 0x11, 0x0e, 0x00, // russian U 0x0e, 0x15, 0x15, 0x15, 0x0e, 0x04, 0x04, 0x00, // russian Fe 0x11, 0x0a, 0x04, 0x04, 0x0a, 0x0a, 0x11, 0x00, // russian He 0x12, 0x12, 0x12, 0x12, 0x12, 0x1f, 0x01, 0x00, // russian Ce 0x11, 0x11, 0x11, 0x0f, 0x01, 0x01, 0x01, 0x00, // russian Che 0x11, 0x15, 0x15, 0x15, 0x15, 0x15, 0x1f, 0x00, // russian She 0x11, 0x15, 0x15, 0x15, 0x15, 0x1f, 0x01, 0x00, // russian Sche 0x18, 0x08, 0x08, 0x0e, 0x09, 0x09, 0x0e, 0x00, // russian Tverduy znak 0x11, 0x11, 0x19, 0x15, 0x15, 0x15, 0x19, 0x00, // russian bI 0x10, 0x10, 0x1e, 0x11, 0x11, 0x11, 0x1e, 0x00, // russian Myagkiy znak 0x1e, 0x01, 0x01, 0x0f, 0x01, 0x01, 0x1e, 0x00, // russian Ee 0x17, 0x15, 0x15, 0x1d, 0x15, 0x15, 0x17, 0x00, // russian You 0x0f, 0x11, 0x11, 0x0f, 0x05, 0x09, 0x11, 0x00, // russian Ya // 0x00, 0x00, 0x0e, 0x01, 0x0f, 0x11, 0x0f, 0x00, // russian small A 0x01, 0x0e, 0x10, 0x1e, 0x11, 0x11, 0x0e, 0x00, 0x00, 0x00, 0x1e, 0x11, 0x1e, 0x11, 0x1e, 0x00, 0x00, 0x00, 0x1e, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x0e, 0x0a, 0x0a, 0x1f, 0x11, 0x00, 0x00, 0x00, 0x0e, 0x11, 0x1e, 0x10, 0x0e, 0x00, 0x00, 0x00, 0x15, 0x15, 0x0e, 0x15, 0x15, 0x00, 0x00, 0x00, 0x1e, 0x01, 0x0e, 0x01, 0x1e, 0x00, 0x00, 0x00, 0x11, 0x13, 0x15, 0x19, 0x11, 0x00, 0x0e, 0x00, 0x11, 0x13, 0x15, 0x19, 0x11, 0x00, 0x00, 0x00, 0x09, 0x0a, 0x0c, 0x0a, 0x09, 0x00, 0x00, 0x00, 0x07, 0x09, 0x09, 0x09, 0x11, 0x00, 0x00, 0x00, 0x11, 0x1b, 0x15, 0x11, 0x11, 0x00, 0x00, 0x00, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x00, 0x00, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e, 0x00, 0x00, 0x00, 0x1f, 0x11, 0x11, 0x11, 0x11, 0x00, // russian small Pe // 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler // 0x00, 0x00, 0x1e, 0x11, 0x11, 0x1e, 0x10, 0x00, // russian small Re 0x00, 0x00, 0x0e, 0x11, 0x10, 0x11, 0x0e, 0x00, 0x00, 0x00, 0x1f, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x11, 0x11, 0x0f, 0x01, 0x0e, 0x00, 0x00, 0x00, 0x1f, 0x15, 0x15, 0x1f, 0x04, 0x00, 0x00, 0x00, 0x11, 0x0a, 0x04, 0x0a, 0x11, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x1f, 0x01, 0x00, 0x00, 0x00, 0x11, 0x11, 0x0f, 0x01, 0x01, 0x00, 0x00, 0x00, 0x15, 0x15, 0x15, 0x15, 0x1f, 0x00, 0x00, 0x00, 0x15, 0x15, 0x15, 0x1f, 0x01, 0x00, 0x00, 0x00, 0x18, 0x0e, 0x09, 0x09, 0x0e, 0x00, 0x00, 0x00, 0x11, 0x11, 0x1d, 0x15, 0x1d, 0x00, 0x00, 0x00, 0x10, 0x1e, 0x11, 0x11, 0x1e, 0x00, 0x00, 0x00, 0x1e, 0x01, 0x0f, 0x01, 0x1e, 0x00, 0x00, 0x00, 0x17, 0x15, 0x1d, 0x15, 0x17, 0x00, 0x00, 0x00, 0x0d, 0x13, 0x0f, 0x05, 0x09, 0x00, // russian small Ya 0x0a, 0x00, 0x1f, 0x10, 0x1e, 0x10, 0x1f, 0x00, // russian Yo 0x0a, 0x00, 0x0e, 0x11, 0x1e, 0x10, 0x0f, 0x00, // russian small Yo // 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00, // filler 0x00, 0x04, 0x0a, 0x15, 0x0a, 0x04, 0x00, 0x00 // filler }; static uint8_t lcd_draw_part = 0; static uint8_t lcd_ch; static uint8_t lcd_draw_string[16] = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87 }; void TIM4_IRQHandler() { if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM4, TIM_IT_Update); // стираем то что светили ранее, part = 0 - 0..7, part = 1 - 8..15 LCD_gotoxy(0, (lcd_draw_part ? 0 : 8)); LCD_write_data(0x20); LCD_write_data(0x20); LCD_write_data(0x20); LCD_write_data(0x20); LCD_write_data(0x20); LCD_write_data(0x20); LCD_write_data(0x20); LCD_write_data(0x20); // загружаем в CGRAM до 8 символов for(uint8_t i = 0; i < 8; i++) { lcd_ch = lcd_draw_string[i + (lcd_draw_part ? 8 : 0)]; if(lcd_ch > 127) LCD_load_symbol(i, cmap + 8 * (lcd_ch & 0x7f) ); } // рисуем LCD_gotoxy(0, (lcd_draw_part ? 8 : 0)); for(uint8_t i = 0; i < 8; i++) { lcd_ch = lcd_draw_string[i + (lcd_draw_part ? 8 : 0)]; if(lcd_ch < 128) LCD_write_data(lcd_ch); else LCD_write_data(i); } lcd_draw_part ^= 0x01; } } void LCD_start_fps() { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); TIM_TimeBaseInitTypeDef timerInitStructure; timerInitStructure.TIM_Prescaler = 48-1; timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up; timerInitStructure.TIM_Period = 20000; // 20ms/isr timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; timerInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM4, &timerInitStructure); TIM_Cmd(TIM4, ENABLE); TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); NVIC_InitTypeDef nvicStructure; nvicStructure.NVIC_IRQChannel = TIM4_IRQn; nvicStructure.NVIC_IRQChannelPreemptionPriority = 0; nvicStructure.NVIC_IRQChannelSubPriority = 2; nvicStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvicStructure); }
В данном случае очень важно правильно настроить управление дисплеем, и использовать близкие к минимальным тайминги обмена, а также по возможности 8-битный интерфейс к дисплею(хотя по тестам и 4хбитный не сильно нагружает контроллер). Также необходимо использовать чтение сигнала BUSY по линии D7 из дисплея, для сокращения времени ожидания его реакции.
Как это выглядит на реальном дисплее можно посмотреть на видео ниже:
В верхняя строка управляется методом быстрой перерисовки и в ней отображается информация из CGRAM. В строке проходят графически отрисованные символы из кодовой таблицы CP866 с кодами 0x80-0xFF, псевдографика заменена на шахматные ромбики. В нижней строке символы встроенного знакогенератора дисплея и рядом их коды.
Недоста��ки способа:
При частоте обновления ниже 25Гц, на экране при взгляде под углом заметно мерцание, при прямом взгляде вертикально его не видно.
Также чуть падает контрастность картинки с дисплея, но это почти не заметно.
