Écrans graphiques et texte Winstar

Les affichages graphiques, y compris ceux du type OLED, les plus représentés sur notre marché par Winstar, ont une demande beaucoup plus faible par rapport aux minuscules et les publications sur leur utilisation sont également beaucoup moins. Pendant ce temps, ce sont les écrans OLED graphiques qui, en raison de l'absence de liaison aux tables de polices d'un modèle prédéfini, constituent le meilleur moyen d'obtenir des dispositifs d'affichage ergonomiques pour une grande variété de besoins. De plus, il s'est avéré que le mode graphique du contrôleur WS0010 est plus facile à initier et fonctionne plus stable que le mode texte.

Avant de passer à l'examen des affichages graphiques réels, nous examinerons le problème persistant des problèmes d'activation du mode texte du contrôleur WS0010, qui a reçu une solution inattendue et évidente (oh, où étaient mes yeux!).

Résolution des problèmes du mode texte WS0010


Il est bien connu que les écrans de ligne Winstar ont des problèmes de stabilité lors de l'initialisation. Soit dit en passant, il s'est avéré que ce n'était pas unique aux «damnés chinois»: les échantillons de Newhaven Display 16x2, que j'ai obtenus avec grande difficulté, situés de l'autre côté du globe, sont en externe une copie complète de Winstar, à l'exception de l'emplacement de certaines inscriptions et du nom de la société sur la tache ( la même forme et la même police):

image

Contenant, comme il est écrit dans les fiches techniques, un certain contrôleur "LCD comparable", ces écrans se comportent de manière totalement identique aux chinois et présentent les mêmes inconvénients. Évidemment, vous ne devriez pas passer du temps à vérifier d'autres sociétés, telles que Midas: à en juger par cette publication , cela ne pourrait se passer d'une coopération internationale. L'économie mondialisée rulez!

Les difficultés du mode texte s'expriment dans le fait qu'au démarrage (par exemple, lors du redémarrage ou de la réinitialisation manuelle du programme du contrôleur de contrôle), des ordures peuvent apparaître sur les écrans, et les lignes 0 et 1 changent de place de manière aléatoire. Les expériences ont montré que cela ne dépend pas de la méthode d'inclusion (8 bits ou 4 bits). Ce problème est particulièrement aigu lorsque des redémarrages périodiques du logiciel sont nécessaires, par exemple, par Watchdog-timer.

Une partie du problème est une attitude soignée vis-à-vis de l'alimentation (à partir d'une source distincte, et en aucun cas de l'USB Arduino), et un redémarrage séparé en éteignant et rallumant l'écran après avoir démarré le programme de contrôle (voir la publication précédente de l'auteur). Il s'est avéré que l'auteur de ces lignes n'était pas le seul à proposer une solution similaire au problème: l'auteur du module complémentaire LuquidCrystal appelé WinstarOLED a également inclus un pw_pin spécial, avec lequel la puissance d'affichage est déformée au moment du démarrage du programme.

Mais ce ne sont bien sûr que des initiatives et des demi-mesures. Quelqu'un SeregaB est tombé sur un chemin radical (voir sa publication sur easyelectronics.ru - merci à Tomasina pour l'astuce). En fait, il a posé une tâche complètement différente: apprendre à travailler uniquement avec le mode graphique plutôt qu'avec le mode texte. En essayant de basculer entre les modes, il a rapidement découvert que " passer au mode graphique était normal, et du graphique au" texte "- très maladroit ." Puis il s'est souvenu qu '« il y a longtemps, lorsque les DSh étaient encore imprimés sur du papier, dans certains DSh du HD44780, j'ai lu que les modes de commutation ne devaient être effectués que lorsque l'écran était éteint ». Et ça a marché.

À partir de la publication citée, je vais simplement reproduire deux procédures de commutation ici, en les adaptant légèrement pour une utilisation avec LuquidCrystal (l'instance de classe est appelée OLED1 ici).

Passer en mode graphique:
OLED1.command(0x08);//  OLED1.command(0x1F);//   OLED1.command(0x01);//    (..  clear()) OLED1.command(0x08|0x04);//  

Passer en mode texte:
  OLED1.command(0x08);//  OLED1.command(0x17);//    OLED1.command(0x01);//    (..  clear()) OLED1.command(0x04 | 0x08);//  

Comme nous le verrons plus tard, la première procédure n'est pas vraiment nécessaire: le WS0010 passe en mode graphique à partir d'un demi-coup, il suffit de lui envoyer la commande 0x1F. Mais la deuxième séquence de commandes était tout à fait le cas. Pour l'échantillon, il a été inclus directement dans l'esquisse à l'aide de LuquidCrystal sous cette forme:
 void reset_textmode() //     { OLED1.command(0x08);//  OLED1.command(0x17);//    OLED1.command(0x01);//    OLED1.command(0x04 | 0x08);//  } 

Ensuite, cette fonction a été appelée dans la configuration juste après le lancement de la bibliothèque:

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

Si vous insérez un délai (500) avant cela, la démonstration se révèle très évidente: après avoir appuyé sur le bouton de réinitialisation de la carte Arduino sur l'écran, comme d'habitude, des ordures apparaissent, mais seulement pendant un moment: après le déclenchement de la fonction, l'écran est effacé et toutes les lignes sont de retour en place .

La fonction fonctionne comme ça, mais pour plus de commodité, j'ai remplacé le contenu de la fonction LiquidCrystalRus :: clear () dans le fichier de la bibliothèque LiquidCrystalRus_OLED.cpp mise à niveau qui a été discutée précédemment avec cette séquence de commandes (je vous rappelle que vous pouvez la télécharger depuis le site de l'auteur). Il n'y a pas d'attente pour que la commande soit exécutée dans la bibliothèque, donc pour la fiabilité, après chaque commande il y a des retards de 100 μs insérés dans le style général de la bibliothèque. Dans les esquisses utilisant cette variante de LiquidCrystalRus_OLED, au début de la configuration, il est nécessaire d'appeler la fonction clear (), et en même temps cela effacera l'écran.
Remarque
Il y a un problème avec le nettoyage de l'écran: dans la fiche technique du tableau des commandes, il est noté que la commande 0x01 peut durer aussi longtemps que 6,2 ms «lorsque fsp ou fosc = 250KHz». Quel type de "fsp ou fosc" est en fait dans des contrôleurs spécifiques, ils étaient trop paresseux pour écrire, mais en tout cas, même si c'est mégahertz, le délai pour cette commande devrait être important (et l'auteur de LiquidCrystal le mentionne). Cependant, dans la pratique, il s'avère que l'équipe de nettoyage travaille pour elle-même s'il n'y a aucun retard. Je n'ai donc pas compris, mais j'ai agi selon la règle de programmation bien connue: "ça marche - ne touchez pas!".

Passons maintenant au mode graphique.

Le mode graphique dans le texte affiche WEH001602


Pour commencer, j'ai essayé de basculer l'affichage de texte que j'avais WEH001602BG en mode graphique. Notez que les affichages graphiques 100x16 et texte (configuration 20x2, 16x2 ont juste moins de points horizontaux) ont des matrices identiques, seuls les affichages texte sont séparés par des intervalles de familiarité. Cela limite considérablement l'utilisation du mode graphique dans les affichages de texte, et encore plus du mode texte dans les graphiques. Mais pour tester son fonctionnement, vous pouvez utiliser n'importe lequel d'entre eux.

L'écran avec le DS1307 a été connecté à l'Arduino Nano selon le schéma suivant:
image

Selon le même schéma, nous connecterons à l'avenir des affichages graphiques. La couleur grise dans le diagramme montre la connexion du deuxième écran, si nécessaire.

Pour passer en mode graphique, vous pouvez utiliser la procédure améliorée de la section précédente, mais une fonction simple à partir d'une seule commande fonctionne:
 . . . . . #define LCD_SETGRAPHICMODE 0x1f LiquidCrystal lcd(9, 4, 8, 7, 6, 5); void setGraphicMode(){ lcd.command(LCD_SETGRAPHICMODE); } . . . . . 

Nous n'avons pas besoin de table russe ici, par conséquent, le LiquidCrystal standard (non redressé) est utilisé, qui fonctionne parfaitement en mode graphique. Afin de ne pas gâcher le débogage de toutes les options de bibliothèque, dans le cas où le texte et les affichages graphiques sont inclus en parallèle, alors pour chacun j'utilise ma propre bibliothèque (pour le texte mis à jour Rus_OLED, pour le graphique normal). Dans ce cas, la connexion peut toujours être établie sur les mêmes branches du contrôleur, à l'exception des broches de sortie E, conformément au schéma ci-dessus.

De plus, j'ai partiellement utilisé les réalisations de l'auteur de la bibliothèque WinstarOLED mentionnée (en soi, ce module complémentaire pour LuquidCrystal est, à mon avis, incomplet, et il n'est pas pratique de l'utiliser comme). Il a introduit une fonction pratique pour régler le curseur graphique (l'erreur d'origine concernant la valeur maximale de x a été corrigée ici):
 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); } } 

La constante LCD_SETDDRAMADDR est définie dans la bibliothèque LiquidCrystal. Un affichage 100x16, comme un affichage texte, est divisé en deux lignes, 0 et 1, car y ne peut prendre que deux valeurs ici. Et la coordonnée horizontale x varie de 0 à 99. Un octet est envoyé avec la commande lcd.write (), dont les bits individuels déterminent les positions lumineuses de la ligne verticale d'une longueur de 8 points. La position la plus à gauche dans la ligne supérieure a les coordonnées 0,0, la plus à droite en bas - 99,1. De plus, le point le plus bas correspondra au bit le moins significatif et le point le plus bas - le plus haut.

Pour la commodité de l'encodage des images, j'ai dessiné une plaque dans laquelle vous pouvez rapidement créer manuellement le code souhaité. Pour des tableaux de polices complets, bien sûr, il est conseillé d'utiliser des éditeurs spéciaux (il existe au moins un million de degrés différents d'activité amateur), mais 10 chiffres avec l'ordre de bits souhaité sont plus rapides à traiter manuellement, d'autant plus que les polices créées automatiquement doivent souvent encore être finies à la main. Conformément à ce qui précède, un glyphe, par exemple, la police numéro 2 10x16 sera codé comme suit:

image

Tout cela est écrit dans un tableau à deux dimensions de la forme:
 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}}; 

Pour chaque chiffre 0-9, un tel tableau distinct est créé: Data0, Data1, Data2, etc. Pour les montres, en plus des chiffres, vous aurez également besoin d'un double point. Il peut être raccourci:
 const byte DataDP[2][2]={{0x70,0x70}, {0x1c,0x1c}};//  

Étant donné que le contrôleur ne sait pas «clignoter» en mode graphique, il est nécessaire de clignoter par programmation les deux-points. Vous pouvez éteindre un double point simplement en affichant des zéros dans les positions correspondantes, mais pour l'uniformité j'ai fait un tableau séparé
 const byte DataDPclr[2][2]={{0x00,0x00}, {0x00,0x00}};// .  

Pour afficher chaque chiffre et séparément pour un double point, une fonction distincte est écrite:
 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]);} } 

Toutes les fonctions sont les mêmes, mais utilisent des tableaux différents, et pour un double point, d'autres limites de la boucle sont également utilisées. Il s'est avéré ne pas être trop économique en termes de quantité de code (voir plus loin à ce sujet), mais il est clair et facile de corriger les erreurs. Les écarts entre les caractères sont pris en compte au niveau de la sortie, indiquant la position correspondante (la bibliothèque RTClib est utilisée pour lire l'horloge):
 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 } 

Dix chiffres de 20 octets chacun occuperont 200 octets en mémoire - environ 10% de son volume (et la police large est 16x16, comme dans l'exemple ci-dessous, et tous les 16%). Une police monolingue complète de cette taille, accompagnée de chiffres, sans tenir compte de toutes sortes de signes de ponctuation et spéciaux. caractères, contient de 62 (anglais) à 74 (russe sans E), la valeur occupera près de la moitié de la RAM ATmega328. Par conséquent, les astuces avec des tableaux et des fonctions de sortie séparément pour chaque personnage devront être annulées et faire comme prévu. Autrement dit, les polices doivent être laissées dans la mémoire du programme et téléchargées via PROGMEM, et tous les modèles de glyphes doivent être organisés en un seul tableau de polices et chargés pour la sortie par numéro de symbole dans une seule table. Sinon, la mémoire sera insuffisante et le code du programme se gonflera à un volume incontrôlable. Ici, nous ne nous attarderons pas sur cela, car dans nos exemples simples, tout cela n'est pas nécessaire - chaque fois, nous serons limités à un petit nombre strictement nécessaire de caractères.

En raison de la grande taille du texte intégral du croquis GraphicOLED_DC1307, je ne l'apporte pas; vous pouvez le télécharger ici . La fonction resetOLED est enregistrée dans le texte, ce qui fausse la puissance d'affichage lorsque le contrôleur redémarre (via pwrPin D2), mais elle n'a jamais été nécessaire, elle peut donc être supprimée en toute sécurité. Le résultat du programme est montré sur la photo:

image

Malheureusement, le séjour simultané en mode texte et graphique est exclu.Par conséquent, si vous souhaitez utiliser l'espace restant, vous devrez dessiner vos propres polices (il reste de la place pour environ 7 caractères de police 5x7 dans chaque ligne).

Affichage graphique WEG010016A


Lorsque, enfin, les affichages graphiques commandés WEG010016AL sont arrivés, j'ai commencé par essayer de les mettre en mode texte afin de voir ce qu'il en était advenu.

Pour vérifier le mode texte, un programme de simulation d'un affichage de l'horloge calendaire avec un capteur de température externe, décrit dans une publication précédente, a été téléchargé. Le résultat m'a rappelé que différents affichages Winstar peuvent être orientés différemment par rapport au connecteur (dans ce cas, le WEG010016A a un connecteur en haut, pour le texte WEH001602B, que nous avons utilisé ci-dessus, en bas et pour le type C sur le côté):

image

Nous traiterons plus loin de l'orientation de l'affichage, mais pour l'instant, nous verrons ce qui s'est passé. Mais cela n'a rien de bon: le mode texte (bien sûr, équipé d'une béquille, qui a été discuté au début de l'article) fonctionne parfaitement, mais en pratique, cela n'a aucun sens en raison du manque d'espaces entre les personnages. Par conséquent, nous ne nous attarderons pas là-dessus, mais passons à l'examen du mode graphique.

Les procédures d'installation en mode graphique elles-mêmes sont les mêmes que celles décrites ci-dessus pour la version texte. Il reste à faire face au flip de l'écran s'il dispose d'un connecteur en haut de l'écran. Bien sûr, vous pouvez simplement retourner l'écran, mais la position avec le connecteur orienté vers le bas me semble plus naturelle et plus pratique. En outre, lorsque vous utilisez un type avec un connecteur sur le côté, vous devrez peut-être orienter le connecteur vers la droite plutôt que vers la gauche. Pour l'orientation «à l'envers», il est nécessaire de transformer l'image - c'est-à-dire, d'échanger les première et dernière positions horizontales, lignes, et également inverser l'ordre des bits dans les octets qui composent le tableau (le bit le moins significatif correspondra au point inférieur).

Comme j'avais déjà peint dix chiffres pour le cas précédent, il restait pour la dernière tâche à introduire la procédure d'inversion de programme:
 byte reverse(byte x) { byte result=0,i; for(i=0;i<8;i++) { if (x & (1 << i)) { result |= 1 << (7-i); } } return result; } 

Vous pouvez modifier l'ordre des coordonnées horizontales et des lignes verticales en modifiant la fonction 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)); } } 

Les fonctions de sortie du tableau de chaque chiffre restent les mêmes, seule l'inversion des bits est ajoutée:
 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);} } 

Le croquis complet de la sortie de la montre GraphicOLED_DC1307_100x16 peut être téléchargé à partir d'ici , et le résultat pour l'affichage WEG010016AL est montré sur la photo:

image

Mais sur cette photo, une police d'un type différent (16x16) sur l'écran WEG010016CG (l'affichage est également à l'envers):

image

Si vous recréez la police en modifiant manuellement l'ordre des bits, il n'est pas nécessaire de faire l'inverse et le programme s'exécutera plus rapidement (bien qu'il n'y ait pas de retards notables dans l'œil). Mais la procédure de retournement de bits donnée est utile dans tous les cas - pour afficher diverses images. Par exemple, à partir d'une flèche pointant vers le haut et vers la droite, vous pouvez obtenir par programme quatre directions à la fois.
Dessin de flèche
Image et code de flèche (les coordonnées et les bits dans le tableau sont inversés en fonction de la position inférieure du connecteur pour l'affichage WEG010016AL, voir ci-dessus):

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

Fonctions de sortie des flèches multidirectionnelles:
 . . . . . 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)]);} } . . . . . 

La photo ci-dessous montre le résultat d'un programme vierge pour afficher le capteur de vitesse et de direction du vent. Comme vous pouvez le voir, il s'est avéré très simple d'implémenter des polices de différentes tailles sur une seule ligne avec des images:

image

En conclusion, j'ajouterai que voici une bibliothèque très intéressante pour travailler avec WS0010 en modes graphiques et textuels en utilisant SPI. Dans le texte, il copie principalement Liquid Crystal (et à quoi d'autre pouvez-vous penser?), Et dans le graphique, il a pour fonctions de dessiner des primitives graphiques, des polices intégrées (épaisses, comme la mienne et le 5x7 habituel) et bien plus encore.

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


All Articles