Contrôle des LED RVB via les microcontrôleurs Cypress UDB PSoC



Présentation


J'ai longtemps voulu apprendre la technique de programmation des blocs UDB dans les contrôleurs Cypress PSoC, mais d'une manière ou d'une autre, toutes mes mains n'ont pas atteint. Et donc, un problème s'est posé sur lequel cela pourrait être fait. Comprenant les matériaux du réseau, j'ai réalisé que les recommandations pratiques pour travailler avec UDB se limitaient à diverses variantes de compteurs et de PWM. Pour une raison quelconque, tous les auteurs font leurs variantes de ces deux exemples canoniques, de sorte que la description d'autre chose pourrait bien être intéressante pour les lecteurs.

Alors. Il y avait un problème pour gérer dynamiquement une longue file de LED RVB WS2812B. Des approches classiques de cette question sont connues. Vous pouvez prendre l'Arduino trivial, mais là, la sortie est programmée, donc pendant que les données sont sorties, tout le reste est inactif, sinon les chronogrammes échoueront. Vous pouvez prendre STM32 et sortir des données soit via DMA vers PWM, soit via DMA vers SPI. Les techniques sont connues. J'ai même, à un moment donné, personnellement contrôlé une ligne de seize diodes via SPI. Mais les frais généraux sont excellents. Un bit de données dans les LED occupe 8 bits en mémoire pour le cas de PWM et de 3 à 4 bits (selon la fraîcheur PLL dans le contrôleur) pour SPI. Bien qu'il y ait peu de LED, ce n'est pas effrayant, mais s'il y en a quelques centaines, alors 200 * 24 = 4800 bits = 600 octets de données utiles doivent être stockés physiquement dans un tampon d'une capacité de plus de 4 kilo-octets pour l'option PWM ou de plus de 2 kilo-octets pour SPI- les options. Pour une indication dynamique des tampons, il devrait y en avoir plusieurs, et le STM32F103 a de la RAM pour tout sur tout 20 kilo-octets. Non pas que nous ayons rencontré une tâche irréalisable, mais une raison de vérifier si cela peut être implémenté sur le PSoC sans avoir besoin de dépenser de RAM supplémentaire, est assez importante.

Références théoriques


Tout d'abord, découvrons quel genre de bête est un tel UDB et comment ils fonctionnent avec. De merveilleux films pédagogiques du fabricant du contrôleur vous y aideront.

Vous devriez commencer à regarder à partir d'ici , puis à la fin de chaque vidéo, il y aura un lien vers la série suivante. Étape par étape, vous acquerrez des connaissances de base et considérerez l'exemple canonique «compteur». Eh bien, et un système de contrôle des feux de circulation.

À peu près le même, mais coupé en petits morceaux, vous pouvez le voir ici . Ma vidéo n'a pas été lue, mais elle peut être téléchargée et visionnée localement. Entre autres choses, il existe également un exemple canonique de la mise en œuvre de PWM.

Solutions finies


Afin de ne pas réinventer la roue (et vice versa - pour apprendre la méthodologie de l'expérience de quelqu'un d'autre), j'ai fouillé le réseau à la recherche de solutions toutes faites pour contrôler les LED RGB. La solution la plus populaire est StripLightLib.cylib. Mais depuis de nombreuses années, il prévoit d'ajouter le support Ajouter un DMA. Mais je veux essayer une solution qui ne dépend pas du processeur central. Je veux commencer le processus et l'oublier, en me concentrant sur la préparation de la prochaine image.

La solution qui correspond à mes souhaits a été trouvée sur https://github.com/PolyVinalDistillate/PSoC_DMA_NeoPixel .

Tout y est implémenté sur UDB (mais les LED ne sont qu'une excuse, le but est d'apprendre UDB). Il existe un support pour DMA. Et le projet là-bas est clairement magnifiquement organisé.

Problèmes de la solution choisie comme base


Comment le "firmware" dans le projet PSoC_DMA_NeoPixel est organisé, ceux qui le souhaitent peuvent le voir après avoir lu l'article. Cela corrigera le matériau. Pour l'instant, je dirai seulement que j'ai d'abord simplifié la logique du firmware d'origine sans réduire les ressources consommées (mais c'est devenu plus facile à comprendre). Puis il a commencé à expérimenter le remplacement de la logique de l'automate, qui promettait un gain de ressources, mais rencontrait un sérieux problème. Et il a donc décidé - il n'est pas éliminé! Et de vagues doutes ont commencé à me tourmenter: l'auteur anglais avait-il le même problème? Sa démo clignote très joliment avec des LED. Mais que se passe-t-il si nous remplaçons le beau remplissage par «toutes les unités» et contrôlons la sortie non pas avec nos yeux, mais avec un oscilloscope?
Donc, aussi grossièrement que possible (vous pourriez même dire «brutalement»), nous formons les données:

memset (pPixelArray,0xff,sizeof(pPixelArray)); //Call NeoPixel update function (non blocking) to trigger DMA pixel update NP_Update(); 

Et ici, nous voyons une telle image sur un oscilloscope:



Le premier bit a une largeur différente de celle du reste. J'ai demandé d'envoyer toutes les unités, mais pas toutes de partir. Parmi eux, zéro! Modifiez l'analyse:



La largeur est différente pour chaque huitième bit.

En général, cet exemple en tant que solution indépendante ne convient pas, mais en tant que source d'inspiration - tout simplement parfait. Tout d'abord, son inopérabilité n'est pas visible à l'oeil (les LED sont toujours brillantes, l'oeil ne voit pas qu'elles brillent à moitié maximum), mais le code est bien structuré, c'est agréable de le prendre comme base. Deuxièmement, cet exemple fournit un espace pour trouver des moyens de simplifier, et troisièmement, il vous fait réfléchir sur la façon de corriger le défaut. L'essentiel est de comprendre le matériel! Donc, encore une fois, après avoir lu l'article, je recommande d'essayer d'analyser l'exemple d'origine, en réalisant comment cela fonctionne.

Partie pratique


Maintenant, nous commençons à pratiquer. Nous testons les principaux aspects du développement de firmware pour UDB. Considérez la relation et les techniques de base. Pour ce faire, ouvrez ma version du projet . Le bloc de gauche stocke des informations sur les fichiers de travail. Par défaut, l'onglet Source est ouvert. La principale source du projet est le fichier main.c. En fait, il n'y a aucun autre fichier de travail dans le groupe Fichiers source .



Le groupe Source générée contient des fonctions de bibliothèque. Il vaut mieux ne pas les modifier. Après chaque modification du «firmware» de l'UDB, ce groupe sera régénéré. Alors, où est la description du code pour UDB dans cette idylle? Pour le voir, vous devez basculer vers l'onglet Composants :



L'auteur du projet original a réalisé un ensemble de composants à deux niveaux. Au niveau supérieur se trouve le circuit NeoPixel_v1_2.cysch . Cela peut être vu à partir du schéma principal:



Le composant est le suivant:



La prise en charge logicielle de ce schéma sera discutée ultérieurement. En attendant, découvrez que lui-même est une unité DMA régulière et un certain symbole NeoPixDrv_v1 . Ce mystérieux bloc est décrit ci-dessus dans l'arborescence, qui découle de l'infobulle suivante:



UDB "Firmware"


Ouvrez ce composant (fichier avec l'extension .cyudb ). Le dessin ouvert est tout simplement énorme. Nous commençons à comprendre ce qui est quoi.



Contrairement à l'auteur du projet original, je considère la transmission de chaque bit de données sous la forme de trois parties égales (dans le temps):

  1. Partie de départ (toujours 1)
  2. Partie de données
  3. Arrêter la partie (toujours 0)

Avec cette approche, un grand nombre de compteurs n'est pas nécessaire (dans l'original, il y avait jusqu'à trois pièces, ce qui consommait une grande quantité de ressources). La durée de toutes les parties est la même et peut être réglée à l'aide d'un seul registre. Ainsi, le graphe de transition du firmware contient les états suivants:

État inactif . La machine y reste jusqu'à l'arrivée de nouvelles données dans FIFO.



D'après les vidéos de formation, il n'était pas tout à fait clair pour moi comment l'état de la machine est lié à l'ALU. Les auteurs utilisent la communication comme une évidence, mais moi, en tant que débutant, je ne pouvais pas la voir immédiatement. Jetons-y un coup d’œil en détail. La figure ci-dessus montre que l'état de veille est codé avec la valeur 1'b0. 3'b000 sera plus correct, mais l'éditeur refait tout de même. Les entrées du bloc Datapath sont décrites comme suit :



Si vous double-cliquez dessus, une version plus détaillée apparaîtra:



Cela signifie que le bit zéro de l'adresse de l'instruction ALU correspond au bit zéro de la variable qui définit l'état de la machine. Le premier est le premier, le second est le second. Si vous le souhaitez, toutes les variables et même les expressions peuvent être mises en correspondance avec les bits d'adresse de l'instruction ALU (dans la version d'origine, le deuxième bit de l'adresse de l'instruction ALU était mis en correspondance par une expression, de plus, il n'est pas utilisé explicitement dans la version actuelle, mais c'est très évident comme exemple de transport de cerveau, alors vous pouvez jeter un coup d'œil).

Alors. Avec les paramètres actuels des entrées, qui est le code d'état binaire de la machine, une telle instruction ALU est utilisée. Lorsque nous sommes en état inactif avec le code 000, une instruction nulle est utilisée. Le voici:



Je sais déjà de cette entrée que c'est un NOP banal. Mais vous pouvez double-cliquer dessus et lire la version complète:



Les NOP sont inscrits partout. Les registres ne sont remplis de rien.

Voyons maintenant quel type de drapeau mystérieux ! NoData , forçant la machine à quitter l'état inactif . Il s'agit de la sortie du bloc Datapath . Au total, jusqu'à six sorties peuvent être décrites. C'est juste que Datapath peut produire beaucoup plus de drapeaux, mais il n'y a pas suffisamment de ressources de trace pour tout le monde, nous devons donc choisir les six (ou moins) dont nous avons vraiment besoin. Voici la liste de la figure:



Si vous double-cliquez dessus, les détails seront révélés:



Voici la liste complète des drapeaux pouvant être affichés:



Après avoir sélectionné le drapeau requis, vous devez lui donner un nom. Désormais, le système a un drapeau. Comme vous pouvez le voir, l'indicateur NoData est le nom de l' état du bloc F0 de la chaîne (vide) . Autrement dit, un signe qu'il n'y a pas de données dans le tampon d'entrée. Ah ! NoData , respectivement, son inversion. Signe de disponibilité des données. Dès que les données entrent dans le FIFO (par programme ou en utilisant DMA), le drapeau sera effacé (et son inversion armé), et au prochain cycle d'horloge, l'automate quittera l'état inactif et entrera dans l'état GetData .



Comme vous pouvez le voir, l'automate sortira inconditionnellement de cet état après y avoir passé exactement un cycle d'horloge. Aucune action n'est indiquée sur le graphique de transition pour cet état. Mais vous devriez toujours regarder ce que fera ALU. Le code de statut est 1'b1, c'est-à-dire 3'b001. Nous regardons l'adresse correspondante dans ALU:



Il y a quelque chose. N'ayant aucune expérience de la lecture de ce qui est écrit ici, ouvrez-le en double-cliquant sur la cellule correspondante:



Il s'ensuit que l'ALU lui-même n'effectue toujours aucune action. Mais le contenu de FIFO0, c'est-à-dire les données provenant du programme ou du bloc DMA, seront placés dans le registre A0. Pour l'avenir, je dirai que A0 est utilisé comme registre à décalage, à partir duquel l'octet sortira sous forme série. Le registre A1 place la valeur du registre D1. En général, tous les registres D sont généralement remplis par le logiciel avant que le matériel ne soit activé. Ensuite, lors de l'examen de l'API, nous verrons que le nombre de tics d'horloge est mis dans ce registre, qui définit la durée du troisième bit. Alors. En A0, la valeur décalée a chuté et en A1, la durée de la partie de début du bit. Et au prochain temps, la machine entrera certainement dans l'état Constant1 .



Comme le nom de l'état l'indique, la constante 1 est générée ici. Regardons la documentation de la LED. Voici comment l'unité doit être transférée:



Et le voici - zéro:



J'ai ajouté des lignes rouges. Si nous supposons que les durées des tiers sont égales, alors les exigences pour la durée des impulsions (données dans la même documentation) sont remplies. C'est-à-dire que toute impulsion se compose d'une unité de démarrage, d'un bit de données et d'un zéro d'arrêt. En fait, l'unité de démarrage est transmise lorsque la machine est en état Constant1 .

Dans cet état, la machine verrouille l'unité dans sa gâchette interne. Le nom du déclencheur est CurrentBit . Dans le projet d'origine, il s'agissait généralement d'un déclencheur qui définit l'état de l'automate auxiliaire. J'ai décidé que cette machine ne ferait que confondre tout le monde, alors j'ai juste commencé un déclencheur. Il n'est décrit nulle part. Mais si vous entrez les propriétés d'état, l'enregistrement suivant est visible dans le tableau:



Et sous l'état sur le graphique, il y a un tel texte:



Ne soyez pas alarmé par le symbole égal. Ce sont les fonctionnalités de l'éditeur. Dans le code Verilog résultant (généré automatiquement par le même système), il y aura une flèche:

 Constant1 : begin CurrentBit <= (1); if (( CycleTimeout ) == 1'b1) begin MainState <= Setup1 ; end end 

La valeur verrouillée dans ce déclencheur est la sortie de l'ensemble de notre bloc:



Autrement dit, lorsque la machine est entrée dans l'état de Constant1 , la sortie du bloc que nous développons en obtient un. Voyons maintenant comment l'ALU est programmé pour l'adresse 3'b010:



Nous révélons cet élément:



L'unité 1 est soustraite du registre A1. La valeur de sortie d'ALU tombe dans le registre A1. Ci-dessus, nous avons considéré que A1 est un compteur d'horloge utilisé pour définir la durée de l'impulsion de sortie. Permettez-moi de vous rappeler qu'il a démarré à partir de D1 à la dernière étape.
Quelle est la condition pour sortir d'un État? CycleTimeOut . Il est décrit parmi les sorties comme suit:



Donc, nous réunissons la logique. Dans l'état précédent, le contenu du registre D1 préalablement rempli par le programme est tombé dans le registre A1. À cette étape, la machine traduit le déclencheur CurrentBit en un, et en ALU, le registre A1 diminue à chaque cycle d'horloge. Lorsque A1 devient nul, le drapeau est automatiquement levé, auquel l'auteur a donné le nom CycleTimeout , à la suite de quoi la machine passe à l'état Setup1 .

L'état Setup1 prépare les données pour transmettre l'impulsion utile.



Nous regardons l'instruction ALU à 3'b011. Je vais l'ouvrir tout de suite:



Il semblerait qu'ALU n'ait aucune action. Opération NOP. Et la sortie ALU ne va nulle part. Mais ce n'est pas le cas. Une action extrêmement importante est le transfert de données dans ALU. Le fait est que le bit de retenue parmi les sorties est connecté à notre chaîne ShiftOut :



Et à la suite de cette opération de décalage, la valeur décalée elle-même n'ira nulle part, mais la chaîne ShiftOut prendra la valeur du bit le plus significatif du registre A0. Autrement dit, les données qui devraient être transmises. Sous l'état du graphique, on peut voir que cette valeur, qui a laissé l'ALU dans la chaîne ShiftOut , sera verrouillée dans le déclencheur CurrentBit . Permettez-moi de montrer à nouveau le dessin afin de ne pas rembobiner l'article:



La transmission de la deuxième partie du bit commence - la valeur immédiate est 0 ou 1.

Nous revenons aux instructions pour ALU. En plus de ce qui a déjà été dit, il est clair que le contenu du registre D1 sera à nouveau mis dans le registre A1 afin de mesurer à nouveau la durée du deuxième tiers de l'impulsion.

L'état DataStage est très similaire à l'état Constant1 . L'automate soustrait simplement un de A1 et entre dans l'état suivant lorsqu'il atteint zéro. Permettez-moi même de le montrer comme ceci:



et comme ça:



Vient ensuite l'état de Setup2 , dont nous connaissons déjà l'essence.



Dans cet état, le déclencheur CurrentBit est remis à zéro (puisque le troisième tiers de l'impulsion sera transmis, la partie stop, et il est toujours nul). ALU charge le contenu de D1 dans A1. Vous pouvez même le voir dans une courte note avec votre œil exercé:



L'état de Constant0 est complètement identique aux états de Constant1 et DataStage . Soustrayez l'unité de A1. Lorsque la valeur atteint zéro, quittez à l'état ShiftData :





L'état de ShiftData est plus complexe. Dans les instructions correspondantes pour ALU, les actions suivantes sont effectuées:



Le registre A0 est décalé de 1 bit et les résultats sont replacés dans A0. En A1, le contenu de D1 est à nouveau mis afin de commencer à mesurer le tiers de départ pour le bit de données suivant.

Il vaut mieux considérer les flèches de sortie en tenant compte des priorités, pour lesquelles on double-clique sur l'état ShiftData .



Si ce n'est pas le dernier bit qui est transmis (sur la façon dont ce drapeau est formé, un peu plus bas), alors nous en transférons un pour le bit suivant de l'octet courant.

Si le dernier bit est transmis et qu'il n'y a pas de données dans FIFO, on passe à l'état inactif.

Enfin, si le dernier bit est transmis, mais qu'il y a des données dans le FIFO, on passe à la sélection et à la transmission du prochain octet.

Maintenant sur le compteur de bits. Il n'y a que deux batteries en ALU: A0 et A1. Ils sont déjà occupés par le registre à décalage et le compteur de retard, respectivement. Par conséquent, un compteur de bits est utilisé en externe.



Double-cliquez dessus:



La valeur au démarrage est de six. Il est chargé à l'aide de l'indicateur LoadCounter décrit dans la section variable:



Autrement dit, lorsque le prochain octet de données est pris, cette constante est chargée en cours de route.

Lorsque la machine entre dans l'état ShiftData , le compteur diminue la valeur. Lorsqu'il atteint zéro, la sortie TerminalCount est connectée, connectée au circuit de notre graine FinalBit . C'est ce circuit qui définit si la machine transférera le bit suivant de l'octet en cours ou transférera un nouvel octet (enfin, ou attendra un nouveau paquet de données).

En fait, tout vient de la logique. Comment le signal SpaceForData est généré , qui définit l'état de la sortie Hungry (informant l'unité DMA qu'il est possible de transmettre les données suivantes), les lecteurs sont invités à suivre indépendamment.

Support logiciel


L'auteur du projet d'origine a choisi de prendre en charge le logiciel pour l'ensemble du système dans le bloc qui décrit la solution intégrée. Permettez-moi de vous rappeler que nous parlons de ce bloc:



À partir de ce niveau, il y a un contrôle sur l'unité de bibliothèque DMA et sur toutes les parties incluses dans la partie UDB. Pour implémenter l'API, l'auteur de l'original a ajouté l'en-tête et les fichiers programme:



Le format du corps de ces fichiers vous rend triste. Tout le blâme est l'amour des développeurs PSoC Designer pour les "purs". D'où les macros terribles et les noms de kilomètres. L'organisation des classes en C ++ serait utile ici. Au moins, nous avons vérifié cela lors de la mise en œuvre de notre RTOS MAX: cela s'est avéré magnifique et pratique. Mais ici, vous pouvez argumenter beaucoup, mais vous devrez utiliser ce que nous avons laissé d'en haut. Je vais seulement montrer brièvement à quoi ressemble la fonction API contenant ces macros:

 volatile void* `$INSTANCE_NAME`_Start(unsigned int nNumberOfNeopixels, void* pBuffer, double fSpeedMHz) { //work out cycles required at specified clock speed... `$INSTANCE_NAME`_g_pFrameBuffer = NULL; if((0.3/(1.0/(fSpeedMHz))) > 255) return NULL; unsigned char fCyclesOn = (unsigned char)(0.35/(1.0/(fSpeedMHz))); `$INSTANCE_NAME`_g_nFrameBufferSize = nNumberOfNeopixels*3; //Configure for 19.2 MHz operation `$INSTANCE_NAME`_Neo_BITCNT_Start(); //Counts bits in a byte //Sets bitrate frequency in number of clocks. Must be larger than largest of above two counter periods CY_SET_REG8(`$INSTANCE_NAME`_Neo_DPTH_D1_PTR, fCyclesOn+1); //Setup a DMA channel `$INSTANCE_NAME`_g_nDMA_Chan = `$INSTANCE_NAME`_DMA_DmaInitialize(`$INSTANCE_NAME`_DMA_BYTES_PER_BURST, `$INSTANCE_NAME`_DMA_REQUEST_PER_BURST, HI16(`$INSTANCE_NAME`_DMA_SRC_BASE), HI16(`$INSTANCE_NAME`_DMA_DST_BASE)); if(pBuffer == NULL) ... 

Ces règles du jeu doivent être acceptées. Vous savez maintenant d'où vous inspirer lors du développement de vos fonctions (il est préférable de le faire dans le projet d'origine). Et je préfère parler des détails, en prenant l'option déjà traitée par le générateur.

Après avoir généré le code (décrit ci-dessous), ce fichier sera stocké ici:



Et la vue sera déjà parfaitement lisible. Jusqu'à présent, il existe deux fonctions. Le premier initialise le système, le second démarre le transfert des données du buffer vers la ligne LED.

L'initialisation affecte toutes les parties du système. Il y a initialisation du compteur à sept bits, qui fait partie du système UDB:

  NP_Neo_BITCNT_Start(); //Counts bits in a byte 

Il y a un calcul constant qui doit être chargé dans le registre D1 (je rappelle qu'il fixe la durée de chacun des troisièmes bits):

 unsigned char fCyclesOn = (unsigned char)(0.35/(1.0/(fSpeedMHz))); CY_SET_REG8(NP_Neo_DPTH_D1_PTR, fCyclesOn+1); 

La configuration d'un bloc DMA occupe la majeure partie de cette fonction. Le tampon est utilisé comme source et le FIFO0 du bloc UDB est utilisé comme récepteur (NP_Neo_DPTH_F0_PTR dans l'enregistrement kilométrique). L'auteur avait une partie de ce paramètre dans la fonction de transfert de données. Mais, à mon avis, faire tous les calculs pour le bien de chaque transmission est trop de gaspillage. Surtout quand on considère que l'une des actions à l'intérieur de la fonction semble très, très volumineuse.

 //work out cycles required at specified clock speed... NP_g_pFrameBuffer = NULL; NP_g_nFrameBufferSize = nNumberOfNeopixels*3; //Setup a DMA channel NP_g_nDMA_Chan = NP_DMA_DmaInitialize(NP_DMA_BYTES_PER_BURST, NP_DMA_REQUEST_PER_BURST, HI16(NP_DMA_SRC_BASE), HI16(NP_DMA_DST_BASE)); ... NP_g_nDMA_TD = CyDmaTdAllocate(); CyDmaTdSetConfiguration(NP_g_nDMA_TD, NP_g_nFrameBufferSize, CY_DMA_DISABLE_TD, TD_INC_SRC_ADR | TD_AUTO_EXEC_NEXT); CyDmaTdSetAddress(NP_g_nDMA_TD, LO16((uint32)NP_g_pFrameBuffer), LO16((uint32)NP_Neo_DPTH_F0_PTR)); CyDmaChSetInitialTd(NP_g_nDMA_Chan, NP_g_nDMA_TD); 

La deuxième fonction dans le contexte de la première est le sommet du laconicisme. C'est juste que le premier est appelé au stade de l'initialisation, lorsque les exigences de performances sont assez libres. Pendant le fonctionnement, il vaut mieux ne pas gaspiller les cycles du processeur sur quoi que ce soit de superflu:

 void NP_Update() { if(NP_g_pFrameBuffer) { CyDmaChEnable(NP_g_nDMA_Chan, 1); } } 

Il n'y a clairement pas assez de fonctionnalités pour travailler avec plusieurs tampons (pour fournir une double mise en mémoire tampon), mais en général, une discussion sur les fonctionnalités de l'API dépasse le cadre de l'article. Maintenant, l'essentiel est de montrer comment ajouter un support logiciel au firmware développé. Maintenant, nous savons comment le faire.

Génération de projets


Donc, toute la partie firmware est prête, l'API est ajoutée, que faire ensuite? Sélectionnez l'élément de menu Build-> Generate Application .



Si tout se passe bien, vous pouvez ouvrir l'onglet Résultats et voir le fichier avec l'extension rpt .



Il montre combien de ressources système ont été consacrées à la mise en œuvre du micrologiciel.





Quand je compare les résultats avec ceux qui étaient dans le projet original, mon âme se réchauffe.

Allez maintenant dans l'onglet Source et commencez à travailler avec la partie logicielle. Mais cela est déjà trivial et ne nécessite aucune explication particulière.



Conclusion


J'espère que grâce à cet exemple, les lecteurs ont appris quelque chose de nouveau et d'intéressant sur le travail pratique avec les blocs UDB. J'ai essayé de me concentrer sur une tâche spécifique (contrôle LED), ainsi que sur la méthodologie de conception, car je devais comprendre certains aspects qui étaient évidents pour les spécialistes. J'ai essayé de les marquer pendant que les souvenirs de la quête étaient frais. En ce qui concerne le problème résolu, les chronogrammes pour moi se sont avérés ne pas être aussi idéaux que ceux de l'auteur du développement original, mais ils s'intègrent parfaitement dans les tolérances définies dans la documentation pour les LED, et les ressources du système étaient nettement inférieures.

En fait, ce n'est qu'une partie des informations non standard trouvées. En particulier, de la plupart des matériaux, il peut sembler que l'UDB ne fonctionne bien qu'avec des données série, mais ce n'est pas le cas. Note d'application trouvée, qui montre brièvement comment vous pouvez conduire des données et en parallèle. Nous pourrions envisager des exemples spécifiques basés sur ces informations (bien qu'il soit impossible de faire de l'ombre au FX2LP, un autre contrôleur de Cypress: PSoC a une vitesse de bus USB inférieure).

Ma tête tourne des idées sur la façon de résoudre le problème du «flashage» d'une imprimante 3D, qui me tourmente depuis longtemps. Là, les interruptions au service des moteurs pas à pas ne dévorent qu'un pourcentage insensé du temps CPU. En général, j'ai beaucoup parlé des interruptions et du temps processeur dans un article sur le RTOS MAX . Il est estimé que pour l'entretien des moteurs pas à pas, il est possible de prendre toutes les huttes temporaires complètement à l'UDB, laissant au processeur une tâche purement informatique sans craindre qu'il n'aura pas le temps de le faire dans un créneau horaire dédié.

Mais ces choses ne peuvent être motivées que si le sujet est intéressant.

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


All Articles