Exibições do modo gráfico e de texto Winstar

Os displays gráficos, incluindo os do tipo OLED, mais representados em nosso mercado pela Winstar, têm uma demanda muito menor em relação às minúsculas e as publicações sobre seu uso também são muito menores. Enquanto isso, são os displays gráficos OLED que, devido à falta de ligação às tabelas de fontes de um padrão predefinido, fornecem a melhor maneira de obter dispositivos de exibição ergonômicos para uma ampla variedade de necessidades. Além disso, o modo gráfico no controlador WS0010 é mais fácil de iniciar e funciona mais estável que o modo texto.

Antes de prosseguirmos com a consideração das exibições gráficas reais, consideraremos o problema permanente com os problemas de ativação do modo de texto do controlador WS0010, que recebeu uma solução inesperada e óbvia (ah, onde estavam meus olhos!).

Resolvendo problemas do modo de texto WS0010


É sabido que os displays da linha Winstar apresentam problemas de estabilidade durante a inicialização. A propósito, descobriu-se que isso não era exclusivo dos “malditos chineses”: as amostras do Newhaven Display 16x2, que obtive com grande dificuldade, localizadas no outro lado do globo, são externamente uma cópia completa do Winstar, exceto a localização de algumas inscrições e o nome da empresa na mancha ( a mesma forma e fonte):

imagem

Contendo, como está escrito nas fichas técnicas, um determinado controlador "comparável ao LCD", esses monitores se comportam completamente idênticos aos chineses e têm as mesmas desvantagens. Obviamente, você não deve perder tempo verificando outras empresas, como a Midas: a julgar por esta publicação , não poderia prescindir da cooperação internacional. A economia globalizada rulez!

As dificuldades do modo de texto são expressas no fato de que, quando você inicia (por exemplo, quando você reinicia ou reinicia manualmente o programa do controlador de controle), o lixo pode aparecer nos visores e as linhas 0 e 1 mudam de lugar aleatoriamente. Os experimentos mostraram que ele não depende do método de inclusão (8 ou 4 bits). Esse problema é especialmente grave quando reinicializações periódicas de software são necessárias, por exemplo, pelo Watchdog-timer.

Parte do problema é uma atitude clara em relação à energia (de uma fonte separada e de maneira alguma do USB Arduino) e uma reinicialização separada, desligando e ligando a tela depois de iniciar o programa de controle (consulte a publicação anterior do autor). Como se viu, o autor dessas linhas não foi o único que propôs uma solução semelhante para o problema: o autor do complemento LuquidCrystal chamado WinstarOLED também incluiu um pw_pin especial nele, com o qual a potência da tela é distorcida no momento em que o programa é iniciado.

Mas isso é tudo, é claro, iniciativa e meias medidas. Alguém SeregaB se deparou com um caminho radical (veja sua publicação em easyelectronics.ru - obrigado a Tomasina pela dica). De fato, ele colocou uma tarefa completamente diferente: aprender a trabalhar apenas com o modo gráfico, em vez de texto. Tentando alternar entre os modos, ele rapidamente descobriu que " mudar para o modo gráfico era normal e do gráfico para o" texto "- muito desajeitado ". Então ele lembrou que “ uma vez, há muito tempo, quando DShs ainda eram impressos em papel, em alguns DShs no HD44780 eu li que os modos de alternância só deveriam ser feitos quando a tela estava desligada ”. E funcionou.

A partir da publicação citada, simplesmente reproduzirei dois procedimentos de comutação aqui, adaptando-os levemente para uso com LuquidCrystal (a instância de classe é chamada OLED1 aqui).

Alterne para o modo gráfico:
OLED1.command(0x08);//  OLED1.command(0x1F);//   OLED1.command(0x01);//    (..  clear()) OLED1.command(0x08|0x04);//  

Alterne para o modo de texto:
  OLED1.command(0x08);//  OLED1.command(0x17);//    OLED1.command(0x01);//    (..  clear()) OLED1.command(0x04 | 0x08);//  

Como veremos mais adiante, o primeiro procedimento não é realmente necessário: o WS0010 alterna para o modo gráfico a partir de um meio chute, basta enviar o comando 0x1F para ele. Mas a segunda sequência de comandos foi bem o caso. Para a amostra, ela foi incluída diretamente no esboço usando o LuquidCrystal neste formulário:
 void reset_textmode() //     { OLED1.command(0x08);//  OLED1.command(0x17);//    OLED1.command(0x01);//    OLED1.command(0x04 | 0x08);//  } 

Em seguida, essa função foi chamada na configuração logo após o início da biblioteca:

  . . . . . OLED1.begin(16,2); //16  2  reset_textmode(); //  clear() . . . . . 

Se você inserir algum atraso (500) antes disso, a demonstração será muito óbvia: depois de pressionar o botão de reset da placa Arduino na tela, como de costume, o lixo aparece, mas apenas por um momento: depois que a função é acionada, a tela é limpa e todas as linhas voltam ao lugar .

A função funciona assim, mas por conveniência, substituí o conteúdo da função LiquidCrystalRus :: clear () no arquivo da biblioteca LiquidCrystalRus_OLED.cpp atualizada que foi discutida anteriormente com esta sequência de comandos (lembre-se de que você pode baixá-lo no site do autor). Não há espera para que o comando seja executado na biblioteca, portanto, para confiabilidade, após cada comando, existem atrasos de 100 µs inseridos no estilo geral da biblioteca. Nos esboços que usam esta variante do LiquidCrystalRus_OLED, no início da instalação, é necessário chamar a função clear () e, ao mesmo tempo, limpar a tela.
Nota
Há um problema com a limpeza da tela: na folha de dados da tabela de comandos, observe que o comando 0x01 pode durar até 6,2 ms “quando fsp ou fosc = 250KHz”. Que tipo de “fsp ou fosc” é realmente em controladores específicos, eles eram muito preguiçosos para escrever, mas em qualquer caso, mesmo que seja megahertz, o atraso para esse comando deve ser significativo (e o autor do LiquidCrystal menciona isso). No entanto, na prática, verifica-se que a equipe de limpeza trabalha por conta própria se não houver atraso algum. Então eu não entendi, mas agi de acordo com a conhecida regra de programação: "funciona - não toque!".

Agora vamos finalmente lidar com o modo gráfico.

O modo gráfico em texto exibe WEH001602


Para começar, tentei mudar a exibição de texto que tinha WEH001602BG para o modo gráfico. Observe que as telas gráficas de 100x16 e texto (configuração 20x2, 16x2 possuem menos pontos horizontais) têm matrizes idênticas, apenas as exibições de texto são separadas por intervalos de familiaridade. Isso limita severamente o uso do modo de gráficos nas exibições de texto e ainda mais no modo de texto nos gráficos. Mas para testar como funciona, você pode usar qualquer um deles.

O monitor, juntamente com o DS1307, foi conectado ao Arduino Nano, de acordo com o seguinte esquema:
imagem

De acordo com o mesmo esquema, conectaremos displays gráficos no futuro. A cor cinza no diagrama mostra a conexão do segundo monitor, se necessário.

Para alternar para o modo gráfico, você pode usar o procedimento aprimorado da seção anterior, mas uma função simples de um único comando funciona:
 . . . . . #define LCD_SETGRAPHICMODE 0x1f LiquidCrystal lcd(9, 4, 8, 7, 6, 5); void setGraphicMode(){ lcd.command(LCD_SETGRAPHICMODE); } . . . . . 

Não precisamos de nenhuma tabela russa aqui; portanto, é usado o LiquidCrystal padrão (não endireitado), que funciona perfeitamente no modo gráfico. Para não mexer na depuração de todas as opções da biblioteca, no caso em que as exibições gráfica e de texto são incluídas em paralelo, então para cada uma delas eu uso minha própria biblioteca (para texto atualizado Rus_OLED, para normal gráfica). Nesse caso, a conexão ainda pode ser feita nas mesmas pernas do controlador, com exceção dos pinos de saída E, de acordo com o diagrama acima.

Além disso, usei parcialmente as conquistas do autor da biblioteca WinstarOLED mencionada (por si só, este complemento para LuquidCrystal é, na minha opinião, incompleto e não é prático usá-lo como). Ele introduziu uma função conveniente para definir o cursor gráfico (o erro original referente ao valor máximo de x foi corrigido aqui):
 void setGraphicCursor( uint8_t x, uint8_t y ){ if( 0 <= x && x <= 99 ){ lcd.command(LCD_SETDDRAMADDR | x); } if( 0 <= y && y <= 1 ){ lcd.command(LCD_SETCGRAMADDR | y); } } 

A constante LCD_SETDDRAMADDR é definida na biblioteca LiquidCrystal. Uma exibição de 100x16, como uma exibição de texto, é dividida em duas linhas, 0 e 1, porque y pode receber apenas dois valores aqui. E a coordenada horizontal x varia de 0 a 99. Um byte é enviado com o comando lcd.write (), cujos bits individuais determinam as posições luminosas da linha vertical com um comprimento de 8 pontos. A posição mais à esquerda na linha superior tem coordenadas 0,0, a mais à direita na parte inferior - 99,1. Além disso, o ponto mais baixo corresponderá ao bit menos significativo e o ponto mais baixo - o mais alto.

Para a conveniência de codificar imagens, desenhei uma placa na qual você pode criar rapidamente o código desejado manualmente. Para tabelas de fontes completas, é claro, é aconselhável usar editores especiais (dos quais existem pelo menos um milhão de graus diferentes de atividade amadora), mas 10 dígitos com a ordem de bits desejada são mais rápidos para processar manualmente, especialmente porque as fontes criadas automaticamente ainda precisam ser finalizadas manualmente. De acordo com o acima, um glifo, por exemplo, a fonte número 2 10x16 será codificado da seguinte maneira:

imagem

Tudo isso é escrito em uma matriz bidimensional do formulário:
 const byte Data2[2][10]={{0x06,0x07,0x03,0x03,0x03,0x83,0xc3,0x63,0x3f,0x1e}, {0xf0,0xf8,0xcc,0xc6,0xc3,0xc1,0xc0,0xc0,0xc0,0xc0}}; 

Para cada dígito de 0 a 9, é criada uma matriz separada: Dados0, Dados1, Dados2 e assim por diante. Para relógios, além de números, você também precisará de um ponto duplo. Pode ser reduzido:
 const byte DataDP[2][2]={{0x70,0x70}, {0x1c,0x1c}};//  

Como o controlador não sabe "piscar" no modo gráfico, é necessário piscar programaticamente os dois pontos. Você pode extinguir um ponto duplo simplesmente exibindo zeros nas posições correspondentes, mas por uniformidade, criei uma matriz separada
 const byte DataDPclr[2][2]={{0x00,0x00}, {0x00,0x00}};// .  

Para exibir cada dígito e separadamente para um ponto duplo, uma função separada é gravada:
 void draw2 (byte x/* */) // “2” { for (byte i = x; i<x+10; i++){ setGraphicCursor(i, 0); lcd.write(Data2[0][ix]); setGraphicCursor(i, 1); lcd.write(Data2[1][ix]);} } 

Todas as funções são iguais, mas usam matrizes diferentes e, para um ponto duplo, outros limites do loop também são usados. Acabou não sendo muito econômico em termos de quantidade de código (veja mais sobre isso mais adiante), mas é claro e fácil corrigir erros. As lacunas entre os caracteres são levadas em consideração no estágio de saída, indicando a posição correspondente (a biblioteca RTClib é usada para ler o relógio):
 void loop() { DateTime clock = RTC.now(); if (clock.second()!=old_second) { uint8_t values; values=clock.hour()/10; //  drawValPos(values,0); values=clock.hour()%10; //  drawValPos(values,12); values=clock.minute()/10; //  drawValPos(values,28); values=clock.minute()%10; //end   drawValPos(values,40); if (clock.second()%2) drawDP(24); else drawDPclr(24); old_second=clock.second(); }//end if clocksecond } 

Dez dígitos de 20 bytes cada ocuparão 200 bytes na memória - cerca de 10% do seu volume (e a fonte larga é 16x16, como no exemplo abaixo, e todos os 16%). Uma fonte monolíngue completa desse tamanho, juntamente com números, sem levar em consideração todos os tipos de sinais de pontuação e especiais. caracteres, contém 62 (inglês) a 74 (russo sem E), o valor ocupará quase metade da RAM do ATmega328. Portanto, truques com matrizes e funções de saída separadamente para cada caractere terão que ser cancelados e fazer conforme o esperado. Ou seja, as fontes devem ser deixadas na memória do programa e baixadas via PROGMEM, e todos os padrões de glifos devem ser organizados como uma única matriz de fontes e carregados para saída pelo número de símbolo em uma única tabela. Caso contrário, não haverá memória suficiente e o código do programa aumentará para um volume incontrolável. Aqui não vamos nos debruçar sobre isso, porque em nossos exemplos simples tudo isso não é necessário - a cada vez estaremos limitados a um pequeno número estritamente necessário de caracteres.

Devido ao tamanho grande do texto completo do esboço GraphicOLED_DC1307, não o trago; você pode fazer o download aqui . A função resetOLED é salva no texto, o que distorce a energia da tela quando o controlador é reinicializado (via pwrPin D2), mas nunca foi necessário, para que possa ser removido com segurança. O resultado do programa é mostrado na foto:

imagem

Infelizmente, a permanência simultânea no modo de texto e gráfico é excluída; portanto, se você quiser usar o espaço restante, precisará desenhar suas próprias fontes (resta espaço para cerca de 7 caracteres da fonte 5x7 em cada linha).

Tela gráfica WEG010016A


Quando finalmente chegaram as telas gráficas ordenadas WEG010016AL, comecei tentando inseri-las no modo de texto para ver o que acontecia.

Para verificar o modo de texto , foi baixado um programa para simular um mostrador de relógio do calendário com um sensor de temperatura externo, descrito em uma publicação anterior . O resultado me fez lembrar que diferentes telas do Winstar podem ser orientadas de maneira diferente em relação ao conector (neste caso, o WEG010016A possui um conector na parte superior, para o texto WEH001602B, que usamos acima, na parte inferior, e para o tipo C ao lado):

imagem

Abordaremos ainda mais a orientação da exibição, mas, por enquanto, veremos o que aconteceu. Mas acabou nada de bom: o modo de texto (é claro, equipado com uma muleta, que foi discutida no começo do artigo) funciona perfeitamente, mas na prática não faz sentido devido à falta de espaço entre os personagens. Portanto, não vamos nos demorar, mas proceda à consideração do modo gráfico.

Os procedimentos de instalação no modo gráfico são os mesmos que foram discutidos acima para a versão em texto. Resta lidar com a inversão da tela, se houver um conector na parte superior da tela. Claro, você pode simplesmente virar a tela, mas a posição com o conector voltado para baixo me parece mais natural e conveniente. Além disso, ao usar um tipo com um conector na lateral, pode ser necessário orientar o conector para a direita e não para a esquerda. Para a orientação “de cabeça para baixo”, é necessário transformar a imagem - isto é, trocar a primeira e a última posição horizontal, linhas e também inverter a ordem dos bits nos bytes que compõem a matriz (o bit menos significativo corresponderá ao ponto inferior).

Como eu já havia pintado dez dígitos no caso anterior, para a última tarefa, restava introduzir o procedimento de reversão do programa:
 byte reverse(byte x) { byte result=0,i; for(i=0;i<8;i++) { if (x & (1 << i)) { result |= 1 << (7-i); } } return result; } 

Você pode alterar a ordem das coordenadas horizontais e das linhas verticais fazendo alterações na função setGraphicCursor:
 void setGraphicCursor( uint8_t x, uint8_t y ){ if( 0 <= x && x <= 99 ){ lcd.command(LCD_SETDDRAMADDR | (99-x)); } if( 0 <= y && y <= 1 ){ lcd.command(LCD_SETCGRAMADDR | (1-y)); } } 

As funções de saída da matriz de cada dígito permanecem as mesmas, apenas a inversão de bits é adicionada:
 void draw2 (byte x/* */) // 2 { for (byte i = x; i<x+10; i++){ setGraphicCursor(i, 0); byte b=reverse(Data2[0][ix]); lcd.write(b); setGraphicCursor(i, 1); b=reverse(Data2[1][ix]); lcd.write(b);} } 

O esboço completo da saída do relógio GraphicOLED_DC1307_100x16 pode ser baixado aqui , e o resultado para o visor WEG010016AL é mostrado na foto:

imagem

Mas nesta foto, uma fonte de um tipo diferente (16x16) na tela WEG010016CG (a tela também está de cabeça para baixo):

imagem

Se você recriar a fonte alterando a ordem dos bits manualmente, não será necessário fazer o inverso e o programa será executado mais rapidamente (embora não haja atrasos visíveis nos olhos). Mas o procedimento de inversão de bits fornecido é útil em qualquer caso - para exibir várias imagens. Por exemplo, de uma seta apontando para cima e para a direita, você pode obter programaticamente quatro direções ao mesmo tempo.
Desenho de seta
Imagem e código de seta (as coordenadas e os bits na tabela são invertidos de acordo com a posição inferior do conector para o visor WEG010016AL, veja acima):

imagem
 const byte DataATR[2][8]={{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x01,0x02,0x04,0x28,0x30,0x78,0x60,0x80}}; 

Funções para saída de setas multidirecionais:
 . . . . . void drawSW (byte x) //   (  ) { for (byte i = x; i<x+8; i++){ setGraphicCursor(i, 0); lcd.write(DataATR[0][ix]); setGraphicCursor(i, 1); lcd.write(DataATR[1][ix]);} } void drawNW (byte x) //   (  ) {//   : for (byte i = x; i<x+8; i++){ setGraphicCursor(i, 0); byte b=reverse(DataATR[1][ix]); lcd.write(b); setGraphicCursor(i, 1); b=reverse(DataATR[0][ix]); lcd.write(b);} } void drawNE (byte x) //   (  ) {//  ,    for (byte i = x; i<x+8; i++){ setGraphicCursor(i, 0); byte b=reverse(DataATR[1][7-(ix)]); lcd.write(b); setGraphicCursor(i, 1); b=reverse(DataATR[0][7-(ix)]); lcd.write(b);} } void drawSE (byte x) //   (  ) {//   for (byte i = x; i<x+8; i++){ setGraphicCursor(i, 0); lcd.write(DataATR[0][7-(ix)]); setGraphicCursor(i, 1); lcd.write(DataATR[1][7-(ix)]);} } . . . . . 

A foto abaixo mostra o resultado de um programa em branco para exibir o sensor de velocidade e direção do vento. Como você pode ver, acabou sendo muito simples implementar fontes de tamanhos diferentes em uma linha, juntamente com imagens:

imagem

Concluindo, acrescentarei que aqui está uma biblioteca muito interessante para trabalhar com o WS0010 nos modos gráfico e de texto usando o SPI. No texto, ele copia principalmente o Liquid Crystal (e o que mais você consegue imaginar?). E no gráfico, ele tem as funções de desenhar primitivas gráficas, fontes internas (grossas, como a minha e o usual 5x7) e muito mais.

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


All Articles