Souhaitez-vous participer à la création d'un jeu vidéo inspiré de Stardew Valley, ou le tester lorsque la version bêta sera disponible ? Remplissez notre sondage ou inscrivez-vous à notre lettre d'information (en bas de page)
1

encodeur programme bas niveau avec interruption arduino

le 29-02-2016 à 20:44 #
Bonjour,

l'affichage sur 4 digits montre l'éloignement de la position de référence si cela augmente vous vous éloignez de votre référence et si cela diminue vous vous en rapprochez
https://circuits.io/circuits/2870218/edit#breadboard


Lorsque vous tombez sur un truc comme cela vous pouvez vous gratter la tête ayant une arduino leonardo j'ai été obligé de modifier
le faisant je me suis dit que c'était un excellent exercice pour comprendre la manipulation des ports des interruptions et le fonctionnement d'un encodeur


attachInterrupt(0, encoderInt, CHANGE); // encoder pin on interrupt 0 - pin 3
attachInterrupt(1, encoderInt, CHANGE); // encoder pin on interrupt 1 - pin 2

const int QEM [16] = {0,-1,1,2,1,0,2,-1,-1,2,0,1,2,1,-1,0}; //
static unsigned char New, Old;

void encoderInt() { //prise en charge des interruptions 0 et 1 sur leonardo
Old = New;
// New = (PINB & 1 )+ ((PIND & 4) >> 1);
New = (PIND & 2) + (PIND & 1); //leonardo
encoder0Pos+= QEM [Old * 4 + New];
}

quadrature encodeur





http://playground.arduino.cc/Main/RotaryEncoders


cette image peut être animée si vous allez sur ce site http://www.pjrc.com/teensy/td_libs_Encoder.html qui donne aussi une librairie de lecture d'encoder assez rapide mais moins rapide que celle ci dessus qui permet de moduler un PID sans consommer trop de cycles



Si cela intéresse quelqu'un j'explique





(Modifié par rokag3 le 02-03-2016 à 04:02)

(Modifié par rokag3 le 05-10-2016 à 03:04)

(Modifié par rokag3 le 05-10-2016 à 08:10)
Re: encodeur programme bas niveau avec interruption arduino
le  1-03-2016 à 21:58 #
simple, efficace et élégant
tout pour plaire
Re: encodeur programme bas niveau avec interruption arduino
le  1-03-2016 à 22:18 #

le  1-03-2016 à 21:58, @karthesius :
simple, efficace et élégant
tout pour plaire


Tu as compris cela {0,-1,1,2,1,0,2,-1,-1,2,0,1,2,1,-1,0};?
oublie les deux et remplace les par des 0 ce n'est qu'une valeur destinée à limiter une erreur de lecture d'encodeur!
sincèrement tu m'épates
Si tu n'as pas compris et c'est bien excusable je t'explique



(Modifié par rokag3 le 01-03-2016 à 22:22)
Re: encodeur programme bas niveau avec interruption arduino
le  1-03-2016 à 22:34 #
je me suis contenté de constater le principe, ce qui evidemment ne pose pas de probleme à quiconque, j'ai pas été detailler ce que donnent les liens.
J'ai donc du rater quelque-chose.
j'y vais de ce pas.

Ajout du 01-03-2016 à 22:48:

bon j'ai été voir de plus près
.. et c'est
je vois pas à quoi tu fais allusion avec {0,-1,1,2,1,0,2,-1,-1,2,0,1,2,1,-1,0};?
(preuve que peut-etre je n'ai rien compris )
le principe de coder un signal avec des photodiodes sur un disque rotatif et d'incrémenter un compteur pour activer le stockage des données sur des mémoires, je trouve la methode sympathique et parfois necessaire.
Mais ensuite aller comprendre les details d'un codage particulier dans un exemple précis..
et puis le script de programmation du circuit , via le Pc, je pratique pas vraiment..
Je m'y suis pourtant interessé de près il y a quelques temps à la faveur des divers sujets que tu postais, mais depuis j'ai pas trouvé à m'en servir.. donc c'est un peu loin. Mais peut-etre un jour si les besoins se présentent.



Ajout du 01-03-2016 à 22:50:

ah oui {0,-1,1,2,1,0,2,-1,-1,2,0,1,2,1,-1,0};?
c'est le resultat des poids du codage hein ?
Re: encodeur programme bas niveau avec interruption arduino
le  1-03-2016 à 23:33 #
cela peut être repris sur n'importe quel processeur et dans n'importe quel langage de bas niveau faut aller vite quand même


J'explique
tu as deux pins A et B ces pins peuvent prendre une valeur 0 ou 1

simple
old = new si new est égal à 0 j'ai maintenant old =0
toujours simple
lorsque tu as un changement sur une de ces pin du vas lire le registre sur leque elles est attaché
ici c'est le port D en position 0 pour la pin A et 1 pour la pin 2

par exemple si tu as A=0 et B=1 le port D peut ressembler à 0001 1010
lorsque tu fais l'instruction tu ne t'intéresse qu'aux deux premiers bits
New = (PIND & 1)+ (PIND & 2)
tu places dans ton octet New la valeur 0000 0010 soit 2

bon j'ai 2 dans new et 0 dans old dans quel sens tourne mon encodeur? dois je ajouter ou retrancher une position?



Ajout du 02-03-2016 à 01:31:

Après tout un encodeur cela sert à ca incrémenter ou décrementer une position
voyons quels sont les positions qui peuvent être prises car toutes ne sont pas possible
old new mettons cela sur 4 bits old * 4 + new
01 00 1*4+0=4 CCW
00 01 0*4+1=1 CW
01 11 1*4+3=7 CW
11 10 3*4+2=14 CW
10 00 2*4+0 =8 CW
changeons de sens
00 10 0*4+2=2 CCW la voila ma réponse je dois ajouter 1
10 11 2*4+3=11 CCW
11 01 3*4+1=13 CCW
01 00 1*4+0=4 CCW
00 10 0*4+2=2 CCW

sur 4 bits nous avons 16 possibilités mais nous constatons que nous n'en avons que 8 valides 4 pour sens de la montre CW et 4 pour anti horaire CCW
observons les positions 1 7 14(E) et 8 de notre matrice NOUS VOYONS QUE NOUS AVONS LA VALEUR -1 DONC CW (on recule par rapport au disque)
{0,-1,1,2,1,0,2,-1,-1,2,0,1,2,1,-1,0}
.0..1.2.3.4.5.6.7..8..9.A.B.C.D.E.F

observons maintenant les positions 2 11(B) 13(D) et 4 NOUS VOYONS QUE NOUS AVONS LA VALEUR 1 DONC CCW (on avance par rapport au disque)
{0,-1,1,2,1,0,2,-1,-1,2,0,1,2,1,-1,0}
.0..1.2.3.4.5.6.7..8..9.A.B.C.D.E.F

La ligne
encoder0Pos+= QEM [Old * 4 + New]; fait juste cela et se lit

nouvelle valeur de position pour l'encodeur 0 = valeur actuelle de position pour l'encodeur zero + contenu de la case [Old * 4 + New] de la matrice QEM
celle ci contenant soit 1 soit -1
pas besoin de faire de comparatif
Si je suis en mode test je pourrai, dans le cas d'une valeur différente de 1 ou -1, incrémenter une variable que l'on pourrait appeler NombreDeFautes qui me permettra de voir que mon encodeur ne fonctionne pas correctement


Si vous me le demandez je vous donnerai et expliquerai comment utiliser un encodeur de récup d'imprimante (souvent 1200 lignes soit 4800 positions par tour) à jet d'encre ces encodeurs fonctionnent avec 3.3V et deux photodiodes on doit donc utiliser un ampliop le LM358 fait le boulot parfaitement





Ajout du 02-03-2016 à 14:54:

Dans mon cas ayant mis les deux broches d'interruptions prioritaires sur l"encodeur je peux alléger le programme
voici donc mon programme optimisé plus court tu meurs
je travaille avec un encodeur de recup ayant 1200lignes par tour soit 4800 positions par tour ce qui représente 2400 changements d'etat par tour
prenons un moteur qui tourne à 3000rpm
doit 3000/60 50 tours secondes soit *2400 120khz ce qui donne 8.3 microsecondes c'est dur pour un processeur à 16Mhz 0.625 microsecondes par tick au pif je dirai que ce programme doit pouvoir fonctionner à 80khz seul l'assembleur pur permettrait de tenir le challenge


volatile long encoder0Pos = 0;//variable volatile pour l'interruption
const int QEM [16] = {0,-1,1,2,1,0,2,-1,-1,2,0,1,2,1,-1,0}; //
static unsigned char New, Old;
long encoder0Chg;
void setup()
{
attachInterrupt(0, encoderInt, CHANGE); // encoder pin on interrupt 0 - pin 3
attachInterrupt(1, encoderInt, CHANGE); // encoder pin on interrupt 1 - pin 2
Serial.begin (115200);
while (!Serial);
}

void loop()
{
if (encoder0Pos!=encoder0Chg){
Serial.println(encoder0Pos); // compte tenue de la lenteur de l'instruction serial il faudra tourner assez lentement
encoder0Chg=encoder0Pos;
}
}

void encoderInt() //prise en charge des interruptions 0 et 1 sur leonardo
{
Old = New;
New = (PIND & 3 );
encoder0Pos+= QEM [Old * 4 + New];
}+

(Modifié par rokag3 le 02-03-2016 à 15:56)
Re: encodeur programme bas niveau avec interruption arduino
le  4-10-2016 à 20:46 #
Il semble que vous ne suivez pas avec beaucoup d'attention
Ce programme comporte un bug facile a lever si l'on a compris le principe
Solution ce dessous en rouge




(Modifié par rokag3 le 04-10-2016 à 20:48)
Re: encodeur programme bas niveau avec interruption arduino
le  5-10-2016 à 21:14 #
Allez, je me mêle :

Lorsque tu dis :
je travaille avec un encodeur de recup ayant 1200lignes par tour soit 4800 positions par tour ce qui représente 2400 changements d'etat par tour


Pourquoi tu passes de 4800 états (00,01,11,10) (position et état, c'est la même chose, non?) à 2400 changement d'état ? J'imagine que 1200 lignes = 1200 lignes noires.

Si tu veux augmenter la vitesse de rotation maximal avant que l'Arduino décroche, il existe un moyen de rendre le code plus rapide (avec un coût au niveau de la résolution). J'imagine que c'est aussi possible de rendre le code plus rapide tout court, mais je n'en suis pas 100% sûr. Tout ça en supposant qu'on a traduit la partie critique en assembleur.

(Modifié par Alcar le 05-10-2016 à 21:15)
Re: encodeur programme bas niveau avec interruption arduino
le  5-10-2016 à 21:39 #
Lorsque tu as 1200 lignes noires tu as aussi 1200 lignes transparentes bien sur
Tu as donc pour chaque photo diode 1200 *2 changements d'états à gérer sur 1 tour 1200 LOW vers HIGH et 1200 HIGH vers LOW
donc un total de 2400 changements d'états * 2 photo diodes soit 4800, un état étant ici le passage du LOW à HIGH et de HIGH à LOW.
Ce sont ces transitions qui sont gérés par interruption
une solution consiste à utiliser un diviseur mais pas si simple car on a vite fait d'accumuler des erreurs et de perdre sa position.
Le diviseur doit être hardware et doit gérer les reliquats .

l'autre solution c'est de travailler avec une horloge plus rapide, pour ce qui est du code ce code est le plus rapide que je connaisse, et je serais intéressé d'en voir un plus rapide mais cela ne me semble pas évident!

On ne peut malheureusement pas écrire en pur assembleur avec le simulateur, c'est dommage car lorsque l'on travaille en optimisation on a vite fait de flinguer un processeur





(Modifié par rokag3 le 05-10-2016 à 21:42)
Re: encodeur programme bas niveau avec interruption arduino
le  5-10-2016 à 23:43 #
Ok, mais ce 2 changement d'état par période n'est pas pertinent vu que le callback (donc ce qui est limité par l'horloge de l'arduino) est appelé 4x par période.

SI ce n'est pas un problème de diminuer la résolution par 4 :
volatile long encoder0Pos = 0;//variable volatile pour l'interruption
volatile unsigned char Old = 0;/*je ne sais pas si l'accès d'une variable volatile est plus lente que pour une variable static*/
long encoder0Chg;
void setup()
{
attachInterrupt(digitalPinToInterrupt(2), encoderIntCW, RISING); // channel A
attachInterrupt(digitalPinToInterrupt(3), encoderIntCCW, RISING); // channel B
Serial.begin (115200);
while (!Serial);
}

void loop()
{
if (encoder0Pos!=encoder0Chg){
Serial.println(encoder0Pos); // compte tenue de la lenteur de l'instruction serial il faudra tourner assez lentement
encoder0Chg=encoder0Pos;
}
}

void encoderIntCW() //channel A, pin 2
{
if old == 12 & (PIND & 12) == 4 //le cas 1,1,0,1
{
encoder0Pos++;//the rotation is CW
}

old = PIND & 12 ;
}

void encoderIntCCW() //channel B, pin 3
{
if old == 12 & (PIND & 12) == 8 //le cas 1,1,1,0
{
encoder0Pos--;//the rotation is CCW
}

old = PIND & 12 ;
}

C'est équivalent à ton code pour :
QEM = {0,0,0,0,0,0,0,0,0,0,0,0,0,1,-1,0};
Petit détail : pour une rotation à vitesse constante, le temps entre 2 callbacks sont long,court,long,court,etc... et lorsque la prochaine callback arrive plus vite, le microcontroleur s'arrête à old == 12 et ne va pas dans le "if".

Après, il y a ce code qui est équivalent au tien, mais je ne sais pas si il est plus court. Au cas où, tu peux toujours faire sauter le premier if (donc la variable old aussi), mais ça sera moins stable (tu peux faire la même chose dans le code ci-dessus) :
volatile long encoder0Pos = 0;//variable volatile pour l'interruption
volatile unsigned char Old = 0;/*je ne sais pas si l'accès d'une variable volatile est plus lente que pour une variable static*/
long encoder0Chg;
void setup()
{
attachInterrupt(digitalPinToInterrupt(2), encoderIntA, CHANGE); // channel A
attachInterrupt(digitalPinToInterrupt(3), encoderIntB, CHANGE); // channel B
Serial.begin (115200);
while (!Serial);
}

void loop()
{
if (encoder0Pos!=encoder0Chg){
Serial.println(encoder0Pos); // compte tenue de la lenteur de l'instruction serial il faudra tourner assez lentement
encoder0Chg=encoder0Pos;
}
}

void encoderIntA() //channel A, pin 2
{
char new = PIND & 12;

if old == new ^ 4//les cas 0,0,0,1;1,0,1,1;1,1,1,0;0,1,0,0. Ce sont tous les cas normals (donc sans anomalie)
{
if new == 4 || new == 8//les cas x,x,0,1;x,x,1,0
encoder0Pos--;//the rotation is CCW
else
encoder0Pos++;//the rotation is CW
}

old = new;
}

void encoderIntB() //channel B, pin 3
{
char new = PIND & 12;

if old == new ^ 8//les cas 0,0,1,0;1,0,0,0;1,1,0,1;0,1,1,1. Ce sont tous les cas normals (donc sans anomalie)
{
if new == 4 || new == 8//les cas x,x,0,1;x,x,1,0
encoder0Pos++;//the rotation is CW
else
encoder0Pos--;//the rotation is CCW
}

old = new;
}

(Modifié par Alcar le 06-10-2016 à 00:29)
Re: encodeur programme bas niveau avec interruption arduino
le  6-10-2016 à 04:32 #
attachInterrupt(digitalPinToInterrupt(2), encoderIntCW, RISING); // channel A

L'idée qui consiste à utiliser les fronts montants je ne l'ai peut être pas assez creusée, je vais voir si je peux perdre des indications de changement de sens en utilisant cette astuce, j'avais pensé que oui probablement et j'ai glissé par flemme de faire les diagrammes

Merci de ton intérêt


Ok, mais ce 2 changement d'état par période n'est pas pertinent vu que le callback (donc ce qui est limité par l'horloge de l'arduino) est appelé 4x par période.

Sur un tour de moteur j'ai 4800 interruptions
prenons un moteur qui tourne à 3000 RPM
3000/60=50 RPSecond
soit 240000 interruptions par secondes
j'ai environ 40 cycles d'opérations de piles et une quinzaines de bricoles + les interruptions de l'horloge de l'arduino je n'ai largement pas assez de temps pour dérouler un programme utile à cette vitesse


excellent article sur les interruptions de l'arduino
https://billgrundmann.wordpress.com/2009/03/02/the-overhead-of-arduino-interrupts/
The following is what is generated for the interrupt handler for INT0. Using the atmegas168 datasheet and information about instructions we can count the number of instruction cycles. There are 45 cycles before there is a call to the “myfunc” and there are 35 cycles after return of “myfunc” and return from the interrupt handler. There is also 3 cycles in the pin synchronizer and another 3 cycles for a JMP instruction (which is in the interrupt vector table). So, we have a total of 51 cycles before we start to execute “myfunc”.


La solution serait de faire un diviseur en quadrature hardware ou d'avoir une horloge plus rapide

volatile unsigned char Old = 0;/*je ne sais pas si l'accès d'une variable volatile est plus lente que pour une variable static*/

une variable volatile est stockée en RAM elle est donc plus lente mais plus "sure"

void encoderIntA() //channel A, pin 2
{
char new = PIND & 12;

if old == new ^ 4//les cas 0,0,0,1;1,0,1,1;1,1,1,0;0,1,0,0. Ce sont tous les cas normals (donc sans anomalie)
{
if new == 4 || new == 8//les cas x,x,0,1;x,x,1,0
encoder0Pos--;//the rotation is CCW
else
encoder0Pos++;//the rotation is CW
}


Mettre des branchements conditionnel dans une routine d'interruption peut ne pas être une bonne idée dans la mesure ou il peut ruiner une optimisation éventuelle du compilateur
Tu penses que c'est plus rapide qu'une simple indexation qui correspond à un offset sur une adresse ?
je pense que ta routine d'interruption est trop longue et risque trop de ne pas passer à travers les gouttes des interruptions des timers de l'arduino

Petit détail : pour une rotation à vitesse constante, le temps entre 2 callbacks sont long,court,long,court,etc... et lorsque la prochaine callback arrive plus vite, le microcontroleur s'arrête à old == 12 et ne va pas dans le "if".


ici le if vise à bloquer une avalanche de serial print mais bien entendue dans ce genre de programmes on n'utilise jamais de serial print C'est uniquement pour des raisons de test et on travaille à faible vitesse pour tester les erreurs.

Je ne comprend pas ta phrase dans sa partie surligné
Un servomoteur ne vas pratiquement jamais à vitesse constante il est en accélération décélération et variation de vitesse de façon quasi permanente









Ajout du 06-10-2016 à 06:03:

Je comprend vite mais il faut m'expliquer TRES longtemps

je viens de faire un diagramme sur OpenOffice et effectivement si l'on utilise rising on a pas un intervalle constant
en fait on a deux changement d'état qui se suivent à la vitesse réelle de l'encodeur puis 3 cycles aveugle

je vais tester le comptage si cela fonctionne je peux sans doute bufferisé le signal de l'encodeur
Il ne faut pas oublier que dans certains cas un moteur peut "brouter" et on ne doit pas perdre sa position; le broutage se traduit par des variations de directions très rapide

Re: encodeur programme bas niveau avec interruption arduino
le  6-10-2016 à 07:55 #
71 cycles pour une fonction d'interruption... c'est énorme !

Un "if A == B", c'est normalement 2 cycles. Donc, pour le premier code, c'est 3(+1 "set" utilisant une variable volatile) cycles si ça ne passe pas la première condition, 6(+1 "set" utilisant une variable volatile) cycles si ça ne passe pas la 2ème et 6(+1 "set" et + 1 "inc" utilisant une variable volatile) cycles sinon. Si tu mets de côtés la stabilité, ça devient 3 cycles si ça ne va pas dans le if et 3(+ 1 "inc" utilisant une variable volatile) cycles sinon.

Mais, bon, je ne savais pas pour ces 71 cycles d'overhead donc ces qq cycles supplémentaire, c'est négligeable.

En faite, mon code est plus stable parce qu'il vérifie si la différence entre le "old" et le "new" correspond à la bonne interruption.

doit 3000/60 50 tours secondes soit *2400 120khz ce qui donne 8.3 microsecondes c'est dur pour un processeur à 16Mhz 0.625 microsecondes par tick au pif je dirai que ce programme doit pouvoir fonctionner à 80khz seul l'assembleur pur permettrait de tenir le challenge


Sur un tour de moteur j'ai 4800 interruptions
prenons un moteur qui tourne à 3000 RPM
3000/60=50 RPSecond
soit 240000 interruptions par secondes

Donc une interruption doit durer moins que 4.1 usec (et non 8.3 usec)
Re: encodeur programme bas niveau avec interruption arduino
le  6-10-2016 à 12:40 #
En faite, mon code est plus stable parce qu'il vérifie si la différence entre le "old" et le "new" correspond à la bonne interruption.

?
Je ne comprend pas !
si une interruption interviens peu importe qu'elle soit déclenchée par int0 ou int1 puisque l'on vas lire le port et utiliser les bits 2 et 3 sous la forme d'un "mot" de 2 bits.

Re: encodeur programme bas niveau avec interruption arduino
le  6-10-2016 à 13:43 #
Si ca passe de 00 à 10 (pin 3, pin 2) et que c'est l'interruption lié au pin 2 qui se déclenche, il y a un problème. Mon code peut le détecter. Mais, bon, quand je dis plus stable, ca ne veut pas dire si ce "plus stable" est notable.
Re: encodeur programme bas niveau avec interruption arduino
le 12-10-2016 à 00:06 #
Une solution pour mettre un encodeur d'imprimante à jet d'encre hacké c'est de fabriquer un diviseur en quadrature



l'arduino gère le moteur et son encodeur avec le potar
le petit attiny 13 lui gère la division avec un switch à deux positions qui per,et une division par 1;2;4;8
le programme de l'attiny
pas d'interuption juste une boucle et une fonction
le attiny 13 coute moins de 2$ je dois voir comment le faire fonctionner à 20Mhz


#include <avr/io.h>

long int encoder0Pos = 0;//variable volatile pour l'interruption
const int QEM [16] = {0,-1,1,2,1,0,2,-1,-1,2,0,1,2,1,-1,0}; //up or down
const int slowEncoder [9] = {8,0,12,4,0,8,0,12,4};//table of sequence of the encoder
const int in1 = 0; // broches pour activer pont
const int in2 = 1;
const int outA1 = 2;
const int outB2 = 3;
const int divider12LedIndex =4;
const int divider48LedExtra =5;
unsigned char Divisor=1; //consigne de vitesse
unsigned char New, Old;
int sign=0;
int diviseNew;

void setup() {
pinMode(in1, INPUT);
pinMode(in2, INPUT);
pinMode(outA1 , OUTPUT);
pinMode(outB2 , OUTPUT);
pinMode(divider12LedIndex, INPUT);
pinMode(divider48LedExtra, INPUT);
Divisor=1<<((PINB &48)>>4);
pinMode(divider12LedIndex, INPUT);
pinMode(divider48LedExtra, INPUT);
}

void encoderInt()
{
Old = New;
New = (PINB &3 );
encoder0Pos+= QEM [Old * 4 + New];
if (!(encoder0Pos%Divisor)){//if the position of fast encoder can be divided by divisor
sign= QEM [Old * 4 + New];
diviseNew=slowEncoder [4+sign+(((PINB &12)>>2)*sign)];
PORTB |= (diviseNew);
PORTB &=(diviseNew);
}
}

void loop() {
if(New != (PINB &3 )){ //any change on the encoder
encoderInt(); //call the routine to determine increment od decrement
}
}
Re: encodeur programme bas niveau avec interruption arduino
le 19-02-2017 à 03:28 #
Je reprend ce programme et je l'implémente sur un attiny84 à 20 mhz en diviseur par 2 à 8 mhz il n'est pas assez rapide je vais utiliser le portA en entree et le portB en sortie
en reprenant ce programme je me suis rendu compte qu'un supplément de commentaires est nécessaire
-

//vB1.0
//rokag3 2/21/2017
//diviseur par 2 d'encodeur en quadrature
//attiny 84 20mhz
//if you use it say it's from me
// ********************************************************************************************
//Arduino IDE & Pin Mapping
// add 2 capacitor 22picof ground to PB0 and PB1 where is the crystal 20mhz
// add a capa 1000 microf between 5V an gnd
// ATMEL ATTINY84 / ARDUINO
//
// +-\/-+
// red VCC 5V 1| |14 GND black
// quartz1 (D 10) PB0 2| |13 AREF (D 0)
// quartz2 (D 9) PB1 3| |12 PA1 (D 1)
// reset PB3 4| |11 PA2 (D 2) outA1 green to DRO
// PWM INT0 (D 8) PB2 5| |10 PA3 (D 3) outB1 yelow to DRO
// PWM (D 7) PA7 6| |9 PA4 (D 4) in1 A green from encoder
// PWM (D 6) PA6 7| |8 PA5 (D 5) PWM in2 B yelow from encoder
// +----+
#include <avr/io.h>

long int encoder0Pos,encoder0Divide = 0;//position of the encoder since power up
const char direction [16] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0}; //up or down table
const char slowEncoder [9] = {4,12,0,8,0,4,12,0,8,};//table of sequence of the encoder A+B+ bits 2 and 3


#define in1 0 //PA4
#define in2 1 //PA5
#define outA1 2 //PA2
#define outB2 3 //PA3
#define Divisor 2 //consigne de vitesse
byte New,Nnew, Old;
char testerr,faults,sign=0;
byte diviseNew;

void setup() {
pinMode(in1, INPUT);
pinMode(in2, INPUT);
pinMode(outA1 , OUTPUT);
pinMode(outB2 , OUTPUT);
}



void loop() {

Nnew=PINA & 3; // Only one read to keep the coherence of the time same as Nnew=PINA &48/16 if I read 32 then divided by 16 Nnew = 2
if(New != Nnew){ //any change on the encoder ? if yes routine to determine increment or decrement
Old = New;
New = Nnew;// I take here the value I read before the test to keep the coherence of the time
sign = direction [Old * 4 + New];//I take the value in my table
if (sign){ //in C if the argument is not equal to zero it is TRUE so -1 is true like 1
encoder0Pos+= sign; //I increment or decrement the position with sign who can be -1 or 1
if (!(encoder0Pos&1))//if divisor is 2 4 8 16 no need to make a costly modulo
{
diviseNew=slowEncoder[4+sign+(((PINA &12)>>2)*sign)];//
if ((encoder0Divide*Divisor)!=encoder0Pos)
{
encoder0Divide+= sign;
PORTA |= (diviseNew);//equivalent to PORTA = PORTA or divisenew
PORTA &=(diviseNew); //equivalent to PORTA = PORTA and divisenew
}
}
}
}
}

/*
Old = New;
New = (PINB &3 );
encoder0Pos+= QEM [Old * 4 + New];
if (!(encoder0Pos%Divisor)){//if the position of fast encoder can be divided by divisor
sign= QEM [Old * 4 + New];
diviseNew=slowEncoder [4+sign+(((PINB &12)>>2)*sign)];
PORTB |= (diviseNew);
PORTB &=(diviseNew)
*/
//explanation not complete let say that
//const int slowEncoder [9] = {8,0,12,4,0,8,0,12,4};//table of sequence of the encoder A+B+ bits 2 and 3
// sign=testerr diviseNew=slowEncoder [4+sign+(((PINB &12)>>2)*sign)];
// value to write is at the position 4(+1 or-1)so 5 or 3 9here we are going to take -1 for sign so we have 3
//+ the bits in +A+B shifted to obtain the value on 2 bits here 12=8+4 so position 3 and 4 lets shift 2 time
// if the value was 4 >>2 and it become 1
//so (((4)>>2)*-1)=-1 so 4+sign+(((PINB &12)>>2)*sign=>3+-1=2
// I will write the value in slowEncoder[2]so 12 in portB bit 3 will be 1 and bit 4 will be 1 job done
// If I want to write A B in portB bit 4 and 5
//const int slowEncoder [9] = {16,48,0,32,0,16,48,0,32};//table of sequence of the encoder A+B+ bits 4 and 5
// sign=testerr diviseNew=slowEncoder [4+sign+(((PINB &48)>>4)*sign)]

(Modifié par rokag3 le 21-02-2017 à 16:06)
Re: encodeur programme bas niveau avec interruption arduino
le  5-06-2017 à 14:30 #
Ce qui serait intéressant ce serait de faire une transmission d'encoder en quadrature sans fil

Si l'on prend bluetooth d'après Texas Instrument http://e2e.ti.com/support/wireless_connectivity/bluetooth_low_energy/f/538/t/123119
on doit compter 5 à 6000 bits par secondes

idéalement un transfert par diode infrarouge permettrait une transmission à haute vitesse mais on ne peut pas ignorer le risque d'interuption de signal

Dans un système de ce type on devrait donc utiliser d'une part une transmission par infrarouge (purement électronique sans soft ) et d'autre part compteur de position qui serait envoyé au récepteur et permettrait à celui ci de vérifier sa position

Je dois donc écrire un logiciel qui vas réécrire une séquence de grey code à partir d'une ancienne position sur une nouvelle position
idéalement dans un système avec PID la réémissions de cette reconstitution devrait se faire à une vitesse compatible avec l'accélération / décélération pour éviter des arrêts cardiaque au calculateur du PID

Je ne conseille pas un système sans fil pour une CNC de haute précision (par contre une transmission par fibre optique oui) dans le cas qui m'intéresse il s'agit d'une mesure en continue par conséquent la fabrication d'une trame de grey code seras envoyée à -10% de la vitesse maximum de réception du récepteur de l'encoder



Ajout du 05-06-2017 à 15:34:

Donc j'ai au niveau de l'encodeur un compteur long int encoder0Pos que je pourrais définir sur 24 bits non signés ce qui me donnerait 2^23 soit +-8388608 soit pour un encodeur de 1 micron de résolution une longueur maxi de 8.38 mètres !!! une résolution à 1 micron sur cette longueur est totalement absurde même 5 microns le serait mais permettrait déjà une longueur de 41.5 m par contre un 2^16 me donnerait 2^15 soit +- 32768 soit pour un encodeur de 1 micron de résolution une longueur maxi de 3.27 cm

je transmet encoder0Pos à receptor01 par exemple 25433
il a lui la valeur lastEncoderPosTransmittedToUser par exemple 25440
Je vais faire le différentiel et déterminer le signe donc encoder0Pos - lastEncoderPosTransmittedToUser= -7
si le différentiel est non nul je dois générer la trame de grey code

Pour cela je dois bien sur connaître le dernier état de mon encoder virtuel disons actualPositionOfVirtualEncoder = 2 (10 en binaire)
je dois donc transmettre la séquence
actualPositionOfVirtualEncoder =11 lastEncoderPosTransmittedToUser =25439
actualPositionOfVirtualEncoder =01 lastEncoderPosTransmittedToUser =25438
actualPositionOfVirtualEncoder =00 lastEncoderPosTransmittedToUser =25437
actualPositionOfVirtualEncoder =10 lastEncoderPosTransmittedToUser =25436
actualPositionOfVirtualEncoder =11 lastEncoderPosTransmittedToUser =25435
actualPositionOfVirtualEncoder =01 lastEncoderPosTransmittedToUser =25434
actualPositionOfVirtualEncoder =00 lastEncoderPosTransmittedToUser =25433

Nous devons trouver un programme très court pour générer cette trame ici (1,0,0,1,1,0,0 sur le port A et 1,1,0,0,1,1,0 sur le port B output
Si la sortie de l'encodeur virtuel doit être en différentiel en enverra 1100,0110,0011,1001,1100,0110,0011 sur A B NotA NotB







(Modifié par rokag3 le 05-06-2017 à 15:37)

Ajout du 05-06-2017 à 20:06:

Nous avons au choix 3 2 0 1 ou 12,6,3,9 pour un encodeur différentiel
sign peut prendre la valeur -1 ou +1

Re: encodeur programme bas niveau avec interruption arduino
le  7-06-2017 à 13:48 #





//
// encoder generator between 2 limits
// the final version will have to read the value 40 time per second and generate the
// AB or AB!A!B signal and this with a speed variation pseudo realist
// for the fun and eventually to use a serial hardware I have used the lines A0,A1,A2,A3
// traditionally used as analogic input
int ledA = 14; //A0 signal A
int ledB = 15; //A1 signal B
int ledEnd = 16;//A2 not transmitting when TRUE
int swOne = 17; //A3 qn input for test
int accel =1; // the value that will be used to send the signal roughly number of frame to send / 40(if 40 value per sec)
bool valA, valB; //general purpose for test
const byte quadratureEncoderAB [4] = {2,3,1,0};//10,11,01,00 the sequence of a quadrature encodeer
int actualPositionOfVirtualEncoder =3;//state of the actual output A and B
long int lastEncoderPosTransmittedToUser =20000;//position received from the emitter
long int encoder0Pos =10000;//actual position
int sign = 0;
int nbOfIteration = 0;
//
void setup() {
// initialize the digital pin as an output.
pinMode(ledA, OUTPUT);
pinMode(ledB, OUTPUT);
pinMode(ledEnd, OUTPUT);
pinMode(swOne, INPUT);
digitalWrite(ledEnd, HIGH);


}

// the loop routine runs over and over again forever:
void loop()
{
valA = digitalRead(swOne); // read the input pin
if (valA)encoder0Pos = 18000;
nbOfIteration=encoder0Pos - lastEncoderPosTransmittedToUser;
if(nbOfIteration)
{digitalWrite(ledEnd, LOW);
if(nbOfIteration>0)
{sign=1;
while (nbOfIteration--)
{actualPositionOfVirtualEncoder--;
if (actualPositionOfVirtualEncoder<0) actualPositionOfVirtualEncoder=3;
makeSignal();
}
}
else
{ sign=-1;
while (nbOfIteration++)
{actualPositionOfVirtualEncoder++;
if (actualPositionOfVirtualEncoder>3) actualPositionOfVirtualEncoder=0;
makeSignal();
}
}
}
digitalWrite(ledEnd, HIGH);
}
void makeSignal()
{
PORTC &= (quadratureEncoderAB [actualPositionOfVirtualEncoder]);
PORTC |= (quadratureEncoderAB [actualPositionOfVirtualEncoder]);
encoder0Pos += sign*-1;
//delayMicroseconds(accel);
}https://circuits.io/users/112377/designs




Ces discussions pourraient vous intéresser également:


arduino et telecommande
Arduino: modifier une page HTML ?
Apprendre à utiliser circuits.io arduino simulateur
acquisition en temps réel Arduino + transmission bluetooth
programme en C# avec l'interface I²C