Texto em russo no visor com o controlador HD44780 e gerador de caracteres japoneses

Uma maneira não padrão de controlar a exibição no controlador HD44780 para renderizar fontes russas com qualquer própria tabela de códigos de exibição.


A própria idéia de tal controle nasceu da tarefa de russificação de um dispositivo com uma exibição personalizada baseada no controlador HD44780. Tendo considerado a opção de substituir caracteres russos por caracteres latinos semelhantes, decidi não mexer com isso, porque parece não comercializável e o dispositivo é comercial.

Ao contrário dos monitores OLED baseados no driver Winstar WS0010, os monitores nos drivers HD44780 ou seus equivalentes não possuem um modo operacional gráfico. Mas eles têm a chamada memória CGRAM, na qual você pode escrever até 8 caracteres em uma representação gráfica.

O algoritmo em si é bastante simples:
  1. Iniciamos o timer de renderização com uma frequência de interrupção aceitável para a carga do processador.
  2. Na interrupção do temporizador:
    • apagar (preencher com espaços) tudo o que estava na tela;
    • carregar as informações gráficas necessárias na primeira familiaridade da memória CGRAM do controlador;
    • exibimos esses caracteres no visor em um determinado deslocamento e saímos da interrupção.



O código se parece com isso:
// 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); } 



Nesse caso, é muito importante configurar corretamente o controle do monitor e usar tempos de troca mínimos, bem como, se possível, uma interface de 8 bits para o monitor (embora, de acordo com os testes, o de 4 bits não carregue muito o controlador). Também é necessário usar a leitura do sinal BUSY na linha D7 no visor para reduzir o tempo de espera por sua resposta.

A aparência de uma tela real pode ser vista no vídeo abaixo:



A linha superior é controlada pelo método de redesenho rápido e exibe informações do CGRAM. A linha contém caracteres desenhados graficamente da tabela de códigos CP866 com os códigos 0x80-0xFF, e os pseudográficos são substituídos por losangos de xadrez. Na linha inferior, estão os símbolos do gerador de caracteres interno da tela e ao lado de seus códigos.

As desvantagens do método:
Em uma taxa de atualização abaixo de 25Hz, a cintilação é notada na tela quando vista em ângulo e, ao olhar diretamente, não é visível na vertical.
Além disso, o contraste da imagem na tela diminui um pouco, mas isso quase não é perceptível.

Source: https://habr.com/ru/post/pt403831/


All Articles