X10 RF transmitter

Génération de signaux RF X10


Version Fr à traduire.

Présentation :

Ce projet conciste en la génération de signaux RF X10 pour allumer ou éteindre des dispositifs X10 (domotique par courant porteur).
Nous utilisons un microcontrôleur dsPIC 30F3010 de chez Microchip comme système numérique, sans quartz (on utilise son FRC interne et une PLLx8) et un transmetteur ASK. Ce système utilise le codage X10 home automotion (domotique par courant porteur) pour envoyer des ordres aux différents appareils électriques.

Les ordres sont reçu par un récepteur RF qui les transforme en ordre X10 envoyé par courant porteur. J'utilise un TM751 modifié.

mots clefs : microcontrôleur dspic 30F3010, transmetteur ASK, X10.



Taille réelle du transmetteur RF X10

Schéma du montage


Configuration des entrées sorties logiques

On configure les 4 entrées logiques connectées aux bouttons poussoirs afin de réagir en générant des CN (change notification) afin de réveiller le dspic de son mode de veille (Sleep)
En effet, pour économiser la pile, nous mettons le dspic en mode de veille tant qu'aucune touche n'est appuyée.
La sortie RF_TX attaque le transmetteur RF acheté chez Sparkfun. C'est un transmetteur 315 MHz car le système dont je dispose est aux normes US.
L'alimentation du transmetteur RF se fait par la sortie logique RF_Alim afin de n'alimenter le transmetteur que pendant la transmission pour préserver la durée des piles.
Attention, en France, il faut utiliser un transmetteur 434 MHz.

On peut également utiliser un transmetteur ASK, comme le MAX1742, en lui réglant la fréquence par un quartz externe. Example pour des applications à 315MHz, le quartz doit avoir une fréquence de 4.7547MHz, alors que pour 433.92MHz, un quartz de 6.6128MHz est nécessaire.
Ce transmetteur (que je n'ai découvert qu'une fois la carte tirée :-( et le projet ficelé) permet même de gérer l'économie d'énergie grâce à entrée STANDBY. Cependant, il est en boitier SOT-23, c'est-à-dire, du CMS très difficile à souder manuellement.
Pour notre transmetteur, nous ne l'alimentons que quelques ms avant l'envoi de donné. Ce qui permet d'amener la consomation de la carte en mode stand by à 6 uA ! pour quelques 35 mA en emission.

Les définitions et l'assignation des E/S logiques :
   
  1. #define StartSendingK1on    PORTBbits.RB0    // input
  2. #define StartSendingK1off  PORTBbits.RB1        // input
  3. #define StartSendingK2on    PORTBbits.RB2    // input
  4. #define StartSendingK2off  PORTBbits.RB3        // input
  5. #define LED_KEY    LATBbits.LATB4   // Output
  6. #define LED_RUN    LATBbits.LATB5   // Output
  7. #define RF_TX     LATDbits.LATD0   // Output
  8. #define RF_Alim    LATEbits.LATE8   // Output
  9. #define LED_CN        LATDbits.LATD1      // Output
  10.  
  11. void setup_ports(void);
  12. void DelayNmSec(unsigned int N);
  13. void Delay(unsigned int N);
  14. void InitUART();
  15.  
  16. struct {
  17.       unsigned Running    :   1;
  18.       unsigned CheckRX    :  1;
  19.       unsigned SendTX   :    1;
  20.       unsigned SendData  :    1;
  21.       unsigned RF_Sending :    1;
  22.       unsigned unused     :  11;
  23.     } Flags;
  24. //-----------------------------------------------------------------------------
  25. //  Setup
  26. //-----------------------------------------------------------------------------
  27. void setup_ports(void)
  28. {
  29.   ADPCFG=0xFFFF;  // all PORTB = Digital(1) : no ADC
  30.   // Clear All Ports Prior to defining I/O
  31.   PORTB=0//Initialize LED pin data to off state
  32.   PORTC=0;
  33.   PORTD=0;
  34.   PORTE=0
  35.   // Now set pin direction registers
  36.   TRISB = 0xFFCF;  // RB0-3 inputs, RB0 StartSending PushB in, RB4 LED_KEY out,  RB5 LED_RUN out  1100|1111
  37.   TRISC = 0xFFFF;  // U1ATX/RC13 in , U1ATX/RC14 out INUTILE de les configurer ainsi car directement on configure l UART
  38.   TRISD = 0xFFFC;  // RD1 RF24G_CE  out , RD0 RF24G_CS out
  39.   TRISE = 0x00FF;  // RE8 RF_Alim out, le reste input 0|1111|1111
  40.   TRISF = 0xFFFB;  // SDI1/RF2 in, SDO1/RF3 out : 1011 ou 1x11 car SDI1 doit se programmer via le TRISF
  41. }
  42.  
  43. //-----------------------------------------------------------------------------
  44. //  intitialise ChangeNotification CN7 et l interruption
  45. //-----------------------------------------------------------------------------
  46. void initChangeNotification(void)
  47. {
  48. //  CNEN1 = 0x000C;  // RB0-RB1 donc CN2-3
  49.   CNEN1 = 0x003C; //  RB0-RB3 donc CN2-5
  50.   CNPU1= 0; CNPU2=0// No pull-up on input change
  51.   IFS0bits.CNIF = 0// Clear IF bit
  52.   IPC3bits.CNIP = 3// assigning Interrupt Priority to IPC Register
  53.   IEC0bits.CNIE = 1// assiging Interrupt Enable
  54. }



Génération des signaux X10

Partie du programme qui génère le signal X10 RF :
   
  1. //---------------------------------------------------------------------
  2. void Delay(unsigned int N)
  3. {
  4. unsigned int j;
  5.   for(j=0;j < N; j++) __builtin_nop();
  6. }
  7. //-----------------------------------------------------------------------------
  8. void SendX10RF_bit(unsigned int databit)
  9. {
  10.   // envoi un 1
  11.   RF_TX=1;
  12.   Delay(1600);    // 0.56 ms
  13.   RF_TX=0;
  14.   Delay(1600);    // 0.56 ms
  15.   // si c est un 0
  16.   if (!databit)  Delay(3200);  // 0.56*2 = 1.12 ms si un zero
  17. }
  18. //-----------------------------------------------------------------------------
  19. void SendX10byte(unsigned int data8)
  20. {
  21. unsigned int j, k;
  22.   for (j=0; j<8; j++)
  23.     {
  24.     k=(data8 & 0x0080 )==0x0080;
  25.     SendX10RF_bit(k);
  26.     data8=data8 << 1;
  27.     }
  28. }
  29. //-----------------------------------------------------------------------------
  30. void SendX10Cmd(unsigned int data)
  31. {
  32. unsigned int Cmd8;
  33.   RF_Alim=1;      // Alimente le transmetteur RE8=ON
  34.   Delay(60000);  // >20 ms
  35. // en-tête
  36.   RF_TX=1;
  37.   Delay(26800);  // 8.96 ms
  38.   RF_TX=0;
  39.   Delay(13400);  // 4.5 ms
  40.   Cmd8=data>>8;     // send les Hi
  41.   SendX10byte(~Cmd8);
  42.   SendX10byte(Cmd8);
  43.   Cmd8=(data) & 0xFF;  // send les Low
  44.   SendX10byte(~Cmd8);
  45.   SendX10byte(Cmd8);
  46.   SendX10RF_bit(1);   // 1 à la fin ??
  47.   Delay(3200);    // 1.12 ms
  48.   RF_Alim=0;      // Eteint transmetteur RE8=OFF
  49. }
  50. //-----------------------------------------------------------------------------
  51.  




Gestion des touches

A chaque appui sur une touche la fonction CN (change notification) réveille le dspic et renvoie vers la fonction de service de l'interruption (ISR) _CNInterrupt :
   
  1. //---------------------------------------------------------------------
  2. // CN interrupt : Reception de données du RF24G, données prêtes pour lecture
  3. //---------------------------------------------------------------------
  4. void __attribute__((interrupt, auto_psv)) _CNInterrupt( void )
  5. {
  6.   LED_RUN=1;
  7.   IFS0bits.CNIF = 0// Clear IF bit
  8. Delay(1000)
  9.   LED_RUN=0
  10. }

Cette fonction ne fait qu'abaisser le drapeau afin de montrer que l'on a pris en compte la CN. Remarque : LED_RUN n'est pas branché, cette patte est utilisée à des fins de deboggage.

La routine principale (main) teste quelle touche est appuyée et demande l'envoi du code X10 correspondant :
   
  1. //-----------------------------------------------------------------------------
  2. //Main routine
  3. int main(void)
  4. {
  5. unsigned int j;
  6.   setup_ports();
  7.   for (j=0; j<15000; j++)  __builtin_nop();  //  plein de délais
  8.   LED_RUN=0;
  9.   initChangeNotification();
  10.   Flags.RF_Sending=0;
  11.   Flags.Running=1;
  12.  
  13.   Sleep();    // attends le premier ordre
  14.   while(1)
  15.     { 
  16.     if (StartSendingK1on) 
  17.       {
  18.       LED_KEY=1;                // indique qu on appuie sur la touche
  19.       SendX10Cmd(0x6000);    // A1 ON
  20.       while (StartSendingK1on)  DelayNmSec(10);  // wait till key is released
  21.       DelayNmSec(500);          // pour pouvoir voir la LED
  22.       LED_KEY=0;                // indique qu on relache la touche
  23.       Sleep();
  24.       }
  25.    
  26.     if (StartSendingK1off) 
  27.       {
  28.       LED_KEY=1;                // indique qu on appuie sur la touche
  29.       SendX10Cmd(0x6020);    // A1 OFF
  30.       while (StartSendingK1off) DelayNmSec(10);    // wait till key is released
  31.       DelayNmSec(500);          // pour pouvoir voir la LED
  32.       LED_KEY=0;                // indique qu on relache la touche
  33.       Sleep();
  34.       }
  35.    
  36.     if (StartSendingK2on) 
  37.       {
  38.       LED_KEY=1;                // indique qu on appuie sur la touche
  39.       SendX10Cmd(0x6010);    // A2 ON
  40.       while (StartSendingK2on)  DelayNmSec(10);  // wait till key is released
  41.       DelayNmSec(500);          // pour pouvoir voir la LED
  42.       LED_KEY=0;                // indique qu on relache la touche
  43.       Sleep();
  44.       }
  45.    
  46.     if (StartSendingK2off) 
  47.       {
  48.       LED_KEY=1;                // indique qu on appuie sur la touche
  49.       SendX10Cmd(0x6030);    // A2 OFF
  50.       while (StartSendingK2off) DelayNmSec(10);    // wait till key is released
  51.       DelayNmSec(500);          // pour pouvoir voir la LED
  52.       LED_KEY=0;                // indique qu on relache la touche
  53.       Sleep();
  54.       }
  55.     } // end of while (1)
  56. }



Le protocole X10

J'ai eu un peu de mal, car les pages web que j'ai trouvées présentent le codage sous des formes différentes.

Le plus intéressente explication, même si le codage est de droite à gauche, est celle de :
 Data format for X-10 wireless units (Edward Cheung, RF sampled data by: Paul Gumerman)
Les autres liens qui parlent du protocle x10 RF et des dipositifs de génération sont :
 X-10 RF Protocol (by Dave Houston)
 Handheld X-10 Wireless Transmitter
 Modified X10 RF transmitter

Le signal RF se présente comme une modulation d'un signal de 315 MHz. La modulation (l'enveloppe de la porteuse) se présente sous la forme d'une succession de 1 et de 0.
Contrairement aux délais présentés dans la référence de départ, j'ai trouvé les délais suivants (fournis par ma télécommande X10, branchée sur l'oscilloscope) :
------------------------             ---------        ---------                      -
|                      |             |       |        |       |                      |
|         9 ms         |    4.5 ms   | 0.56ms| 0.56ms |0.56ms | 0.56ms     1.12 ms   |
-                      ---------------       ---------        --------+---------------
               en-tête RF            +      '1'       +      '1'      +    '0'

Pour arriver à ce codage, on part du code de la commande X10 que l'on veut, exemple, allumer l'appareil n°1 de la maison codée A : A1 ON.
Le code qui correspond est : 0x6000
Voici quelques codes :
0x6000 pour A1 ON
0x6020 pour A1 OFF
0x6010 pour A2 ON
0x6030 pour A2 OFF

Examinons la fonction :
   
void SendX10Cmd(unsigned int data)
{
unsigned int Cmd8;
// en-tête
  RF_TX=1;
  Delay(26800);  // 8.96 ms
  RF_TX=0;
  Delay(13400);  // 4.5 ms
  Cmd8=data>>8;     // send les Hi
  SendX10byte(~Cmd8);
  SendX10byte(Cmd8);
  Cmd8=(data) & 0xFF;  // send les Low
  SendX10byte(~Cmd8);
  SendX10byte(Cmd8);
  SendX10RF_bit(1);   // 1 à la fin ??
}

Il y a d'abord l'envoi de l'en-tête (un '1' pendant 8.96 ms suivi d'un '0' pendant 4.5 ms).
Le codage proprement dit se retrouve au milieu et il est assez particulier :
Si l'on reprend l'exemple de 0x6000, pour le protocole X10, il nous faut générer le complément des 2 octets 0x60 et 0x00 :
10011111 01100000 11111111 00000000
qui correspond à :
~0x60 0x60 ~0x00 0x00

Ensuite le codage impose que pour chaque '1', on envoie '1' mais pour chaque '0', on envoie '10', c'est-à-dire un '1' suivi d'un '0'.
Ceci se fait par le biais de la fonction :
   
void SendX10RF_bit(unsigned int databit)
{
  // envoi un 1
  RF_TX=1;
  Delay(1600);    // 0.56 ms
  RF_TX=0;
  Delay(1600);    // 0.56 ms
  // si c est un 0
  if (!databit)  Delay(3200);  // 0.56*2 = 1.12 ms si un zero
}

Ce qui donne pour notre exemple qu'à partir de :
10011111 01100000 11111111 00000000
On a : 1 0 011111  011 0 0 0 0 0 11111111  0 0 0 0 0 0 0 0
Donc : 1101011111 10111010101010 11111111 1010101010101010

A ce code on rajoute donc l'entête et le '1' terminateur et on retrouve la modulante avec laquelle on attaque le génrateur RF.


Détails du transmetteur RF X10 : on distingue le module RF en vert, le dspic et quelques boutons poussoirs
Le connecteur est compatible avec l'ICD2

Download executable and sources.
Download :
x10orcad.pdf   Schéma du montage.
x10orcad.zip   Schématics et pcb.
X10.silk.pdf   Circuit imprimé.
x10.zip   Programme source complet (nécessite MPLAB et le compilateur C30).


Back to homepage

Last update : 30/12/2007