Cours de formation. Connexion encodeur. Structure typique d'un programme simple. Générateur sur AVR. Nous interfaçons l'encodeur et le microcontrôleur

Apprenez à utiliser un encodeur rotatif incrémental dans un projet Arduino.

Un encodeur rotatif est un dispositif électromécanique qui convertit le mouvement de rotation en informations numériques ou analogiques. Il ressemble beaucoup à un potentiomètre, mais peut être tourné indéfiniment, dans le sens des aiguilles d'une montre ou dans le sens inverse. Il existe plusieurs types de codeurs rotatifs. Les deux principaux types sont les codeurs absolus et relatifs (incrémentaux). Alors qu'un codeur absolu génère une valeur proportionnelle à l'angle actuel de l'arbre, un codeur incrémental génère le pas et la direction de l'arbre. Les encodeurs rotatifs deviennent de plus en plus populaires dans l'électronique grand public, notamment comme boutons de commande, en plus d'applications dans de nombreux autres domaines. Ils remplacent les potentiomètres et les boutons de navigation lorsqu'une navigation, une configuration, une saisie de données et une sélection de menu rapides sont nécessaires. Certains encodeurs incluent également un bouton intégré qui crée une entrée supplémentaire sur le processeur pouvant être utilisée comme une autre commande utilisateur dans l'interface de contrôle. Dans l'image ci-dessous, vous pouvez voir un encodeur rotatif incrémental typique avec un bouton d'alimentation.

Dans cet article, nous allons vous montrer comment utiliser un encodeur rotatif incrémental dans un projet Arduino. Nous expliquerons comment gérer le rebond de contact et interpréter les signaux du codeur dans un programme de microcontrôleur à l'aide d'interruptions.

Signal de sortie en quadrature du codeur incrémental

Un codeur rotatif incrémental produit deux signaux de sortie lorsque l'arbre tourne, également appelé sortie en quadrature. Selon la direction, un signal est en avance sur l'autre. Ci-dessous, vous pouvez voir la forme d'onde de sortie d'un codeur rotatif incrémental et la séquence de bits attendue.

Comme le montre la figure, les deux sorties sont initialement dans l'état logique. Lorsque l'arbre du codeur commence à tourner dans le sens des aiguilles d'une montre, l'état de la sortie A tombe d'abord à zéro logique, puis la sortie B suit avec un décalage. Lors d'une rotation dans le sens inverse des aiguilles d'une montre, l'inverse se produit. Les intervalles de temps sur le diagramme des signaux dépendent de la vitesse de rotation, mais le retard du signal est dans tous les cas garanti. Sur la base de cette caractéristique d'un codeur rotatif incrémental, nous allons écrire un programme pour Arduino.

Filtrage du rebond de contact d'un codeur mécanique

Les codeurs mécaniques ont des commutateurs intégrés qui génèrent un signal de sortie en quadrature pendant la rotation.

Lorsqu'il s'agit de signaux de codeur, le principal problème est le rebond des contacts. Cela entraîne une détermination erronée du sens de rotation et de l’ampleur de la rotation de l’arbre du codeur et rend problématique l’utilisation des codeurs. Nous pouvons nous débarrasser du rebond de contact en le filtrant dans un programme ou en utilisant des schémas de filtrage supplémentaires.

Le filtrage du bruit dans le logiciel du microcontrôleur est une option de filtrage, mais elle présente certains inconvénients. Vous devez écrire du code plus complexe pour gérer le bruit. Le filtrage prendra du temps de traitement et introduira des retards dans le thread principal du programme. Vous devrez peut-être définir des minuteries pour ignorer les intervalles de rebond des contacts. Au final, vous ne pourrez peut-être pas obtenir un résultat satisfaisant et fiable.

Le filtrage du bruit avec du matériel supplémentaire est plus simple et arrête le bruit à sa source. Vous aurez besoin d'un filtre RC de premier ordre. Dans l'image ci-dessous, vous pouvez voir à quoi ressemble le signal après avoir utilisé un filtre RC.

Le filtre RC ralentit le temps de chute et le temps de montée et assure l'anti-rebond matériel. Lors de la sélection d'une paire résistance-condensateur, vous devez tenir compte de la vitesse maximale. Sinon, la réponse attendue du codeur sera filtrée.

Application simple

Nous allons créer une application démontrant comment utiliser un encodeur rotatif dans un projet Arduino. Nous utiliserons l'encodeur pour la navigation, la saisie des données et la sélection. Vous trouverez ci-dessous un diagramme schématique de l'application.

Le circuit est basé sur la carte Arduino Uno. L'écran LCD du Nokia 5110 est utilisé pour l'interface graphique. Un encodeur rotatif mécanique avec un bouton et des filtres RC est ajouté comme commandes.

Nous développerons un menu logiciel simple dans lequel nous démontrerons le fonctionnement d'un encodeur rotatif.

Traitement des signaux d'encodeur à l'aide d'interruptions

Les signaux du codeur doivent être détectés et interprétés dans le programme le plus rapidement possible afin de ne pas bloquer le déroulement principal du programme. Nous pouvons détecter les signaux en interrogeant la boucle principale ou en utilisant des interruptions. L'interrogation n'est pas efficace car vous devez réserver du temps et des ressources dans la boucle principale, ce qui introduit des retards supplémentaires. L'utilisation d'interruptions est une solution plus rapide et plus rentable. Nous allons vous montrer comment utiliser les interruptions pour traiter les signaux du codeur.

L'Atmega328 dispose de deux types d'interruptions qui peuvent être utilisées à cette fin ; interruption externe et interruption de changement de sortie. Les broches INT0 et INT1 sont affectées à une interruption externe, et PCINT0 - PCIN15 sont affectées à une interruption lorsqu'une broche change d'état. Une interruption externe peut détecter si le signal d'entrée diminue ou augmente et peut être déclenchée dans l'une des conditions suivantes : hausse, baisse ou commutation. Il existe beaucoup plus de ressources matérielles disponibles pour l'interruption de changement de broche, mais elle ne peut pas détecter les fronts montants et descendants, et elle est appelée lorsqu'un changement d'état logique (bascule) se produit sur une broche.

Pour utiliser une interruption de changement de broche, connectez les sorties de virage A et B de l'encodeur à A1 et A2, et la sortie du bouton à la broche A0 de l'Arduino, comme indiqué dans le schéma de circuit. Réglez les broches A0, A1 et A2 en mode d'entrée et activez leurs résistances de rappel internes. Activez l'interruption de changement de broche dans le registre PCICR et activez les interruptions pour les broches A0, A1 et A2 dans le registre PCMS1. Si un changement d'état logique est détecté sur l'une de ces entrées, ISR (PCINT1_vect) (Pin State Interrupt) sera appelé.

Étant donné que l'interruption de changement de broche est appelée pour tout changement logique, nous devons surveiller les deux signaux (A et B) et détecter la rotation lorsque la séquence attendue est reçue. Comme le montre le diagramme des signaux, un mouvement dans le sens des aiguilles d'une montre génère A = …0011… et B = …1001… . Lorsque nous écrivons les deux signaux dans les octets seqA et seqB , en décalant la dernière lecture vers la droite, nous pouvons comparer ces valeurs et déterminer le nouveau pas de rotation.

Vous pouvez voir une partie du code qui inclut la fonction d'interruption d'initialisation et de changement de broche.

Void setup() ( pinMode(A0, INPUT); pinMode(A1, INPUT); pinMode(A2, INPUT); // Activer les résistances de rappel internes digitalWrite(A0, HIGH); digitalWrite(A1, HIGH); digitalWrite( A2, HIGH); PCICR = 0b00000010 ; // 1. PCIE1 : Activer l'interruption de changement d'état 1 PCMSK1 = 0b00000111 ; // Activer l'interruption de changement d'état pour A0, A1, A2) void loop() ( // Boucle principale) ISR (PCINT1_vect ) ( // Si l'interruption est provoquée par un bouton if (!digitalRead(A0)) ( button = true; ) // Si l'interruption est provoquée par des signaux de codeur else ( // Lire les signaux A et B boolean A_val = digitalRead( A1); boolean B_val = digitalRead (A2); // Écrit les signaux A et B dans des séquences séparées seqA<<= 1; seqA |= A_val; seqB <<= 1; seqB |= B_val; // Маскировать четыре старших бита seqA &= 0b00001111; seqB &= 0b00001111; // Сравнить запсанную последовательность с ожидаемой последовательностью if (seqA == 0b00001001 && seqB == 0b00000011) { cnt1++; left = true; } if (seqA == 0b00000011 && seqB == 0b00001001) { cnt2++; right = true; } } }

L'utilisation d'une interruption externe simplifie le processus, mais comme cette interruption n'a que deux broches qui lui sont attribuées, vous ne pourrez pas l'utiliser à d'autres fins si vous l'occupez avec un encodeur. Pour utiliser une interruption externe, vous devez définir les broches 2 (INT0) et 3 (INT1) en mode d'entrée et activer leurs résistances de rappel internes. Sélectionnez ensuite l'option de front descendant pour déclencher les deux interruptions dans le registre EICRA. Activez les interruptions externes dans le registre EIMSK. Lorsque l'arbre du codeur commence à tourner, le signal principal tombe d'abord à zéro logique et le deuxième signal reste à un zéro logique pendant un certain temps. Par conséquent, nous devons déterminer lequel des signaux est dans l’état logique pendant l’interruption. Une fois que le signal principal est tombé à zéro logique, après un certain temps, le deuxième signal tombera également à zéro logique, provoquant une autre interruption. Mais cette fois et l’autre signal (principal) sera faible, ce qui signifie que ce n’est pas le début de la rotation, donc nous l’ignorons.

Ci-dessous, vous pouvez voir une partie du code qui inclut la fonction d'initialisation et de gestion des interruptions externes.

Void setup() ( pinMode(2, INPUT); pinMode(3, INPUT); // Activer les résistances de rappel internes digitalWrite(2, HIGH); digitalWrite(3, HIGH); EICRA = 0b00001010; // Sélectionnez l'appel sur front descendant EIMSK = 0b00000011; // Activer l'interruption externe ) void loop() ( // Boucle principale ) ISR (INT0_vect) ( // Si le deuxième signal est à l'état logique, alors il s'agit d'une nouvelle rotation if (digitalRead( 3) == HIGH) ( left = true; ) ) ISR (INT1_vect) ( // Si le deuxième signal est dans l'état logique, alors il s'agit d'une nouvelle rotation if (digitalRead(2) == HIGH) ( right = vrai; ) )

Le code complet du croquis Arduino, y compris la boucle principale, est ci-dessous :

#inclure #inclure #inclure octet volatile seqA = 0 ; octet volatile seqB = 0 ; octet volatile cnt1 = 0 ; octet volatile cnt2 = 0 ; booléen volatile right = false ; volatile booléen gauche = faux ; bouton booléen volatile = faux ; rétroéclairage booléen = vrai ; élément de menu octet = 1 ; page d'octets = 1 ; Affichage Adafruit_PCD8544 = Adafruit_PCD8544(13, 12,11, 8, 10) ; void setup() ( pinMode(A0, INPUT); pinMode(A1, INPUT); pinMode(A2, INPUT); // Activer les résistances de rappel internes digitalWrite(A0, HIGH); digitalWrite(A1, HIGH); digitalWrite( A2, HIGH); // Activer le rétroéclairage de l'écran LCD pinMode (9, OUTPUT); digitalWrite (9, HIGH); // 1. PCIE1 : Activer l'interruption de changement d'état 1 = 0b00000111 ; (2); // Définir l'orientation LDC display.begin(60); // Définir le contraste de l'écran LCD display.clearDisplay(); // Effacer l'affichage display.display(/Appliquer les modifications sei(); () ( // Créer des pages de menu if (page==1) ( display.setTextSize(1); display.clearDisplay(); display.setTextColor(BLACK, WHITE); display. setCursor(15, 0); display.print ("MENU PRINCIPAL"); display.drawFastHLine(0,10,83,BLACK); display.setCursor(0, 15); if (menuitem==1) ( display.setTextColor (BLANC, NOIR); else ( display. setTextColor (NOIR, BLANC);<<= 1; seqA |= A_val; seqB <<= 1; seqB |= B_val; // Маскировать четыре старших бита seqA &= 0b00001111; seqB &= 0b00001111; // Сравнить запсанную последовательность с ожидаемой последовательностью if (seqA == 0b00001001 && seqB == 0b00000011) { cnt1++; left = true; } if (seqA == 0b00000011 && seqB == 0b00001001) { cnt2++; right = true; } } }

Vous pouvez voir l'encodeur en action dans la vidéo ci-dessous.

Cet article parlera de l'encodeur et de la façon de le connecter au microcontrôleur. On le trouve dans des appareils tels que les systèmes audio, les machines à laver, les fours à micro-ondes et un certain nombre d’appareils modernes. Par exemple, dans les systèmes audio, des encodeurs et des microcontrôleurs sont utilisés pour contrôler le volume. Mais bon, assez d'eau, passons aux choses sérieuses.

Un codeur, ou comme on l'appelle également capteur d'angle de rotation, est un dispositif électromécanique qui convertit la position d'un angle d'arbre en un signal électrique. Les encodeurs sont divisés en 2 types : absolus et incrémentaux.

Dans les codeurs incrémentaux, lorsque l'arbre tourne, des impulsions sont générées, le nombre de ces impulsions est proportionnel à l'angle de rotation de l'arbre. Si vous comptez ces impulsions, vous pouvez connaître l'angle de rotation de l'arbre du codeur. Si la poignée de l'encodeur est au repos, aucune impulsion n'est générée. De tels encodeurs sont largement utilisés dans les systèmes audio et les commandes industrielles.

Les codeurs absolus ont un principe de fonctionnement complètement différent, basé sur l'émission d'un code unique pour chaque position de l'arbre. La formation d'impulsions se produit lorsque l'arbre tourne et lorsqu'il est au repos. De plus, les informations sur la position actuelle de l'arbre seront conservées même après l'arrêt de l'alimentation en tension.

Dans notre exemple, nous connecterons un codeur incrémental à un microcontrôleur. Encodeur PEC12 422OF SOO24 qui a 24 impulsions par tour.

L'encodeur a 5 broches, 3 d'entre elles sont les broches de l'encodeur lui-même et les deux autres sont le bouton. Les broches du codeur ont une broche commune et les deux autres broches de signal. Le schéma de connexion n'est pas différent du schéma de connexion d'un bouton ordinaire. Les broches de signal sont connectées aux ports E/S du microcontrôleur. Et la borne commune, qui est reliée à la masse au milieu. Pour vous protéger contre les rebonds de contact, vous pouvez ajouter des condensateurs d'une capacité de plusieurs nF. Nous configurons les broches auxquelles nous avons connecté l'encodeur dans le programme comme entrées et activons les résistances de rappel qui peuvent être connectées ;

Schéma de connexion de l'encodeur au microcontrôleur

Le principe de fonctionnement de l'encodeur est basé sur la fermeture et l'ouverture des contacts ; lorsque personne ne tourne le bouton, il y en a un logique à l'entrée du MK. Lorsque le bouton commence à être tourné, deux impulsions rectangulaires apparaissent, décalées l'une par rapport à l'autre. La direction dans laquelle nous tournons déterminera quel signal est en avance.

Mais comme il y a plusieurs contacts dans chacun d’eux, l’image ressemblera à ceci.

Algorithme du programme du microcontrôleur

À un certain intervalle, l'appel à la fonction d'interrogation du codeur commence. Cette fonction lit les niveaux logiques présents sur les broches du microcontrôleur et écrit cette valeur dans une variable temporaire. À l'intérieur de la fonction d'interrogation de l'encodeur, il existe une autre variable statique qui est enregistrée lors de la sortie de cette fonction ; elle stocke la séquence des valeurs précédentes ; La dernière valeur enregistrée est extraite de cette variable et comparée à la valeur actuelle pour déterminer s'il y a eu des changements. Si ces valeurs sont égales, la fonction se termine, et si elles diffèrent, alors la valeur de la variable statique est décalée de 2 bits vers la gauche et une nouvelle valeur (actuelle) est écrite dans l'espace « libre ».

Il s'avère que lorsque l'arbre du codeur tourne, une nouvelle valeur sera constamment écrite dans la variable temporaire et une séquence de code répétitive sera obtenue. Si on le fait tourner vers la droite : 11100001, et si on le fait tourner vers la gauche, alors 11010010. En utilisant ces valeurs, vous pouvez comprendre dans quel sens l'arbre tourne.

L'archive contient 2 fichiers encoder.h et encoder.c. Dans un premier temps, vous devez définir le port et le nombre de broches auxquelles la connexion est établie ; ce sont les variables LEFT_SPIN et RIGHT_SPIN. Le fichier "c" contient l'implémentation des fonctions.

Codeur incrémental on dirait potentiomètre, mais contrairement à un potentiomètre, il n'a pas de positions extrêmes ; il peut tourner dans les deux sens sur un nombre illimité de tours. Il convient également de noter que le codeur incrémental ne tourne pas aussi doucement qu'un potentiomètre, mais par étapes. On le voit sur l'autoradio, oscilloscope, centre de musique, machine à laver et autres équipements où le réglage de certains paramètres est effectué dans de larges limites. Bien entendu, les paramètres peuvent également être modifiés à l'aide de boutons, par exemple, afin de rendre la musique plus forte de 20 valeurs, lors du contrôle du bouton, vous devez appuyer dessus 20 fois, et lors du contrôle de l'encodeur, le tourner sur un certain angle, en fonction de l'algorithme de traitement.

Codeur incrémental se compose de deux contacts, dont l'ordre de fermeture dépend du sens de rotation.


Essentiellement un codeur incrémental convertit la rotation de l'arbre en impulsions électriques, contenant des informations sur le sens de rotation.

Assemblons le circuit de test montré dans l'image ci-dessus et connectons-le aux broches A et B. oscilloscope, résistances de rappel - 4,7K.
Tournons l'encodeur dans le sens des aiguilles d'une montre.


Maintenant dans le sens inverse des aiguilles d'une montre.


Les oscillogrammes montrent qu'en fonction du sens de rotation, l'ordre de fermeture des contacts change. Mais les façades ne sont pas toujours aussi belles.


Les contacts étant mécaniques, ils sont sujets à des vibrations, c'est-à-dire que lorsqu'ils sont fermés en raison de l'élasticité des matériaux, de multiples courts-circuits et ouvertures incontrôlés se produisent, comme le montre l'oscillogramme ci-dessus.

Il existe deux manières de gérer le bavardage : d'abord consiste à ajouter des condensateurs et des résistances, comme le montre l'image ci-dessous.


Le broutage étant un phénomène à court terme, il est facilement éteint par un condensateur.


L'oscillogramme montre qu'après l'installation des condensateurs, les fronts sont devenus moins raides et les vibrations ont disparu.

Deuxième façon- le logiciel et ici tout dépend de la mise en œuvre du polling des sorties codeur. Si L'état du codeur est surveillé à l'aide d'interruptions externes, puis après le déclenchement de l'interruption, il est nécessaire d'effectuer un délai de 20 à 30 millisecondes, pendant lequel le MK ne répondra pas à un changement de l'état de sortie, c'est-à-dire qu'il ne ressentira pas de rebond. Si l'interrogation des sorties du codeur est implémentée à l'aide d'une minuterie, alors l'intervalle entre les sondages doit être supérieur à la durée du bavardage, les mêmes 20 à 30 millisecondes.

Jetons un coup d'oeil méthodes de traitement des données provenant du codeur.
La première méthode consiste à connecter l'une des branches du codeur à la sortie d'interruptions externes et à la configurer pour qu'elle s'interrompe sur un front descendant. Dans l'interruption, on vérifie l'état de l'autre jambe et s'il est nul, alors la rotation se fait dans un sens, sinon dans l'autre. Vous trouverez ci-dessous le code qui implémente cette méthode pour AVR.
#inclure ISR(INT2_vect) ( si (PINB & 0X02) ( PORTB |= (1<<0); } else { PORTB &= ~(1<<0); } //антидребезг _delay_ms(20); //сбрасываем флаг прерывания вызванный дребезгом GIFR = (1<Lorsque l'encodeur est tourné dans un sens, la LED s'allume, lorsqu'il est tourné dans l'autre, il s'éteint.

La deuxième méthode est comparer l'état actuel et le précédent. Exprimons les niveaux logiques d'une séquence d'impulsions sous forme de zéros et de uns.


Nous obtenons alors un nombre fini d’états de codeur. Le premier chiffre est le niveau logique de la première sortie du codeur, le second est le niveau logique de la deuxième sortie.

Supposons que le dernier état dans lequel se trouvait l'encodeur était égal à trois, si l'état suivant est égal à un, alors il tourne dans un sens, s'il y en a deux, alors dans l'autre. Il s'avère que vous pouvez enregistrer la transition d'un état à un autre et déterminer le sens de rotation, mais la mise en œuvre la plus simple consiste à passer de 11 à 01 et 10. Vous trouverez ci-dessous le code qui implémente l'algorithme décrit pour AVR,
#définir F_CPU 8000000UL #inclure #inclure uint8_t dernier_état = 0 ; ISR(TIMER0_COMP_vect) ( //les deux broches de l'encodeur sont connectées aux broches 2 et 3 du port B //lire leur état uint8_t current_state = (PINB & 0x06)>>1; // prendre en compte la transition uniquement si l'état précédent est 11 //et s'il n'est pas égal au nouveau if ((last_state == 3) && (last_state != current_state)) ( //si le nouvel état est 01 - allumez la LED if(current_state == 1) ( PORTB |= 0x01; ) //si le nouvel état est 10 - éteignez la LED if(current_state == 2) ( PORTB &= ~0x01; ) ) //à la sortie de l'interruption, l'état actuel devient le précédent last_state = current_state; ) int main(void) ( //deux entrées pour connecter l'encodeur DDRB &= ~ 0x06; //connecter les entrées au PORTB d'alimentation |= 0x06; //sortie pour connecter la LED DDRB |= 0x01; // régler la minuterie en mode réinitialisation TCCR0=(1<C'est tout.
J'ai acheté l'encodeur

Le principe de fonctionnement, le schéma de connexion et le code source de la bibliothèque pour travailler avec un codeur incrémental ont déjà été abordés par moi dans l'un des articles. Aujourd'hui, nous allons parler de l'application pratique de l'encodeur. À titre d'exemple, j'ai choisi un programme générateur d'ondes carrées avec une plage de fréquences de fonctionnement de 1 à 100 Hz. Le plan initial supposait une plage de 1 à 1 000 Hz, mais dans la pratique, il s'est avéré que parcourir mille valeurs est fastidieux même avec un encodeur.

Préparation

Créer un nouveau projet dans un espace de travail vide

Projet > Créer un nouveau projet…

Type de modèle C > principal

Copiez les fichiers sources de la bibliothèque pour travailler avec l'encodeur dans le dossier du projet
encoder.h et encoder.c

Nous connectons le fichier encoder.c à notre projet
Bouton droit de la souris dans la fenêtre de l'espace de travail et dans le menu qui s'ouvre Ajouter > Ajouter des fichiers...

Copiez le fichier bits_macros.h dans le dossier du projet.


Y compris les fichiers d'en-tête

Au début du fichier main.c nous écrivons les lignes suivantes
#inclure
#inclure
#include "encodeur.h"
#include "bits_macros.h"

Définition des paramètres du projet

Projet > Options

Type de microcontrôleur
Options générales > Cible > Configuration du processeur > ATMega8535

Autoriser l'utilisation de noms de bits définis dans les fichiers d'en-tête
Options générales > Système > Activer les définitions de bits...

Optimisation du code pour la taille
Compilateur C/C++ > Optimisations > Taille élevée

Type de fichier de sortie
Linker > Vérification du fichier de sortie Remplacer la valeur par défaut et changer l'extension en hexadécimal
Linker > Format > Autre, sélectionnez Intel Standard

Cliquez sur OK. Enregistrez le projet et l'espace de travail.
Nous avons maintenant un projet vide avec une bibliothèque connectée et des paramètres spécifiés.

Tâche

Faites en sorte que le microcontrôleur génère une onde carrée avec une fréquence de 1 à 100 Hz. La valeur de fréquence doit être réglée à l'aide d'un encodeur. Tourner le codeur d'une position doit correspondre à un changement de la fréquence du générateur de 1 Hz.

Schéma pour notre exemple

Une LED est connectée à la broche sur laquelle le méandre sera généré afin de voir au moins en quelque sorte le résultat du programme. Il est peu probable que beaucoup de gens disposent d’un oscilloscope.

Algorithme du programme

Le signal d'onde carrée est généré à l'aide d'un temporisateur 16 bits T1, qui fonctionne en mode CTC - réinitialisé en cas de coïncidence. La mémoire flash du microcontrôleur stocke un tableau contenant une constante pour chaque valeur de la fréquence requise. La variable pTimerValue est utilisée pour accéder aux éléments du tableau. Lors des interruptions du temporisateur T1, la valeur de la constante est lue et écrite dans le registre de comparaison.

La broche PD5 (OC1A) est utilisée pour générer le signal. Il a des fonctions alternatives - il peut changer d'état dans le sens inverse si le registre de comptage et le registre de comparaison sont égaux.

Dans le programme principal, dans une boucle while sans fin, le microcontrôleur interroge le tampon de l'encodeur et, en fonction de sa valeur, diminue ou augmente la variable pTimerValue.

Au tout début de main se trouve le code pour initialiser les périphériques et les variables nécessaires.

Structure du programme

Pour plus de clarté, j'ai représenté la structure du programme sous la forme d'un diagramme.

Il s'agit d'une structure typique pour construire des programmes simples. Les interruptions se produisent naturellement à des points aléatoires de la boucle.

  • Initialisation.
  • Une boucle infinie (appelée superboucle) dans laquelle un événement est attendu, généralement sous la forme d'indicateurs d'interrogation ou d'une sorte de tampon.
  • Fonctionnement en parallèle de périphériques provoquant des interruptions. Ils exécutent du code (de préférence court) et définissent des indicateurs.

Pour des tâches simples, cette approche est suffisante. Pour les programmes complexes, il existe d’autres façons d’organiser les programmes. Soyez patient, les choses leur arriveront bientôt.

Calcul des constantes pour le temporisateur T1

Calculons la valeur de la constante pour une fréquence de 1 Hz. J'ai déjà donné un calcul similaire, mais il serait utile de s'en souvenir

La fréquence d'horloge du microcontrôleur est de 16 MHz (voir schéma). Le facteur de pré-échelle de minuterie est de 256. Il vous permet d'obtenir des interruptions avec n'importe quelle fréquence de notre gamme.

La période d'un tick de minuterie sera égale à 1/(16 MHz/ 256) = 16 µs

Sur la broche PD5, nous devons recevoir un signal d'une fréquence de 1 Hz. La broche change d'état à chaque interruption de la minuterie, ce qui signifie que la fréquence d'interruption doit être 2 fois plus élevée. Pour notre cas - 2 Hz.

Combien de ticks de minuterie peuvent contenir 2 Hertz ? (1/2 Hz)/16 µs = 31250
C'est la constante souhaitée.

Les valeurs restantes sont calculées de la même manière. J'utilise habituellement Excel pour cela.


Nous plaçons les valeurs résultantes dans un tableau

__éclair entier non signé valeur du minuteur =
{

enregistrez-le dans un fichier séparé – timer_value.h et connectez-le au fichier main.c

#include "timer_value.h"

Oui, vous devez encore ajouter quelques constantes à ce fichier

#définir MAX_TIM_VALUE 99
#définir MIN_TIM_VALUE 0

Assurons-nous que nous avons calculé correctement les constantes de la minuterie. Lançons-le. Le code du programme sera comme ceci.

//Programmation AVR en C

//site web 17/10/09
#inclure
#inclure
#include "encodeur.h"
#include "bits_macros.h"
#include "timer_value.h"

//index pour accéder aux éléments du tableau
caractère volatil non signé pTimerValue = 0 ;

int principal( vide )
{
//initialise le temporisateur T1
TCNT1 = 0 ;
TCCR1A = (0<TCCR1B = (0<

//définit la broche PD5 sur la sortie
SetBit(PORTD, PD5);
SetBit(DDRD, PD5);

//ne rien faire dans une boucle sans fin
alors que(1);
retour 0;
}

Je pense que seule la partie initialisation de la minuterie nécessite une explication.

Remise à zéro du registre du compteur
TCNT1 = 0 ;

Initialisation des registres de configuration du timer T1.
TCCR1A = (0<TCCR1B = (0<

Où les bits WGM13, WGM12, WGM11, WGM10 définissent le mode de fonctionnement de la minuterie - CTC,
CS12, CS11, CS10 – déterminent le coefficient du pré-échelonneur de minuterie –256,

COM1A1, COM1A0 – déterminent le comportement de la broche PD5(OC1F) – dans ce cas, sur la base d'un signal de minuterie, elle changera son état pour celui opposé


Initialisez le registre de correspondance à la valeur initiale.
OCR1A = valeur du minuteur ;

Nous compilons le programme et le chargeons dans le microcontrôleur. La LED doit clignoter à une fréquence de 1 Hz.
Il n'y a aucune interruption dans le programme. Il n'y a aucune manipulation de la broche PD5. Cependant, la LED clignote !

Programme

Vous devez maintenant « visser » l'encodeur sur ce programme. Définissons les paramètres dans le fichier d'en-tête encoder.h - le port et les broches auxquelles l'encodeur est connecté, les valeurs des constantes.


#define PORT_Enc PORTA
#define PIN_Enc PINA
#define DDR_Enc DDRA
#définir Pin1_Enc 2
#définir Pin2_Enc 1

#définir RIGHT_SPIN 0x01
#définir LEFT_SPIN 0xff

L'en-tête contient des prototypes de trois fonctions. Rappelons-nous leur objectif.

vide ENC_InitEncoder (vide) configure les broches du microcontrôleur auxquelles l'encodeur est connecté à l'entrée. Cette fonction doit être appelée au début de main.


annuler ENC_PollEncoder (vide)– interroge l'encodeur une fois, analyse les états actuel et précédent et écrit les constantes correspondantes (RIGHT_SPIN et LEFT_SPIN) dans le tampon. Cette fonction restera dans l'interruption de la minuterie T0.


caractère non signé ENC_GetStateEncoder (void)– renvoie le contenu du tampon de l'encodeur. Si la rotation d'une position n'était pas fixe, la fonction renverra 0 ; si la rotation était fixe, la fonction renverra la valeur de la constante correspondante. Cela effacera la valeur du tampon. Cette fonction sera appelée dans le programme principal – dans la boucle while.


Nous élargissons notre programme. Vous pouvez essayer de le faire vous-même.

//Programmation AVR en C
//un exemple d'utilisation d'un encodeur
//site web 17/10/09

#inclure
#inclure
#include "encodeur.h"
#include "bits_macros.h"
#include "timer_value.h"

#définir TCNT0_const 253
#définir TCCR0_const 5

caractère volatil non signé pTimerValue = 0 ;

int principal( vide )
{
ENC_InitEncoder();

//initialise le minuteur t0
TCNT0 = TCNT0_const ;
TCCR0 = TCCR0_const ;

//initialise le temporisateur t1
TCNT1 = 0 ;
TCCR1A = (0<TCCR1B = (0<OCR1A = valeur du minuteur ;

//activer les interruptions du minuteur
//t0 - par débordement, t1 - par coïncidence

TIMSK = (1<

//définit PD5 sur la sortie
SetBit(PORTD, PD5);
SetBit(DDRD, PD5);

__enable_interrupt ();
alors que (1){
//lire le contenu du tampon de l'encodeur
//après lecture, il est effacé

caractère non signé stateEnc = ENC_GetStateEncoder();

//si ce n'est pas vide
si(étatEnc != 0)(
//détermine le sens de rotation et change la variable timerValue
si(stateEnc == RIGHT_SPIN)(
si(pTimerValue == MAX_TIM_VALUE) pTimerValue = MIN_TIM_VALUE ;
autre pTimerValue++;
}
si(stateEnc == LEFT_SPIN) (
si(pTimerValue == MIN_TIM_VALUE) pTimerValue = MAX_TIM_VALUE ;
autre pTimerValue--;
}
}
}
retour 0;
}

//sondage de l'encodeur
#pragma vecteur=TIMER0_OVF_vect
__interromprevide timer0_ovf_my( vide)
{
TCNT0 = TCNT0_const ;
ENC_PollEncoder();
}

#pragma vecteur=TIMER1_COMPA_vect
__interrompre vide timer1_compa_my( vide)
{
// met à jour la valeur du registre de comparaison
OCR1A = valeur du minuteur ;
}

Il semble que tout devrait être clair.
Le morceau de code qui modifie la valeur de pTimerValue pourrait également être écrit comme ceci :

si(étatEnc != 0) (
pTimerValue = pTimerValue + stateEnc ;
si(pTimerValue == (MAX_TIM_VALUE + 1)) pTimerValue = MIN_TIM_VALUE ;
sinon si(pTimerValue == (MIN_TIM_VALUE - 1)) pTimerValue = MAX_TIM_VALUE ;
}

Lorsque l'encodeur tourne vers la droite, pTimerValue est ajouté à 1, c'est-à-dire qu'il est incrémenté.

Lors de la rotation de l'encodeur vers la gauche, pTimerValue est ajouté à 0xff, ce qui équivaut à soustraire 1. C'est la même opération, mais le résultat est exactement le contraire.

Dans cet article, vous apprendrez ce qu'est un encodeur, pourquoi il est nécessaire et comment le connecter à un microcontrôleur. Si vous avez utilisé une machine à laver, un four à micro-ondes ou un système audio moderne, vous avez probablement déjà eu affaire à un encodeur sans le savoir. Par exemple, la plupart des systèmes stéréo modernes pour la maison et la voiture utilisent des encodeurs pour régler le volume audio.
Un codeur ou capteur d'angle de rotation est un dispositif électromécanique conçu pour convertir la position angulaire d'un arbre ou d'un axe en signaux électriques. Il existe deux principaux types de codeurs : incrémentiel et absolu.
Lors de la rotation, le codeur incrémental génère des impulsions dont le nombre est proportionnel à l'angle de rotation. Compter le nombre de ces impulsions nous donnera l'angle de rotation de l'arbre codeur par rapport à sa position initiale. Ce type de codeur ne génère pas d'impulsions de sortie lorsque son arbre est au repos. Les codeurs incrémentaux sont largement utilisés dans les commandes industrielles, les appareils électroménagers et les équipements musicaux.
Un codeur absolu produit un code unique pour chaque position de son arbre. Contrairement à un codeur incrémental, il n'a pas besoin de compteur ; l'angle de rotation est toujours connu. Un codeur absolu génère un signal à la fois lorsque l'arbre tourne et lorsqu'il est au repos. Un codeur absolu ne perd pas d'informations sur sa position en cas de coupure de courant et ne nécessite pas de retour à la position initiale. Ce type d'encodeurs est utilisé dans les équipements industriels - robotique, machines-outils, lignes de convoyeurs.
Je voudrais parler du couplage d'un codeur mécanique incrémental avec un microcontrôleur. Pour ce faire, j'ai acheté un encodeur incrémental chez Bourns - PEC12-4220F-S0024. Voici le décodage de son nom selon la fiche technique : PEC12 – modèle, 4 – position verticale des cordons, 2 – 24 butées, 20 – longueur de l'arbre en mm, S – présence d'un bouton, 0024 – 24 impulsions par tour.

Il dispose de 5 broches. 2 broches sur la photo de gauche sont les broches du bouton, 3 broches sur la photo de droite sont les broches de l'encodeur. Parmi ceux-ci, 2 sont des signaux et 1 est général. Il est au milieu. Le schéma de connexion de l'encodeur n'est pas différent de la connexion des boutons conventionnels. Nous connectons les broches du signal de l'encodeur à n'importe quel port d'entrée/sortie du microcontrôleur. On place la sortie commune de l'encodeur au sol. Pour se protéger contre les rebonds de contact, ce serait une bonne idée d'ajouter quelques condensateurs céramiques supplémentaires d'une valeur de plusieurs nanofarads. Nous configurons les broches du microcontrôleur dans le programme comme entrées et allumons les résistances de rappel. Des externes peuvent être utilisés.

Lorsque le bouton de l'encodeur est à l'arrêt, il y a des unités logiques aux entrées du microcontrôleur. Lorsque le bouton de l'encodeur est tourné, deux signaux rectangulaires apparaissent au niveau des broches du microcontrôleur, décalés l'un par rapport à l'autre. Le sens de rotation de l'arbre du codeur détermine quel signal mènera l'autre. La figure ci-dessous montre les options de signal possibles pour un cas idéal.


À l'intérieur de l'encodeur se trouvent des contacts qui, lorsqu'ils sont tournés, se ferment ou s'ouvrent. Ce processus s'accompagne naturellement de rebonds, les vrais signaux peuvent donc ressembler à ceci.


Les signaux provenaient d'un ancien codeur, allumé sans condensateurs de filtrage.


L'algorithme de traitement du signal du codeur est le suivant. La fonction d'interrogation de l'encodeur est démarrée dans le gestionnaire d'interruption de minuterie. Il lit les niveaux logiques présents sur les broches du microcontrôleur auxquelles l'encodeur est connecté et les écrit dans une variable temporaire. À l'intérieur d'une fonction, il y a une variable statique (une variable qui conserve sa valeur lorsque la fonction se termine) qui stocke la séquence des états précédents. À l'aide d'un masque de bits, le microcontrôleur extrait le dernier état de cette variable et le compare à l'état actuel pour déterminer si des changements se sont produits. Si les états sont égaux, la fonction se termine ; s'ils sont différents, la valeur de la variable statique est décalée de 2 bits vers la gauche et l'état actuel est écrit dans l'espace « libéré ». Ainsi, si l'arbre du codeur tourne, la fonction stockera en permanence une certaine séquence de codes répétitifs. Lors d'une rotation vers la droite, ce sera 11100001. Lors d'une rotation vers la gauche, ce sera 11010010. En utilisant ces séquences, le microcontrôleur comprendra dans quelle direction la rotation se produit.

Le code source pour travailler avec l'encodeur peut être téléchargé. L'archive contient deux fichiers : encoder.h et encoder.c. L'en-tête spécifie les numéros de port et de broches auxquels l'encodeur est connecté, les constantes LEFT_SPIN et RIGHT_SPIN. Des prototypes de fonctions y sont également décrits. Le fichier C contient l'implémentation des fonctions.


annuler InitEncoder (vide)– initialise les broches du port.

annuler PollEncoder (vide)
– interroge l'encodeur une fois. Si une rotation est détectée, il écrit l'une des constantes dans le tampon ; sinon, il se termine simplement.

caractère non signé GetStateEncoder (void)
– renvoie le contenu du tampon et l'efface.

J'interroge généralement l'encodeur avec une fréquence de ~ 4 kHz. Si l'interrogation est plus lente, le microcontrôleur sautera des impulsions lorsque le bouton de l'encodeur est tourné rapidement. Si l'encodeur est utilisé pour définir une valeur variable linéairement, par exemple pour définir l'heure en heures, il est alors pratique d'utiliser les nombres 255 et 1 comme constantes LEFT_SPIN et RIGHT_SPIN, respectivement. Dans le processeur de signal du codeur, ces nombres sont simplement ajoutés à la valeur définie. Lors de l'ajout de 1, la valeur augmente de 1, lors de l'ajout de 255, elle diminue de 1. Bien sûr, cela est pertinent si cette valeur est d'un octet. Eh bien, en principe, les constantes LEFT_SPIN et RIGHT_SPIN peuvent être choisies arbitrairement, l'essentiel est d'écrire correctement le gestionnaire. C'est tout.

Code source pour travailler avec l'encodeur.



Des questions ?

Signaler une faute de frappe

Texte qui sera envoyé à nos rédacteurs :