Console de jeux stm32

Quelques tireurs pour stm32; comment, pourquoi, ce qui s'est passé.



Préface


En tant que fan de la «vieille» école de tireurs d'une part et développeur embarqué de l'autre, je me suis toujours demandé comment et pourquoi les auteurs de cette époque ont réussi à mettre en œuvre un nouveau genre qui nécessitait des approches complètement nouvelles sur un matériel très «modeste». Et j'ai décidé d'essayer de lancer quelque chose de similaire en utilisant des solutions basées sur MK moderne - ici il y a des ressources nues et "modestes" et un outil de débogage assez puissant (stm32, IMHO). Et donc, mon choix s'est porté sur la carte de développement de découverte stm32f769i.

Remarques


Pour le moment, l'assemblage n'est possible qu'à partir de l'environnement Keil MDK (chargeur de démarrage, jeux) ou en utilisant arm-gcc + make (chargeur de démarrage uniquement). Ports actuellement disponibles pour - Quake I (+ mods), Doom (+ mods), Duke Nukem (+ mods), Hexen, Heretic. Avec toutes les modifications, la liste peut être considérablement étendue.

Commençons


Dans cet article, je vais essayer de décrire brièvement les principales idées et principes de leur mise en œuvre sur la façon de créer une console de jeu en particulier pour la découverte de stm32f769i. Aussi, je vais essayer d'éviter les détails techniques détaillés, je poursuis plutôt l'objectif d'introduire le lecteur à une autre option pour utiliser MK moderne. Par "console de jeu" - je veux dire un appareil indépendant avec la possibilité d'exécuter des applications "utilisateur" sans mettre à jour le logiciel principal.

Architecture


Étant donné que l'implémentation finale nécessite la possibilité d'exécuter indépendamment divers jeux sans mettre à jour le micrologiciel MK, un besoin s'est fait sentir pour une certaine version du «chargeur de démarrage», donc:

  1. Chargeur; travailler avec la mémoire - «installation» d'une application (jeu), lancement.
  2. Chauffeur; Service au niveau du système HAL et fonctions connexes, transfert de l'API à l'application.
  3. Application; Le programme final ne renvoie pas le contrôle au chargeur de démarrage.

1. Bootloader


Il est basé sur le modèle IAP (In-Application-Programming) - pilotes utilisant un exemple de ST-microelectronics.

La particularité de cette approche est qu'il n'est pas nécessaire de modifier la configuration du démarrage MK. L'
ensemble du «corps» du chargeur de démarrage est dans la mémoire principale, ce qui permet à son tour d'utiliser la découverte stm32f769i «prête à l'emploi».

La principale fonctionnalité de ce niveau est la lecture du fichier .bin, l'écriture de son contenu dans la mémoire MK et le transfert du contrôle. A ce stade, le point clé est de lire l'adresse du point d'entrée et l'adresse du pointeur de pile, la seconde n'est pas obligatoire, car l'application ne renvoie pas le contrôle -
la pile est partagée et il n'est pas nécessaire de réécrire le pointeur. Un appel peut également être effectué via un pointeur vers une fonction. Ainsi, le résultat sera un chargeur «hybride» - sa partie «pilote» continue de servir l'application, tandis que les ressources du chargeur lui-même sont déchargées.

2. Pilote


Le pilote se présente sous la forme d'un «wrapper» au-dessus du niveau HAL, donnant accès aux ressources nécessaires - système de fichiers, affichage \ moniteur, son, contrôleur de jeu \ capteur d'affichage. Pour une utilisation ultérieure, l'API du pilote est transmise sous la forme d'une structure de pointeur via la «mémoire partagée» - un morceau de mémoire réservé à la fois au chargeur de démarrage et au côté retour. Une telle manipulation nécessite des coûts de mémoire, et la meilleure solution serait peut-être d'utiliser SWI (soft-interruption, appel svc), mais pour cela, à son tour, vous devez pouvoir changer le contexte - car tous les appels ne peuvent pas être traités dans une interruption. De plus, la mémoire «partagée» est utilisée pour transmettre des arguments utilisateur (par exemple, via la console), une condition préalable est d'ajouter l'attribut no-init pour cette section,cela évitera de l'écraser avec runtime-library au moment de l'initialisation de l'application utilisateur.

3. Application


Par conséquent, la seule chose que vous devez savoir au moment de la création de l'application est l'architecture du cœur du processeur, il n'y a pas de dépendances de la couche HAL, il n'y a pas non plus de table de vecteurs d'interruption, toutes les interruptions sont traitées par le chargeur. En conséquence, l'application utilise beaucoup moins d'espace dans la mémoire du programme - du fait qu'une partie de la fonctionnalité est «protégée» avec le chargeur de démarrage / pilote, ce qui vous permet de l'installer dans la zone de données SRAM (RAM interne). Cela peut à son tour réduire considérablement le nombre de cycles d'écriture de la mémoire Flash et également accélérer le processus de débogage et d'exécution en général. Parmi les inconvénients - au moment du débogage, il est possible d'appeler l'application uniquement de l'extérieur, par exemple, en utilisant une commande de la console (port COM sur ST-Link, VCOM), pour cela une version très simplifiée de la ligne de commande est utilisée.

Ressources:

chargeur , hal , pilote

Fonctionnalités de développement


Mémoire


La première chose à laquelle j'ai dû faire face a été le problème d'allocation d'une ressource de mémoire externe (SDRAM). Cela est dû au fait que certains jeux nécessitent plus de mémoire pour la section .bss (duke nukem ~ 5,5 Mo). Placer un tel volume n'est possible que dans sdram, mais parce que la même mémoire est utilisée par le chargeur de démarrage pour stocker des données temporaires - images, son, contenu des fichiers, etc. ..., nécessaires uniquement avant le démarrage de l'application - il a été décidé de diviser la gestion de cette partie de la mémoire - de chaque côté il y a son propre malloc / free. Après le démarrage - le pilote utilise des pointeurs vers les fonctions malloc \ free, qui, si nécessaire, sont passées en paramètre à la fonction d'appel. Autrement dit, après le démarrage du jeu, le pilote ne peut pas effectuer directement l'allocation à partir de sdram.Un fait intéressant à propos de D-Cache et I-Cache - en raison des particularités de la gestion de la mémoire externe - vous devez désactiver les deux lignes avant de commencer, car sdram est réinitialisé, tout irait bien, mais il y en a un «mais» - vous devez toujours invalider le cache, sinon, par défaut, il conserve l'état valide de toutes les lignes, alors qu'elles étaient écrasées dans l'intervalle lorsque le cache a été désactivé.
Une autre caractéristique - toutes les données du chargeur de démarrage sont placées dans la section DTCM, cela permet de ne pas utiliser le cache lors de l'accès à la mémoire (le MPU le permet) et en conséquence - les problèmes de cohérence sont résolus lorsque vous travaillez avec DMA;
En conjonction - CPU -> D-Cache -> Memory <- DMA

Arts graphiques


Pour la plupart, ce sont plusieurs fonctions de mise à l'échelle de l'image (2x2, 3x3),
la fonction d'initialisation et le chargement de la palette. Le point clé est l'indication correcte des attributs de la mémoire de trame (effectuée via la configuration MPU) - pour le chargeur de démarrage, ce sera «Write-back, no write allocate», pour éliminer l'effet de scintillement, comme le mode tampon unique est utilisé, tandis que l'application utilise «Écriture directe, aucune allocation d'écriture», ce qui permet d'obtenir le FPS le plus élevé (Doom ~ 28-40).

Tous les ports de jeu existants fonctionnent avec des graphiques 8 bits, mais il est également possible de passer en mode couleurs vraies 16 bits (nécessite une modification du côté du jeu).
Il est possible de mettre à l'échelle une image 8 bits à l'aide de DMA2D, mais cette approche n'a pas porté ses fruits - cela coûte ~ 1000 interruptions nécessaires pour traiter une image avec une résolution finale de 640x480 pixels, elle génère également beaucoup d'artefacts dans les jeux - images individuelles (sprites, polygones) ne seront pas entièrement rendus, car dans ce cas, l'ensemble du processus de rendu dans le jeu se déroulera en parallèle avec l'esquisse de l'écran.

Du son


Cette partie est réalisée sous la forme d'un simple logiciel de mixage 16 bits, 16 canaux basé sur des exemples issus de la microélectronique ST, le contrôleur I2S est également utilisé. Pour le moment, il n'y a aucun moyen de convertir les formats audio entre eux - cette partie est implémentée au niveau du jeu en fonction des exigences. Duke Nukem, à mon avis, possède le plus riche ensemble d'utilitaires pour travailler avec le son, y compris et réverbération.

Entrer


Le pilote du joystick est également fait en utilisant la classe usb-hid (en fait, la manette de jeu sera définie comme une souris d'ordinateur ..). Le capteur d'affichage - comme une manette de jeu, utilise le même canal pour transmettre des événements, et c'est extrêmement gênant.

Jeux


Condamner


Le port stm32doom a été pris comme base ,
une prise en charge sonore supplémentaire, corrigée avec les derniers changements de doom au chocolat , quelques corrections dues à Killough, de prBoom, ont été ajoutées. Le jeu vous permet d'utiliser toutes les modifications et cartes disponibles pour Chocolate Doom, y compris Ajout de décors et de sons à partir des versions 3DO et PS1 du jeu. Une optimisation graphique a été ajoutée - la résolution du rendu de texture dépend de la distance, la solution est telle, à différents endroits - un gain de + 3-7 images par seconde. disponible. La dernière version ajoute également la prise en charge des sprites "transparents" - tout est basé sur le tableau généré de combinaisons d'éléments de palette - quelque chose de similaire est utilisé dans Quake II et les jeux basés sur le moteur de construction. Jeu incl. les modifications peuvent être complétées.

Ressources:

stm32doom , 3DO doom , chocolate doom , Version actuelle

Duke nukem


Basé sur le porto duc au chocolat . Il n'y avait pas de temps pour bien comprendre le code source du jeu, donc tout est resté «tel quel», seuls des défauts mineurs ont été corrigés. Le port vous permet également d'exécuter des modifications officielles et pas tout à fait - édition atomique, hiver nucléaire, etc ...
Remarque - pour le moment, aucun des épisodes du jeu ne peut être complètement terminé en raison de défauts existants.

Ressources:

chocolate duke , Version actuelle

Quake i


Malheureusement, le lien vers le dépôt d'origine n'a pas été conservé et je ne le trouve pas, le
port a été exécuté sous le nom sdl quake. Parmi les fonctionnalités, il convient de noter la présence d'une architecture client-serveur, une pile très "gourmande" (~ 700kb) à cause de laquelle de nombreuses situations intéressantes se sont produites pour la première fois (armcc ne surveille pas vraiment son utilisation), des problèmes généralisés d'alignement - peut-être cela concerne-t-il uniquement l'armcc du compilateur, mais presque partout où il y a un appel à un élément de la structure de plus d'un octet - vous devez utiliser la fonction wrapper pour lire / écrire l'octet autrement - exception de faute grave. Le jeu "passe" assez bien, avec un fps moyen ~ 15. Plusieurs épisodes peuvent également être complétés, plus ou moins confortables uniquement au premier niveau de difficulté :)

Ressources:

Version actuelle

Hexen, Heretic


Pour la plupart, ils héritent du moteur Doom, donc du point de vue du portage, ils sont presque identiques (à mon humble avis). Dans hexen ajouté la possibilité de démarrer le jeu à l'emplacement sélectionné, les jeux ne peuvent pas être terminés complètement.

Ressources:

hexen stm32 , heretic stm32

Résultat


Doom , Duke Nukem , Quake

Merci de votre attention.

All Articles