Go Down

Topic: telecommande infrarouge pour apn (Read 1 time) previous topic - next topic

deldude

bonjour a tous, je suis en train de m'arracher les cheveux pour mettre au point une telecommande pour appareil photo numerique a base d'arduino. Je commence par emuler une telecommande wl-dc100 canon. J'ai trouve les definition des trames pour cette telecommande ici http://www.icb.chel.su/~sapa/lj/rc/pic_v4m.asm en assembleur pic et la http://lirc.sourceforge.net/remotes/canon/WL-DC100 sous une forme plus lisible. Malheureusement rien ne se passe. Voici mon code, voyez vous quelque chose qui vous choque, car ca a l'air pourtant correct. De plus, je vois bien la led ir clignoter en regardant par mon apn.

Code: [Select]

/* Exemple Emetteur WL-DC100    */
/* Jean-Christophe DURANTON 24/12/2007  */

#define LEDIR 3  // n° broche numérique de l'anode de la LED infrarouge  

int wldc_zero[]   = {612,512};
int wldc_one[]    = {612,1621};
int wldc_header[] = {9042,4379};
int wldc_ptrail[] = {599,107448};
int wldc_repeat[] = {9039,2115};
int wldc_bits     = 16;
int wldc_prebits  = 16;
int wldc_predata  = 0x538D;       // 0101001110001101
int wldc_snap     = 0xF807;        // 1111100000000111
int wldc_zoomin   = 0x42BD;     // 0100001010111101
int wldc_zoomout  = 0xC23D;    // 1100001000111101

void sendIRvalue(int value[])
{
 digitalWrite(LEDIR, HIGH);
 delayMicroseconds(value[0]);
 digitalWrite(LEDIR, LOW);
  delayMicroseconds(value[1]);
}  

void sendIRCodes(int hexval,int bytval)
{
 int cmp;
 int result[bytval];
 for(int itmp=0; itmp < bytval; itmp++)
 {
   int cmp = 1 << itmp;
   if ((hexval | cmp) == hexval)
   {
     result[bytval-1-itmp] = 1;
   }else
   {
     result[bytval-1-itmp] = 0;
   }
 }
 for(int jtmp=0; jtmp < bytval; jtmp++)
 {
   if(result[jtmp] == 1)
   {
     sendIRvalue(wldc_one);
   }else
   {
     sendIRvalue(wldc_zero);
   }  
 }  
}

void setup ()
{
 pinMode (LEDIR, OUTPUT);
}

void loop ()
{
 //header
 sendIRvalue(wldc_header);
 //predata
 sendIRCodes(wldc_predata,wldc_prebits);
 //snap
 sendIRCodes(wldc_snap,wldc_bits);
 //ptrail
 sendIRvalue(wldc_ptrail);
 delay(5000);
}


Si vous avez une petite idee, je suis preneur. Car je vois vraiment plus trop quoi teste.

Bonnes fetes.

deldude

Bon j'ai compris ce qu'il ne marche pas : la modulation à 38 Khz :-(
Merci à Plasma qui m'a mit sur la voie dans ce post : http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1198744543.

J'ai trouvé des solutions sur le net pour faire cette porteuse avec un NE555 ou un TL555, mais j'aimerais bien savoir comment effectuer cette porteuse de manière logicielle.

Essayons de savoir si j'ai compris quelque chose à cette histoire de porteuse :-)
Soit une fréquence de 38 Khz, cela veut dire qu'il faut osciller toute les 1/38000s = 26µs (à la louche bien sur). Ce qui signifie qu'il faut que la led soit allumée pendant 13µs et eteinte pendant 13µs.

Si on reprend mon exemple pour la telecommande canon alors un zero doit être codé de la manière suivante :
- 612µs en état high soit 612/13= 47 fois hight et 47 fois low
- 512µs en état high soit 512/13= 39 fois hight et 39 fois low

J'ai tout compris ?

denis_ceara

Bonsoir,
merci pour les liens, c'est intéressant, j'ai aussi trouvé celui-ci qui devrait t'intéresser:
http://www.datelec.fr/detectir/p0.htm
selon ce qui est écrit, il faut respecter un rapport cyclique de 25%, donc si je reprend tes 26 micro-secondes, cela fait environs 6 ou 7 micro-secondes à l'état haut, le reste à zero, pour la porteuse à appliquer avec un et logique sur ton signal...
Vu la précision attendue, il faut bien sure connaître le temps d'exécution de chaque instruction afin de pouvoir ajuster les différents délais, l'idéal, étant d'avoir un oscilloscope pour vérifier tout cela...
Tiens nous au courant
Denis

deldude

#3
Jan 03, 2008, 10:11 am Last Edit: Jan 03, 2008, 12:02 pm by deldude Reason: 1
Excellent ce lien, je n'ai plus qu'a tester tout cela. Selon moi l'avantage de le faire en soft est de pouvoir gérer différentes porteuses.
A priori le digitalwrite prend 10µs, donc il vas falloir que je prenne cela en compte.

Au passage j'ai fait un erreur dans mon explication, à la place de
- 612µs en état high soit 612/13= 47 fois hight et 47 fois low  
- 512µs en état high soit 512/13= 39 fois hight et 39 fois low  
Il faut plutot faire
- 612µs en état high soit 612/13= 47 fois hight et 47 fois low  
- 512µs en état low
Pas besoin de faire de modulation sur l'etat low :-)

Voila ma modif :
Code: [Select]
int wlcd_freq     = 38;        // carrier freq 38khz

void sendIRvalue(int value[], int freq)
{
 int space = abs(1/freq*1000)/2;
 int nb_space = abs(value[0]/space);
 for (int i=0; i<nb_space; i++)
 {
   digitalWrite(LEDIR, HIGH);
   delayMicroseconds(13);
   digitalWrite(LEDIR, LOW);
   delayMicroseconds(13);
 }  
 digitalWrite(LEDIR, LOW);
 delayMicroseconds(value[1]);
}  

Pour rappel value[0] contient la duree en µs de l'etat hight et value[1] contient la duree en µs de l'etat low.

Et voila le code complet (non testé pour le moment) je pense qu'il doit encore y avoir un problème de timing à cause du délai d'execution de digitalWrite.
Code: [Select]
/* Exemple Emetteur WL-DC100    */
/* Jean-Christophe DURANTON 24/12/2007  */

#define LEDIR 3  // n° broche numérique de l'anode de la LED infrarouge  

int wldc_zero[]   = {612,512};
int wldc_one[]    = {612,1621};
int wldc_header[] = {9042,4379};
int wldc_ptrail[] = {599,107448};
int wldc_repeat[] = {9039,2115};
int wldc_bits     = 16;
int wldc_prebits  = 16;
int wldc_predata  = 0x538D;    // 0101001110001101
int wldc_snap     = 0xF807;    // 1111100000000111
int wldc_zoomin   = 0x42BD;    // 0100001010111101
int wldc_zoomout  = 0xC23D;    // 1100001000111101
int wlcd_freq     = 38;        // carrier freq 38khz

void sendIRvalue(int value[], int freq)
{
 int space = abs(1/freq*1000)/2;
 int nb_space = abs(value[0]/space);
 for (int i=0; i<nb_space; i++)
 {
   digitalWrite(LEDIR, HIGH);
   delayMicroseconds(13);
   digitalWrite(LEDIR, LOW);
   delayMicroseconds(13);
 }  
 digitalWrite(LEDIR, LOW);
 delayMicroseconds(value[1]);
}  

void sendIRCodes(int hexval,int bytval, int freq)
{
 int cmp;
 int result[bytval];
 for(int itmp=0; itmp < bytval; itmp++)
 {
   int cmp = 1 << itmp;
   if ((hexval | cmp) == hexval)
   {
     result[bytval-1-itmp] = 1;
   }else
   {
     result[bytval-1-itmp] = 0;
   }
 }
 for(int jtmp=0; jtmp < bytval; jtmp++)
 {
   if(result[jtmp] == 1)
   {
     sendIRvalue(wldc_one, freq);
   }else
   {
     sendIRvalue(wldc_zero, freq);
   }  
 }  
}

void setup ()
{
 pinMode (LEDIR, OUTPUT);
}

void loop ()
{
 //header
 sendIRvalue(wldc_header,wldc_freq);
 //predata
 sendIRCodes(wldc_predata,wldc_prebits,wldc_freq);
 //snap
 sendIRCodes(wldc_snap,wldc_bits,wldc_freq);
 //ptrail
 sendIRvalue(wldc_ptrail,wldc_freq);
 delay(5000);
}


A+

denis_ceara

Bonjour Jean-Christophe,

merci pour les mises à jour, c'est intéressant! En fouillant un peu, j'ai trouvé une discussion concernant les performances:

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1198758865/6#6

c'est intéressant, mais à vérifier avec un oscilloscope... notamment sur le fait que l'instruction:
digitalWrite(PIN, HIGH);
soit plus lente que:
PORTB = 0x40;

me semble pas si évident, étant donné que les instructions sont compilées...
En tout cas il serait bon d'en être sur.
Bonne journée

Denis




nrolland

#5
Jan 04, 2008, 10:25 pm Last Edit: Jan 04, 2008, 10:26 pm by nrolland Reason: 1
Bonjour a vous 2,

En effet le digitalWrite est bcp + long que le PORTB = 0x40;

Pour se faire une première idée vous pouvez allez voir le code source (dans /lib/targets/arduino/wiring_digital.c).
Vous y verrez le code source de digitalWrite().

Si vous avez besoin d'un décompte précis du nombre d'instructions exécutées il faut désassemblé un sketch :
1) ecrire un sketch avec un digitalWrite() + le compiler
2) dans un terminal taper la commande  (avr-objdump -S /le/path/vers/ton/home/arduino/applet/TonSketchxxxx.elf)
3) tu auras dans ce fichier le digitalWrite décompilé
4) en comptant le nombre d'instructions (la plus part des instructions s'exécute en 1 seul cycle d'horloge dans la famille avr cf datasheet du atmega168) et en divisant par 16 000 000 (16Mhz) tu auras le temps en seconde.

Ca donne qq chose comme ca (instruction en 3eme colonne paramètre après) :

000003f2 <digitalWrite>:
3f2:      48 2f             mov      r20, r24
3f4:      55 27             eor      r21, r21
3f6:      ca 01             movw      r24, r20
3f8:      81 5a             subi      r24, 0xA1      ; 161
3fa:      9f 4f             sbci      r25, 0xFF      ; 255
3fc:      fc 01             movw      r30, r24
3fe:      24 91             lpm      r18, Z
400:      ca 01             movw      r24, r20
402:      85 5b             subi      r24, 0xB5      ; 181
404:      9f 4f             sbci      r25, 0xFF      ; 255
406:      fc 01             movw      r30, r24
408:      94 91             lpm      r25, Z
40a:      49 5c             subi      r20, 0xC9      ; 201
40c:      5f 4f             sbci      r21, 0xFF      ; 255
40e:      fa 01             movw      r30, r20
410:      34 91             lpm      r19, Z
412:      33 23             and      r19, r19
414:      11 f1             breq      .+68           ; 0x45a <digitalWrite+0x68>
416:      22 23             and      r18, r18
418:      81 f0             breq      .+32           ; 0x43a <digitalWrite+0x48>
41a:      23 30             cpi      r18, 0x03      ; 3
41c:      19 f4             brne      .+6            ; 0x424 <digitalWrite+0x32>
41e:      8f b5             in      r24, 0x2f      ; 47
420:      8f 77             andi      r24, 0x7F      ; 127
422:      04 c0             rjmp      .+8            ; 0x42c <digitalWrite+0x3a>
424:      24 30             cpi      r18, 0x04      ; 4
426:      21 f4             brne      .+8            ; 0x430 <digitalWrite+0x3e>
428:      8f b5             in      r24, 0x2f      ; 47
42a:      8f 7d             andi      r24, 0xDF      ; 223
42c:      8f bd             out      0x2f, r24      ; 47
42e:      05 c0             rjmp      .+10           ; 0x43a <digitalWrite+0x48>
430:      25 30             cpi      r18, 0x05      ; 5
432:      19 f4             brne      .+6            ; 0x43a <digitalWrite+0x48>
434:      85 b5             in      r24, 0x25      ; 37
436:      8f 7d             andi      r24, 0xDF      ; 223
438:      85 bd             out      0x25, r24      ; 37
43a:      e3 2f             mov      r30, r19
43c:      ff 27             eor      r31, r31
43e:      e3 5d             subi      r30, 0xD3      ; 211
440:      ff 4f             sbci      r31, 0xFF      ; 255
442:      e4 91             lpm      r30, Z
444:      ff 27             eor      r31, r31
446:      66 23             and      r22, r22
448:      29 f4             brne      .+10           ; 0x454 <digitalWrite+0x62>
44a:      80 81             ld      r24, Z
44c:      90 95             com      r25
44e:      89 23             and      r24, r25
450:      80 83             st      Z, r24
452:      08 95             ret
454:      80 81             ld      r24, Z
456:      89 2b             or      r24, r25
458:      80 83             st      Z, r24
45a:      08 95             ret
 

Y'a + fun comme sport mais si vous avez besoin de timing précis => il faut passer par la.

Nicolas

denis_ceara

Bonjour Nicolas,

merci beaucoup :) je vais jouer un peu avec tout cela... ma première semaine d'arduino aura été instructive! En fait j'ai plein d'autres questions, mais je vais pas polluer le sujet de Jean-Christophe.
A+

Denis


denis_ceara

Juste pour enfoncer le clou (pour dire combien j'avais tord!!) et après avoir joué avec avr-objdump...
Je me suis exectué deux petites boucles, l'une avec digitalWrite(PIN, HIGH), l'autre avec PORTB = 0x40, Le résultat est édifiant:

le premier:
Code: [Select]

#define PIN 13      
void setup()
{
 pinMode(PIN, OUTPUT);
 Serial.begin(9600);
}
void loop()
{
 long x;
 x = 2000000;
 Serial.print("Ca commence!: ");
 Serial.println(millis());
 while(x>0)
 {
   digitalWrite(PIN, HIGH);
   digitalWrite(PIN, LOW);
   x--;
 }
 Serial.print("C'est fini!");
 Serial.println(millis());
}


me sort:
Ca commence!: 12
C'est fini!14705
Ca commence!: 14727
C'est fini!29423
Ca commence!: 29445
C'est fini!44141

ca fait 14693 ms entre chaque boucle

le second:
Code: [Select]

void setup()
{
 Serial.begin(9600);
}
void loop()
{
 long x;
 x = 2000000;
 Serial.print("Ca commence!: ");
 Serial.println(millis());
 while(x>0)
 {
       PORTB = 0x40; // set pin 13 to high, all other port B pins low
       PORTB = 0x00; // set pin 13 and all other port B pins low
   x--;
 }
 Serial.print("C'est fini!");
 Serial.println(millis());
}


sort:
Ca commence!: 12
C'est fini!1029
Ca commence!: 1050
C'est fini!2069
Ca commence!: 2089
C'est fini!3109

soit 1017ms

cela fait un rapport de 14,4...

En plus il y a une incertitude de 3 ou 4ms sur les temps successifs, mais cela vient logiquement de la comparaison.

Autrement dit, il faut pas longtemps pour se rendre compte qu'il n'est pas inutile d'apprendre un peu d'assembleur ;)

A+

Denis

deldude

#8
Jan 05, 2008, 06:53 pm Last Edit: Jan 05, 2008, 06:59 pm by deldude Reason: 1
j'ai corrigés mes erreurs de calculs sur les int et modifié très légérement mon wait pour me rapprocher des valeurs que je trouve en lançant le frequency test dispo ici http://www.arduino.cc/en/Reference/Millis et là surprise .....
ça ne marche toujours pas  ;D

Voici le code, j'en arrive à me demander si je n'ait pas une erreur ailleur, mais je ne vais pas vous infliger l'integralite du code encore une fois:
Code: [Select]
void sendIRvalue(int value[], int freq)
{
int space = abs(1000/freq/2) - 4;  
//le -4 correspond à la valeur que je trouve avec le frequency test ie 9 pour 38khz
int nb_space = abs(value[0]/space);
for (int i=0; i<nb_space; i++)
{
  digitalWrite(LEDIR, HIGH);
  delayMicroseconds(space);
  digitalWrite(LEDIR, LOW);
  delayMicroseconds(space);
}  
digitalWrite(LEDIR, LOW);
delayMicroseconds(value[1]);
}  



Je doit plus être très loin de la solution mais là je seche completement..

denis_ceara

Salut Jean-Christophe,

je viens de bricoler avec une LED infra rouge, et j'ai vu que ses caractéristiques sont assez différentes de celles des Led communes, notamment il faut une intensité bien plus importante. Celle que j'ai acheté a une tension de 1,5V et fonctionne avec une intensité de 250 mA, (au lieu de 3,2V et 20 mA pour une led blanche). En fait, en utilisant la Webcam de mon ordi, j'ai vu qu'elle ne fonctionnait pas, (donc j'ai recherché ses caractéristiques), j'ai ajouté un transistor en commutation à mon montage et ca va  beaucoup mieux...

A+

Denis

Go Up