Lancer l'affichage sur STM32 via LTDC ... sur les registres

Salutations! Récemment, un projet avait besoin de lancer un écran doté d'une interface LVDS. Pour implémenter la tâche, le contrôleur STM32F746 a été sélectionné, car J'ai déjà beaucoup travaillé avec lui et il a le module LTDC, qui vous permet de travailler directement avec l'écran sans contrôleur. Dans ce cas, le contrôleur est déjà implémenté à l'intérieur du microcontrôleur. De plus, le dernier argument était qu'il y avait un débogage STM32F746-Disco sur cette pierre, que j'avais en main, ce qui signifie que je pouvais commencer à travailler sur le projet sans attendre que la carte, les composants, etc. viennent à moi.

Aujourd'hui, je vais vous expliquer comment exécuter le module LTDC, en travaillant avec des registres (CMSIS). HAL et d'autres bibliothèques n'aiment pas et n'utilisent pas pour des raisons religieuses, mais cela est également intéressant. Vous verrez qu'élever des périphériques complexes sur des registres est aussi simple qu'un SPI standard. Intéressant? Alors allons-y!



1. Un peu sur le LTDC


Ce module périphérique est essentiellement un contrôleur, qui se trouve généralement sur le côté de l'écran, par exemple SSD1963 et similaires. Si nous regardons la structure du LTDC, nous verrons qu'il s'agit physiquement d'un bus parallèle à 24 bits + un accélérateur graphique matériel + un tableau de données en RAM, qui est en fait un tampon d'affichage (tampon de trame).



En sortie, nous avons un bus parallèle ordinaire, qui contient 24 bits de couleur (8 bits par couleur du modèle RGB), des lignes de synchronisation, une ligne on / off d'affichage et une horloge pixel. Ce dernier, en fait, est un signal d'horloge par lequel les pixels sont chargés dans l'affichage, c'est-à-dire que si nous avons une fréquence de 9,5 MHz, alors en 1 seconde, nous pouvons charger 9,5 millions de pixels. Ceci en théorie, bien sûr, dans la pratique, les chiffres sont un peu plus modestes en raison des délais et d'autres choses.

Pour une introduction plus détaillée au LTDC, je vous conseille de lire quelques documents:

  1. Un aperçu des capacités du LTDC en F4, dans notre F7 tout est le même
  2. Note d'application 4861. "Contrôleur d'affichage LCD-TFT (LTDC) sur les microcontrôleurs STM32"

2. Que devons-nous faire?


Les microcontrôleurs ST ont gagné en popularité pour une bonne raison, l'exigence la plus importante pour tous les composants électroniques est la documentation, et tout va bien. Le site est certes horrible, mais je vais laisser des liens vers toute la documentation. Le fabricant nous évite les tourments et l'invention du vélo, donc à la page 520 du manuel de référence RM0385, des étapes en noir et blanc sont données, ce que nous devons faire:



En fait, vous n'avez pas à faire la moitié des tâches décrites: il n'est pas nécessaire de démarrer ou est déjà configuré par défaut. Pour le démarrage minimum, qui nous permet de dessiner des pixels, d'afficher des images, des graphiques, du texte, etc., il suffit de faire ce qui suit:

  • Activer l'horloge LTDC
  • Réglez le système d'horloge et la fréquence de sortie des données (horloge pixel)
  • Configurer les ports d'E / S (GPIO) pour fonctionner avec LTDC
  • Configurer les horaires pour notre modèle d'affichage
  • Ajustez la polarité des signaux. Déjà fait par défaut
  • Spécifiez la couleur d'arrière-plan de l'affichage. Nous ne le verrons pas encore, vous pouvez le laisser "à zéro"
  • Définir la taille réelle de la zone visible de l'affichage pour un calque spécifique
  • Sélectionnez le format de couleur: ARGB8888, RGB 888, RGB565, etc.
  • Spécifiez l'adresse du tableau qui servira de tampon de trame
  • Indiquez la quantité de données sur une ligne (longueur en largeur)
  • Indiquez le nombre de lignes (hauteur d'affichage)
  • Inclure la couche avec laquelle nous travaillons
  • Activer le module LTDC

Effrayant Et j'avais peur, mais cela s'est avéré fonctionner pendant 20 minutes avec toutes les procédures. Il y a une tâche, le plan est planifié et il ne reste plus qu'à l'accomplir.

3. Configuration du système d'horloge


Le premier élément dont nous avons besoin pour envoyer un signal d'horloge au module LTDC, cela se fait en écrivant dans le registre RCC:

RCC->APB2ENR |= RCC_APB2ENR_LTDCEN; 

Ensuite, vous devez configurer la fréquence d'horloge du quartz externe (HSE) à une fréquence de 216 MHz, c'est-à-dire au maximum. La première étape consiste à allumer la source d'horloge à partir du résonateur à quartz et à attendre le drapeau prêt:

 RCC->CR |= RCC_CR_HSEON; while (!(RCC->CR & RCC_CR_HSERDY)); 

Réglez maintenant le délai pour la mémoire flash du contrôleur, comme elle ne sait pas comment travailler à la fréquence centrale. Sa valeur, comme le reste des données, est extraite du manuel de référence:

 FLASH->ACR |= FLASH_ACR_LATENCY_5WS; 

Maintenant, pour obtenir la fréquence souhaitée, je vais diviser 25 MHz de l'entrée à 25 et obtenir 1 MHz. Ensuite, juste en PLL, je multiplie par 432, car à l'avenir, il y a un diviseur de fréquence avec une valeur minimale de / 2 et vous devez lui appliquer deux fois la fréquence. Après cela, nous connectons l'entrée PLL à notre résonateur à quartz (HSE):

 RCC->PLLCFGR |= RCC_PLLCFGR_PLLM_0 | RCC_PLLCFGR_PLLM_3 | RCC_PLLCFGR_PLLM_4; RCC->PLLCFGR |= RCC_PLLCFGR_PLLN_4 | RCC_PLLCFGR_PLLN_5 | RCC_PLLCFGR_PLLN_7 | RCC_PLLCFGR_PLLN_8; RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC; 

Activez maintenant PLL et attendez le drapeau prêt:

 RCC->CR |= RCC_CR_PLLON; while((RCC->CR & RCC_CR_PLLRDY) == 0){} 

Nous attribuons la sortie de notre PLL comme source de la fréquence du système et attendons le drapeau prêt:

 RCC->CFGR |= RCC_CFGR_SW_PLL; while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_1) {} 

Ceci termine le réglage général de l'horloge et nous passons au réglage de la fréquence d'horloge (PLLSAI) pour notre affichage (horloge pixel). Le signal pour PLLSAI selon la fiche technique est pris après le diviseur / 25, c'est-à-dire, à l'entrée, nous avons 1 MHz. Nous devons obtenir une fréquence d'environ 9,5 MHz, pour cela, nous multiplions la fréquence de 1 MHz par 192, puis en utilisant deux diviseurs par 5 et 4, nous obtenons la valeur souhaitée PLLSAI = 1 MHz * 192/5/4 = 9,6 MHz:

 RCC->PLLSAICFGR |= RCC_PLLSAICFGR_PLLSAIN_6 | RCC_PLLSAICFGR_PLLSAIN_7; RCC->PLLSAICFGR |= RCC_PLLSAICFGR_PLLSAIR_0 | RCC_PLLSAICFGR_PLLSAIR_2; RCC->DCKCFGR1 |= RCC_DCKCFGR1_PLLSAIDIVR_0; RCC->DCKCFGR1 &= ~RCC_DCKCFGR1_PLLSAIDIVR_1; 

Comme dernière étape, nous activons PLLSAI pour l'affichage et attendons l'indicateur prêt à travailler:

 RCC->CR |= RCC_CR_PLLSAION; while ((RCC->CR & RCC_CR_PLLSAIRDY) == 0) {} 

Ceci termine la configuration de base du système d'horloge, afin de ne pas oublier et ensuite de ne pas souffrir, activons la synchronisation sur tous les ports d'entrée / sortie (GPIO). Nous n'avons pas de batterie, au moins pour le débogage, donc nous n'économisons pas:

 RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOFEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOGEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOHEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOJEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOKEN; 

4. Configuration des ports d'E / S (GPIO)


La configuration de gpio est très simple - nous avons toutes les jambes du bus LTDC à configurer comme sortie alternative et à haute fréquence. Pour ce faire, dans le manuel de référence à la page 201, nous avons cette astuce:



Le tableau indique les bits des registres que vous devez définir pour obtenir le paramètre nécessaire. Il convient de noter que toutes les accolades sont désactivées. Où chercher quelle fonction alternative inclure? Et pour cela, allez à la page 76 de la fiche technique de notre contrôleur et consultez le tableau suivant:



Comme vous pouvez le voir, la logique du tableau est la plus simple: nous trouvons la fonction dont nous avons besoin, dans notre cas LTDC B0, puis nous regardons sur quel GPIO il se trouve (PE4, par exemple) et en haut nous voyons le numéro de la fonction alternative que nous utiliserons pour configurer (AF14 avec nous). Pour configurer notre sortie en tant que sortie push-pull avec une fonction alternative, LTDC B0, nous devons écrire le code suivant:

 GPIOE->MODER &= ~GPIO_MODER_MODER4; GPIOE->MODER |= GPIO_MODER_MODER4_1; GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR4_1; GPIOE->AFR[0] &= ~GPIO_AFRL_AFRL4_0; GPIOE->AFR[0] |= GPIO_AFRL_AFRL4_1 | GPIO_AFRL_AFRL4_2 | GPIO_AFRL_AFRL4_3; 

J'ai donné un exemple pour la broche PE4, qui correspond à la broche B0 sur le bus LTDC, c'est-à-dire qu'il s'agit d'un bit zéro de couleur bleue. Pour toutes les autres conclusions, le réglage est identique, seules 2 conclusions méritent une attention particulière, l'une des prêtes comprend un écran, et l'autre son rétro-éclairage. Ils sont configurés comme une sortie push-pull normale, que tout le monde utilise pour faire clignoter une LED. La configuration ressemble à ceci:

 GPIOK->MODER &= ~GPIO_MODER_MODER3; GPIOK->MODER |= GPIO_MODER_MODER3_0; 

Ce paramètre concerne la sortie PK3, qui active et désactive notre rétro-éclairage. Soit dit en passant, vous pouvez également le pousser pour régler la luminosité en douceur. Pour PI12, qui comprend un écran (DISP), tout est pareil. La vitesse sur ces 2 broches est faible par défaut, car certaines actions à haute fréquence ne leur sont pas requises.

Vous pouvez consulter tous les autres ports d'E / S sur la carte de circuit imprimé de la carte de débogage ou sur le schéma de circuit de votre propre appareil.

5. Horaires et leurs paramètres


Les délais d'un point de vue physique sont des retards ordinaires. Je pense que vous avez observé à plusieurs reprises diverses perversions du type delay (1) lorsque vous avez examiné des exemples de code sur des écrans avec des contrôleurs SPI / I2C similaires à ILI9341. Là, un délai est nécessaire pour que le contrôleur, par exemple, ait le temps d'accepter la commande, de l'exécuter, puis de faire quelque chose avec les données. Dans le cas du LTDC, tout est à peu près le même, seulement nous ne ferons pas de béquilles et pourquoi pas - notre microcontrôleur lui-même est capable de configurer les temporisations nécessaires dans le matériel. Pourquoi sont-ils nécessaires sur un écran sans contrôleur? Oui, il est élémentaire qu'après avoir rempli la première ligne de pixels, passez à la ligne suivante et revenez à son début. Cela est dû à la technologie de production des écrans, et donc chaque modèle d'affichage spécifique a ses propres horaires.

Pour connaître les valeurs dont nous avons besoin, rendez-vous sur le site Web de ST et consultez le schéma de la carte de débogage STM32F746-Disco . Là, nous pouvons voir que l'affichage est RK043FN48H-CT672B et la documentation pour cela est disponible, par exemple, ici . Nous sommes particulièrement intéressés par le tableau de la page 13 de la section 7.3.1:



Ce sont nos valeurs dont nous aurons besoin lors de la mise en place. De plus, dans la documentation, il y a beaucoup plus qui est intéressant, par exemple, des diagrammes de signaux sur le bus et ainsi de suite, dont vous pourriez avoir besoin si, par exemple, vous voulez augmenter l'affichage en FPGA ou CPLD.

Accédez aux paramètres. Tout d'abord, afin de ne pas garder ces valeurs dans ma tête, je les disposerai sous forme de définit:

 #define DISPLAY_HSYNC ((uint16_t)30) #define DISPLAY_HBP ((uint16_t)13) #define DISPLAY_HFP ((uint16_t)32) #define DISPLAY_VSYNC ((uint16_t)10) #define DISPLAY_VBP ((uint16_t)2) #define DISPLAY_VFP ((uint16_t)2) 

Il y a une fonctionnalité intéressante. La largeur d'impulsion de synchronisation, appelée DISPLAY_HSYNC , a une valeur dans le tableau uniquement pour la fréquence d'horloge de pixel de 5 MHz, mais pas pour 9 et 12 MHz. Ce timing doit être sélectionné pour votre affichage, j'ai obtenu cette valeur de 30, alors que dans les exemples de ST, c'était différent. Au premier démarrage, si vous avez une erreur avec son réglage, l'image sera décalée soit vers la gauche, soit vers la droite. Si c'est à droite, on diminue le timing; si à gauche, on l'augmente. En fait, cela affecte l'origine de la zone visible, que nous verrons plus loin. Gardez à l'esprit, et l'image suivante de la page 24 de notre AN4861 vous aidera à comprendre tout ce paragraphe:



Une petite abstraction est pratique ici. Nous avons 2 zones d'affichage: visible et générale. La zone visible a des dimensions avec une résolution déclarée de 480 par 272 pixels, et la zone totale est le visible + nos timings, dont il y a 3 de chaque côté. Il est également utile de comprendre (ce n'est plus une abstraction) qu'un tick du système fait 1 pixel, donc la surface totale est de 480 pixels + HSYNC + HBP + HFP.

Il est également utile de réaliser que moins il y a de synchronisations, mieux c'est - l'affichage sera mis à jour plus rapidement et la fréquence d'images augmentera légèrement. Par conséquent, après la première exécution, expérimentez les synchronisations et réduisez-les autant que possible tout en conservant la stabilité.

Pour définir les horaires, je me suis fait une petite "feuille de triche" pour l'avenir dans le projet, cela vous aidera également à comprendre quelle figure spécifique et où l'écrire:

 /* *************************** Timings for TFT display********************************** * * HSW = (DISPLAY_HSYNC - 1) * VSH = (DISPLAY_VSYNC - 1) * AHBP = (DISPLAY_HSYNC + DISPLAY_HBP - 1) * AVBP = (DISPLAY_VSYNC + DISPLAY_VBP - 1) * AAW = (DISPLAY_HEIGHT + DISPLAY_VSYNC + DISPLAY_VBP - 1) * AAH = (DISPLAY_WIDTH + DISPLAY_HSYNC + DISPLAY_HBP - 1) * TOTALW = (DISPLAY_HEIGHT + DISPLAY_VSYNC + DISPLAY_VBP + DISPLAY_VFP - 1) * TOTALH = (DISPLAY_WIDTH + DISPLAY_HSYNC + DISPLAY_HBP + DISPLAY_HFP - 1) * */ 

D'où vient cette "feuille de triche" ... D'abord, vous avez vu une "formule" similaire quelques paragraphes auparavant. Deuxièmement, allez à la page 56 de notre AN4861:



Certes, j'espère que vous avez compris la signification physique des horaires avant l'apparition de cette feuille de triche et je suis sûr que vous auriez pu vous-même la compiler. Il n'y a rien de compliqué, et les images de RM et AN aident à comprendre visuellement l'effet des temporisations sur le processus de formation d'image.

Il est maintenant temps d'écrire un code qui configure ces timings. Dans la "feuille de triche" sont indiqués les bits du registre dans lesquels écrire, par exemple, TOTALH, et après le signe est égal à la formule donnant à la sortie un certain nombre. D'accord? Ensuite, nous écrivons:

 LTDC->SSCR |= ((DISPLAY_HSYNC - 1) << 16 | (DISPLAY_VSYNC - 1)); LTDC->BPCR |= ((DISPLAY_HSYNC+DISPLAY_HBP-1) << 16 | (DISPLAY_VSYNC+DISPLAY_VBP-1)); LTDC->AWCR |= ((DISPLAY_WIDTH + DISPLAY_HSYNC + DISPLAY_HBP - 1) << 16 | (DISPLAY_HEIGHT + DISPLAY_VSYNC + DISPLAY_VBP - 1)); LTDC->TWCR |= ((DISPLAY_WIDTH + DISPLAY_HSYNC + DISPLAY_HBP + DISPLAY_HFP -1)<< 16 |(DISPLAY_HEIGHT + DISPLAY_VSYNC + DISPLAY_VBP + DISPLAY_VFP - 1)); 

Et c'est tout avec les horaires! Dans cette section, vous ne pouvez configurer que la couleur d'arrière-plan. Je l'ai en noir par défaut, il est donc écrit en zéro. Si vous souhaitez modifier la couleur du calque d'arrière-plan (arrière-plan), vous pouvez également écrire n'importe quelle valeur, par exemple, 0xFFFFFFFF et tout remplir en blanc:

 LTDC->BCCR = 0; 

Il y a une merveilleuse illustration dans le manuel de référence , qui démontre clairement que nous avons en fait 3 couches: fond, couche 1 et couche 2. La couche de fond est "castrée" et ne peut être remplie qu'avec une couleur spécifique, mais elle peut également être incroyablement utile dans la mise en œuvre future conception graphique. En outre, cette illustration montre clairement la priorité des calques, ce qui signifie que nous ne verrons la couleur de remplissage sur l'arrière-plan que lorsque les calques restants sont vides ou transparents.

À titre d'exemple, je montrerai l'une des pages du projet où, lors de la mise en œuvre du modèle, l'arrière-plan a été rempli avec une couleur et le contrôleur n'a pas redessiné la page entière, mais uniquement des secteurs individuels, ce qui a permis de recevoir environ 50-60 ips pour de nombreuses autres tâches:



6. La dernière partie de la configuration LTDC


Les paramètres LTDC sont divisés en 2 sections: les premières sont communes à l'ensemble du module LTDC et sont situées dans le groupe de registres LTDC , et les secondes sont configurées dans l'une des deux couches et se trouvent dans les groupes LTDC_Layer1 et LTDC_Layer2 .

Nous avons fait les réglages généraux dans le paragraphe précédent, ceux-ci incluent le réglage des timings, le calque d'arrière-plan. Nous passons maintenant à la définition des couches et selon notre liste, nous avons besoin de la taille réelle de la zone visible de la couche, qui est décrite sous la forme de 4 coordonnées (x0, y0, x1, y2), qui nous permettent d'obtenir les dimensions du rectangle. La taille du calque visible peut être inférieure à la résolution de l'affichage, personne ne dérange pour rendre la taille du calque 100 pour 100 pixels. Pour ajuster la taille de la zone visible, écrivez le code suivant:

 LTDC_Layer2->WHPCR |= (((DISPLAY_WIDTH + DISPLAY_HBP + DISPLAY_HSYNC - 1) << 16) | (DISPLAY_HBP + DISPLAY_HSYNC)); LTDC_Layer2->WVPCR |= (((DISPLAY_HEIGHT + DISPLAY_VSYNC + DISPLAY_VBP - 1) << 16) |(DISPLAY_VSYNC + DISPLAY_VBP)); 

Comme vous pouvez le voir, tout est comme avec les horaires. Les points de départ (x0, y0) de la zone visible consistent en la somme de deux temporisations: HSYNC + HBP et VSYNC + VBP. Pour calculer les coordonnées du point final (x1, y1), la largeur et la hauteur en pixels sont simplement ajoutées aux données de valeur.

Vous devez maintenant configurer le format des données reçues. La qualité maximale est obtenue lors de l'utilisation du format ARGB8888, mais en même temps, nous obtenons la quantité maximale de mémoire occupée. Un pixel occupe 32 bits ou 4 octets, ce qui signifie que tout l'écran prend 4 * 480 * 272 = 522 240 octets, c'est-à-dire la moitié de la mémoire flash de notre contrôleur pas le plus faible. N'ayez pas peur - la connexion de SDRAM externe et de mémoire flash via QSPI résout les problèmes de mémoire et il n'y a aucune restriction sur ce format, nous nous réjouissons de bonne qualité. Si vous souhaitez économiser de l'espace ou si votre écran ne prend pas en charge le format 24 bits, des modèles plus adaptés sont utilisés pour cela, par exemple, RGB565. Un format très populaire pour les écrans et les caméras, et surtout lors de son utilisation, 1 pixel ne prend que 5 + 6 + 5 = 16 bits ou 2 octets. En conséquence, la quantité de mémoire occupée par la couche sera 2 fois inférieure. Par défaut, le contrôleur a déjà le format ARGB8888 configuré et ressemble à ceci:

 LTDC_Layer2->PFCR = 0; 

Si vous avez besoin d'un format différent de l'ARGB8888, passez aux pages 533 et 534 du manuel de référence et sélectionnez le format souhaité dans la liste ci-dessous:



Maintenant, créez un tableau et passez son adresse au LTDC, il se transformera en un tampon de trame et sera un «reflet» de notre couche. Par exemple, vous devez remplir le 1er pixel de la 1ère ligne de couleur blanche, pour cela, il vous suffit d'écrire la valeur de couleur (0xFFFFFFFF) dans le premier élément de ce tableau. Besoin de remplir le 1er pixel de la 2ème rangée? Ensuite, nous écrivons également la valeur de couleur dans l'élément avec le nombre (480 + 1). 480 - faites une saut de ligne, puis ajoutez le nombre dans la ligne dont nous avons besoin.

Ce paramètre ressemble à ceci:

 #define DISPLAY_WIDTH ((uint16_t)480) #define DISPLAY_HEIGHT ((uint16_t)272) const uint32_t imageLayer2[DISPLAY_WIDTH * DISPLAY_HEIGHT]; LTDC_Layer2->CFBAR = (uint32_t)imageLayer2; 

Dans le bon sens, après avoir configuré LTDC, vous devez également configurer la SDRAM afin de supprimer le modificateur const et d'obtenir le tampon de trame dans la RAM, car La propre RAM de MK ne suffit pas, même pour une couche de 4 octets. Bien que cela ne fasse pas de mal pour tester la bonne configuration des périphériques.

Ensuite, vous devez spécifier la valeur de la couche alpha, c'est-à-dire la transparence de notre couche Layer2 , pour cela nous écrivons une valeur de 0 à 255, où 0 est une couche complètement transparente, 255 est complètement opaque, c'est-à-dire 100% visible:

 LTDC_Layer2->CACR = 255; 

Selon notre plan, il est maintenant nécessaire d'enregistrer la taille de notre zone d'affichage visible en octets, pour cela nous écrivons les valeurs correspondantes dans les registres:

 LTDC_Layer2->CFBLR |= (((PIXEL_SIZE * DISPLAY_WIDTH) << 16) | (PIXEL_SIZE * DISPLAY_WIDTH + 3)); LTDC_Layer2->CFBLNR |= DISPLAY_HEIGHT; 

Les deux dernières étapes restent, à savoir l'inclusion de la couche 2 et le module périphérique LTDC lui-même. Pour ce faire, écrivez les bits correspondants:

 LTDC_Layer2->CR |= LTDC_LxCR_LEN; LTDC->GCR |= LTDC_GCR_LTDCEN; 

Ceci termine la configuration de notre module et vous pouvez travailler avec notre écran!

7. Un peu de travail avec LTDC


Tout travail avec l'écran se résume désormais à l'écriture de données dans le tableau imageLayer2 , il a une taille de 480 par 272 éléments, ce qui correspond pleinement à notre résolution et suggère une vérité simple - 1 élément de tableau = 1 pixel sur l'écran .

Par exemple, j'ai écrit une image dans un tableau que j'ai converti dans LCD Image Converter , mais en réalité, il est peu probable que vos tâches se limitent à cela. Il y a deux façons: utiliser une interface graphique prête à l'emploi et l'écrire vous-même. Pour des tâches relativement simples telles que la sortie de texte, la représentation graphique, etc., je vous conseille d'écrire votre propre interface graphique, cela prendra un peu de temps et vous donnera une compréhension complète de son fonctionnement. Lorsque la tâche est grande et difficile, et que vous n'avez pas le temps de développer votre propre interface graphique, je vous conseille de faire attention aux solutions prêtes à l'emploi, par exemple, uGFX et similaires.

Les symboles de texte, de lignes et d'autres éléments sont intrinsèquement des tableaux de pixels, donc pour les implémenter, vous devez implémenter la logique vous-même, mais vous devez commencer par la fonction la plus élémentaire - «sortie pixel». Il doit prendre 3 arguments: la coordonnée le long de X, la coordonnée le long de Y et, en conséquence, la couleur dans laquelle le pixel donné est peint. Cela peut ressembler à ceci:

 typedef enum ColorDisplay { RED = 0xFFFF0000, GREEN = 0xFF00FF00, BLUE = 0xFF0000FF, BLACK = 0xFF000000, WHITE = 0xFFFFFFFF } Color; void SetPixel (uint16_t setX, uint16_t setY, Color Color) { uint32_t numBuffer = ((setY - 1) * DISPLAY_WIDTH) + setX; imageLayer2[numBuffer] = Color; } 

Après avoir pris les coordonnées dans une fonction, nous les recalculons dans le numéro du tableau qui correspond à la coordonnée donnée, puis écrivons la couleur reçue sur l'élément reçu. Sur la base de cette fonction, vous pouvez déjà implémenter des fonctions pour afficher la géométrie, le texte et d'autres «goodies» de l'interface graphique. Je pense que l'idée est compréhensible, mais comment la concrétiser est à votre discrétion.

Résumé


Comme vous pouvez le voir, la mise en œuvre de périphériques même complexes sur les registres (CMSIS) n'est pas une tâche difficile, il vous suffit de comprendre comment cela fonctionne à l'intérieur. Bien sûr, il est maintenant à la mode de développer un firmware sans comprendre ce qui se passe, mais c'est une impasse si vous envisagez de devenir ingénieur, et non ...

Si vous comparez le code résultant avec une solution en HAL ou SPL, vous remarquerez que le code écrit dans les registres est plus compact. En ajoutant quelques commentaires là où vous en avez besoin et en les enveloppant dans des fonctions, nous obtenons une lisibilité au moins pas pire que celle de HAL / SPL, et si vous vous souvenez que le manuel de référence documente les registres, alors travailler avec CMSIS est plus pratique.

1) Le projet avec les sources dans TrueSTUDIO peut être téléchargé ici

2) Pour ceux qui sont plus à l'aise de regarder GitHub

3) Téléchargez l'utilitaire pour convertir les images en code LCD Image Converter ici

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


All Articles