Pages: [1] 2   Go Down
Author Topic: Librairie Guyt54 pour Arduino  (Read 1813 times)
0 Members and 1 Guest are viewing this topic.
Montréal
Offline Offline
Newbie
*
Karma: 0
Posts: 48
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

J'espère ne pas abuser en mettant ici les modules de ma librairie personnelle qui je suis en train de développer pour Arduino, une façon simple de la rendre disponible pour mes étudiants.

Les quelques modules de la librairie seront développés avec une approche de programmation orienté objet, histoire de faire moderne (et d'être conforme à mon propre plan de cours, appelée "Programmation orientée objet appliquée a l'électronique" smiley-wink

En premier lieu, la classe "TgeComProt" qui implante sous Arduino le protocole de communication que nous utilisons depuis des années et qui fonctionne bien dans un contexte où le contrôle en temps réel n'est pas critique ( > 100 ms).

Juste au cas où ça pourrait vous intéresser, quelques détails sur le protocole en question.

Le protocole de communication tge (Tech. du génie électrique)  est un protocole simple  de communication sériel entre un maitre (habituellement un pc) et un esclave (habituellement un système-cible basé sur un microcontrôleur). Le but est de faire un maximum de traitement sur le PC, alors que le système-cible fait le strict minimum, soit répondre aux commandes de bas niveau du maitre.

En gros, le maitre envoie une commande à l'esclave qui répond par une commande, ce qui est une façon simple de s'assurer que la commande a  été effectuée. Les commandes sont composées des chaines de caractères ASCII délimités par les caractères "<" et ">". Les données sont en hexadécimal à longueur fixe ( ex: byte = 2 caractères).

Par exemple, supposons que nous désirons écrire un bit à une entrée/sortie numérique du Arduino One via le pc. La commande transmise par le PC (maitre) pourrait être:

<BW0D01>

où:

B: Commande de bit
W: Write (écriture)
0D: bit 13
01:  donnée (1)

Le système-cible va renvoyer l'écho de la commande, soit:

<PW0D01>

Pour lire un bit d'une entrée numérique, la commande pourra être:

<BR02>

Le système-cible répondra par:

<BR0200>

où:

B: Commande de bit  indique une commande de bit
R: Read (lecture)
02: bit 2
00: donnée lue (0)

On pourrait implanter des commandes de lecture d'entrées analogiques de la façon suivante:

Maitre -> Eclave: <AR04>    ( lire entrée analogique 4)
Esclave->Maitre: <AR0480> (valeur lue: 128)
 
Trève de bla bla voici l'interface de la classe (TComProt.h):

Code:
// TgeComProt.h: Protocole de communication sérielle TGE
// Guy Tessier, nov. 2010

#ifndef TGECOMPROT_H
#define TGECOMPROT_H

#include "WProgram.h"

typedef void (*TgeProtRxCmdEvent)(String cmd) ;

class TgeComProt
{
  public:
  TgeComProt(void) ;
  void Setup(int baudRate,TgeProtRxCmdEvent event) ;
  void TreatReceive(void) ;
  void Send(String cmd) ;

  private:
  String fCmd ;
  TgeProtRxCmdEvent fOnRxCmd ;
  void TreatChar(char car) ;
} ;

#endif

La méthode "Setup" sera appelé par la fonction "setup" de l'application. On passe en paramètre le baud rate ainsi que l'adresse de la fonction qui sera appelée automatiquement lorsqu'une commande a été reçue et qui aura comme paramètre d'entrée la commande en question (dénudée des délimiteurs). La méthode "TreatReceive" devra être appelée par la fonction "loop" du programme, histoire d'assurer le traitement des caractères reçus au port sériel. La méthode "Send" permettra de transmettre une commande au PC.

Voici un petit programme qui illustre le plus simplement possible l'utilisation de la classe "TgeComProt":

Code:
#include <TgeComProt.h>

TgeComProt comProt ;

void TreatCommand(String cmd)
// fonction de rappel lorsqu'une commande est recu:
// les délimiteurs de commande ont été enlevés
{
  cmd = cmd.toUpperCase() ;
  comProt.Send(cmd) ;
}

void setup()
{
  comProt.Setup(9600,TreatCommand) ;
}

void loop()
{
  comProt.TreatReceive() ; // pour traitement des car recus
}




Et finalement, l'implantation du module TgeComProt (TgeComProt.cpp):

Code:
// TgeComProt.cpp: Protocole de communication sérielle TGE
// Guy Tessier, nov. 2010

#include "TgeComProt.h"

// ------
// Public
// ------

TgeComProt::TgeComProt(void)
// Constructeur
{
  fCmd = "" ;
}

void TgeComProt::Setup(int baudRate,TgeProtRxCmdEvent event)
// Initialisation à appeler dans "setup" de l'application
// baudRate: Baud Rate utilisé
// event: Adresse de la fonction de rappel lorsqu'une commande à été recue.
//          ex:  void CommandeRecue(String cmd) //
//               {
//                 // Traite(cmd) ; // commande avec délimiteurs enlevé
//               }  
//  
// n.b.: devrait normalement être dans le constructeur, mais y avait un phoque
//       avec la communication sérielle
{
  Serial.begin(baudRate) ;
  fOnRxCmd = event ;
}

void TgeComProt:: TreatReceive(void)
// Traitement des caractères reçus. Doit être appellé par "loop"
{
  char car ;
  while (Serial.available())
    TreatChar(Serial.read()) ;
}  

void TgeComProt::Send(String cmd)
// Transmission de la commande
{
  cmd = '<' + cmd + '>' ;
  Serial.print(cmd) ;
}  

// -------
// Private
// -------

void TgeComProt::TreatChar(char car)
// traitement du car recu
{
  switch (car)
  {
  case '<' :
    fCmd = "" ;
    break ;
  case '>' :
    if (fCmd.length() && fOnRxCmd )
    {
      fOnRxCmd(fCmd) ;
      fCmd = "" ;                        
    }
    break ;
  default:  
    fCmd = fCmd + car ;  
  }        
}




« Last Edit: November 28, 2010, 07:44:06 pm by Guyt54 » Logged

Sophia-Antipolis (06)
Offline Offline
God Member
*****
Karma: 4
Posts: 618
Rejoignez le club !
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Bonjour.

Euh... je vois pas trop ce que fait cette ligne :

Code:
fOnRxCmd(fCmd) ;

Logged

Montréal
Offline Offline
Newbie
*
Karma: 0
Posts: 48
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Bonjour Luj06,

D'abord, merci d'avoir jeter un coup d'œil sur mon code.

Voici l'explication de la ligne:

fOnRxCmd(fCmd) ;

les variables "fOnExCmd" et "fCmd" font parti de la classe "TComProt"

Code:

typedef void (*TgeProtRxCmdEvent)(String cmd) ;

class TgeComProt
{
  public:
  ...
  private:
  String fCmd ;
  TgeProtRxCmdEvent fOnRxCmd ;
  ...
} ;


Le petit "f" de "fCmd" et "fOnRxCmd" est une convention personnelle qui m'indique qu'une variable est un champ (field) de la classe. La variable "fCmd" contiendra la commande et est de type "String". "fOnRxCmd" est un pointeur de fonction, c'est à dire une variable qui va contenir l'adresse d'une fonction.

Le concept de pointeur de fonction est rarement évident pour mes étudiants. Voici comment je tente de leur expliquer ça.

Considère le code suivant:

Code:
void test(void)
 {
    // code
 }

void main(void)
{
   test() ; // appel de la fonction
   test  ;  //  erreur fréquente chez un programmeur débutant
}


Le premier énoncé de la fonction "main" appelle la fonction "test". Comme la fonction "test" n'a pas de paramètre d'entrée, un programmeur débutant aura tendance à oublier les parenthèses pour appeler la fonction:

   test ;

Si les parenthèses sont omises, la fonction ne sera par appelée. et  l'énoncé "test" et équivalent à (si je suppose que la fonction "test" est localisée à l'adresse 1000):

  1000 ;

C'est les parenthèses qui font que la fonction "test" est appelé, "test" pouvant être vu comme une variable contenant l'adresse en mémoire de la fonction. Avec un compilateur un peu permissif, on pourrait appeler la fonction "test" de la façon suivante:

1000() ; // appel de la fonction à l'adresse 1000

En supposant que "PF" est un pointeur de fonction, c'est à dire une variable qui contient l'adresse d'une fonction, on pourrait appeler la fonction "test" de la façon suivante:

Code:
PF = test ;  // PF va contenir 1000
PF() ;         // Appel de la fonction dont l'adresse est donné par le
                 // contenu de PF

Le plus difficile, c'est de définir une variable "pointeur de fonction". Voici le code à peu près fonctionnel qui nous permettrait d'appeler la fonction "test" indirectement  par un pointeur:

Code:

void test(void)
{
}

void main(void)
{
    void (*PF)(void) ;    // définit une variable pointeur de fonction

    PF = test ;    
    PF() ; // appel de la fonction
}


Comme la lecture de la déclaration de la variable "PF" n'est pas évidente, je préfère passer par un "typedef"

Code:

typedef void (*TPF)(void) ;

void test(void)
{
}

void main(void)
{
    TPF PF   ; // définit une variable de type TPF

    PF = test ;    
    PF() ; // appel de la fonction
}


Voici un autre exemple (probablement fonctionnel ;)de pointeur de fonction, cette fois-ci avec un paramètre d'entrée:

Code:

typedef void (*TPF)(String) ;

void test(String mes)
{
  Serial.print(mes) ;
}

TPF PF ;

void setup()
{
  PF = test ;
  Serial.begin(9600) ;
}

void loop()
{
    PF("bonjour") ;
   delay(1000) ;
   PF(" le monde\n") ;
  delay(1000) ;
}


À mon humble avis, comprendre la notion de pointeur de fonction est essentiel si on veut faire de la programmation modulaire stucturée, histoire d'éviter des références circulaires.

Prenons par exemple l'application "TestComProt" qui utilise le module de plus bas niveau "TgeComProt". L'application "connait" le module  "TgeComProt", mais l'inverse n'est pas vrai, puisque le module "TGeComProt" a été écrit avant son programme test ou tout autre application qui pourrait utiliser le module.

Alors comment fait le module "TgeComProt" pour appeler une fonction dans un autre module qui n'existe pas encore? Par un pointeur de fonction!

Regarde attentivement le code de "TestComProt":

Code:

#include <TgeComProt.h>

TgeComProt comProt ;

void TreatCommand(String cmd)

{
 //
}

void setup()
{
  comProt.Setup(9600,TreatCommand) ;
}

...


La méthode "SetUp" de TgeComProt passe en paramètre l'adresse de la fonction qui doit être appelé lorsqu'une commande a été reçue.

Cette adresse sera mémorisée par la variable "fOnRxCmd" de la classe "TgeComProt", ce qui permettra d'appeler la fonction "TreatCommand" de l'application en lui passant comme paramêtre la commande reçue:

fOnRxCmd(fCmd) ;

Bon, désolé pour la réponse un peu longue, c'est une tactique de vieux prof qui consiste à assommer l'élève avec une réponse interminable, ce qui évite d'autres questions de l'élève smiley-wink smiley-wink smiley-wink






« Last Edit: November 29, 2010, 06:55:30 am by Guyt54 » Logged

Ales
Offline Offline
Faraday Member
**
Karma: 29
Posts: 3186
Do or DIY
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
Bon, désolé pour la réponse un peu longue, c'est une tactique de vieux prof qui consiste à assommer l'élève avec une réponse interminable, ce qui évite d'autres questions de l'élève ;) ;) ;)

Enfin un prof qui l'assume  ;D
Logged


Geneva
Offline Offline
Faraday Member
**
Karma: 24
Posts: 3171
Yoplait... le pt'it suisse
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Sous forme de tuto :

A l'assaut des pointeurs

 smiley-wink
Logged

MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

Montréal
Offline Offline
Newbie
*
Karma: 0
Posts: 48
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
Enfin un prof qui l'assume

Après 33 ans dans l'enseignement, on finit par s'assumer.

Plus que 2 ans et c'est la retraite. Heureusement que je suis pas français, il m'en resterait 17 à travailler!  smiley-grin

Logged

Geneva
Offline Offline
Faraday Member
**
Karma: 24
Posts: 3171
Yoplait... le pt'it suisse
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Tu enseignes où ?
Logged

MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

Montréal
Offline Offline
Newbie
*
Karma: 0
Posts: 48
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
Tu enseignes où ?

J'enseigne au collège Lionel-Groulx au Québec dans un programme de Technologie du génie électrique. Le programme dure 3 ans et se situe entre le lycée et l'université. À peu près 50% de mes étudiants poursuivent à l'université pour obtenir un diplôme d'ingénieur.
Logged

Sophia-Antipolis (06)
Offline Offline
God Member
*****
Karma: 4
Posts: 618
Rejoignez le club !
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Merci pour la réponse.
Logged

Geneva
Offline Offline
Faraday Member
**
Karma: 24
Posts: 3171
Yoplait... le pt'it suisse
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Doivent être sympa tes cours.  smiley-wink
Logged

MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

Montréal
Offline Offline
Newbie
*
Karma: 0
Posts: 48
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Bon, récapitulons où nous en sommes.

La classe TgeComProt nous permet d'implanter facilement notre protocole de communication entre le PC et la carte Arduino par le port sériel.

Rappelons par un exemple le protocole en question. Le PC transmet une commande au Arduino via une trame de caractères ASCII délimitée par les caractères "<" et ">". Les données seront des caractères ASCII représentant un nombre hexadécimal.

Par exemple, supposons qu'on veut lire les entrées analogiques du Ardinio à partir du PC.

Le PC pourrait envoyer la commande suivante:

<Abb>

où:
A: Commande entrée analogique
bb: Numéro de l'entrée analogique (sur 8 bits)

Le Ardinio va répondre par la trame suivante:

<Abbddddd>
où:
dddd: valeur lue (sur 16 bits)

Ainsi, si l'entrée analogique 2 est à sa valeur maximum (1023), on aurait:

PC->Arduino:  <A02>
Arduino->PC: <A0203FF>

Pour le traitement de données, il nous faudra des fonctions nous permettant de passer facilement du binaire à une chaine de caractères représentant un nombre hexadécimal et vice-versa, genre:

int  HexStringToBin(String stg) ;
String ByteToString(byte data) ;

On rappelle ici les règles du jeu. On est en programmation orientée objet, alors toutes les fonctions de la librairie devront être encapsulées dans des classes.

J'entends déjà mes éleves dire "Guyt, t'es pas pour nous demander  de mettre des fonctions "ordinaires" dans des classes?". Et je répondrai "Bin oui, c'est moi le patron ici", la dictature étant mon mode de fonctionnement préféré (tant que c'est moi, le dictateur, évidemment;) )

Par exemple, supposons que nous désirons mettre dans une classe deux fonctions équivalentes à "bitSet" et "bitClear".  Ça pourrait ressembler à ça:

Code:
// déclaration de la classe
class TBit
{
  public:
  static int Set(int data, int bitPos) ;
  static int Clear(int data, int bitPos) ;
} ;

// définition des fonctions de classe

int TBit::Set(int data, int bitPos)
{
  return data | (1 << bitPos) ;
}

int TBit::Clear(int data, int bitPos)
{
  return data & ~(1 << bitPos) ;
}

La clé ici, c'est le qualificatif "static" qui précède la définition des fonctions de classe "Set" et "Clear". Ce qu'on dit essentiellement, c'est que ces fonctions  sont "globales", mais connues seulement
à l'intérieur de la classe. Elles sont donc tout à fait équivalentes à:

Code:

int TBitSet(int data, int bitPos)
{
  return data | (1 << bitPos) ;
}

int TBitClear(int data, int bitPos)
{
  return data & ~(1 << bitPos) ;
}


Pour utiliser les fonctions de la classe, on pourrait toujours définir une variable de type "TBit", par exemple:

TBit bit ;

data = bit.Set(data,2) ;

Mais c'est parfaitement inutile (et inefficace) , on a qu'à préciser que la fonction "Set" fait parti de la classe "TBit":

data = TBit::Set(data,2) ;

Évidemment (un mot généralement à éviter en pédagogie smiley-wink, les fonctions statiques "Set" et "Clear" ont les défauts de leurs qualités. Comme ce sont dans les faits des fonctions globales, elles n'ont pas accès aux variables définies dans la classe, mais bon, fallait bien les encapsuler ces fonctions, alors c'est fait!

Bon, enfin, voici l'interface de notre module "THex" de fonctions de manipulation binaire <--> chaine hex:

Code:
// THex.h: Fonctions générales de tranformation hex. binaire <--> ASCII
// Guy Tessier, nov. 2010

#ifndef THEX_H
#define THEX_H

#include "WProgram.h"

class THex
{
  public:

  // Binaire à String
  static char   NibbleToChar(byte data) ;
  static String ByteToString(byte data) ;
  static String WordToString(word data) ;
  
  //String à Binaire
  static char    CharVal(char car) ;
  static boolean StringToWord(word& res, String stg, byte fromIndex, byte digits=4) ;
  static boolean StringToByte(byte& res, String stg, byte fromIndex) ;
} ;

#endif

Et son implantation:

Code:
// THex.cpp: Fonctions générales de tranformation hex. binaire <--> ASCII
// Guy Tessier, nov. 2010

#include "THex.h"

char THex::NibbleToChar(byte data)
// Retourne le chiffre hex. (en caractère) des 4 bits les moins significatifs de "data"
{
  data = data & 0x0F ;
  if (data < 10)
    data = '0' + data ;
  else
    data = 'A' + (data-10) ;
  return data ;
}

String THex::ByteToString(byte data)
// Retourne la valeur 8 bits en hex
{
  String res ;

  res = "xx" ;
  res[0] = NibbleToChar(data >> 4)  ;
  res[1] = NibbleToChar(data)  ;
  return res ;
}  

String THex::WordToString(word data)
// Retourne la valeur 16 bits en hex
{
  return ByteToString(data >> 8) +ByteToString(data)  ;
}  

char THex::CharVal(char car)
// Retourne la valeur binaire du car. hex (retourne -1 si erreur)
{
  if (car >= '0' && car < '9')
    car = car - '0' ;
  else
    if (car >= 'A' && car <= 'F')
      car = car - 'A' + 10 ;
    else  
      if (car >= 'a' && car <= 'f')
      car = car - 'a' + 10 ;  
    else
      car = -1 ; // 0xFF
  return car ;  
}

boolean THex::StringToWord(word& res, String stg, byte fromIndex, byte digits)
// Retourne "vrai" ssi la chaine "stg" contient un nombre hex légal commençant à "fromIndex"
// avec "digits" chiffres (valeur par défaut: 4)
// Si OK, "res" va contenir la valeur binaire de lachaine
{
  word tmp ;
  char digit ;
  boolean ok ;

  tmp = 0 ;
  ok = (fromIndex + digits <= stg.length()) ;
  while (digits && ok)
  {
    digit = CharVal(stg[fromIndex]) ;
    ok = (digit != -1) ;
    if (ok)
     {  
      tmp = (tmp << 4) + digit ;
      fromIndex++ ;
      digits-- ;
    }  
  }
  if (ok)
    res = tmp ;
  return ok ;    
}  

boolean THex::StringToByte(byte& res, String stg, byte fromIndex)
{
   bool ok ;
   word tmp ;
  
   ok = StringToWord(tmp,stg,fromIndex,2) ;
   if (ok)
     res = tmp ;
   return ok ;  
}  


Faut que je file au boulot (c'est fou comment ça peut nuire au travail), j'élabore sur l'utilisation de ces fonctions dans le prochain topo.

A+



« Last Edit: November 30, 2010, 05:54:20 am by Guyt54 » Logged

Montréal
Offline Offline
Newbie
*
Karma: 0
Posts: 48
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Bon voilà, voici le programme de contrôle des entrées/sorties du Arduino Uno à partir d'un PC, avec le petit protocole décrit précédemment.

La version présente effectue des lecture/écriture de bits selon la syntaxe décrite dans l'entête du programme.

Par exemple, pour allumer la LED test (sortie 13):

PC->Uno :  <BW0D01>    Uno->PC <BR0D01> (écho)

Code:
/*
==============================================================================
UnoCtrl.cpp Programme de contrôle entrées/sorties via le protocole de
            communication maitre/esclave TGE
Guy Tessier, décembre 2010

Commande implantées:

Écriture bit:
PC->Ctrl: <BWnndd>  où nn= numéro du bit    dd: 00 ou 01
Ctrl->PC: <BWnndd> (echo)

Lecture bit:
PC->Ctrl: <BRnn>
Ctrl->PC: <BRnndd>  

Enable pullup sur lecture de bit:
PC->Ctrl: <BPdd> où dd=00 (enable) ou 01 (disable)
===============================================================================
*/

#include <TgeComProt.h>
#include <THex.h>

TgeComProt comProt ;

String rxCmd ; // Commande reçue (sans délimiteurs)
String txCmd ; // Commande à transmettre
bool   enablePullups ; // si vrai, les


void CmdError(void)
// Doit être appelé quand il y a une erreur dans rxCmd
{
  txCmd = "ERR" ;
}

// -----------------
// Commandes de bits
// -----------------

void TreatWriteBitCmd(void)
// Traitement
// Syntaxe: BWbbnn  où bb= bit nn=00 ou 01

{
  byte bitPos,data ;
  if (THex::StringToByte(bitPos,rxCmd,2) && THex::StringToByte(data,rxCmd,4))
  {  
    pinMode(bitPos,OUTPUT) ;
    digitalWrite(bitPos,data) ;
    txCmd = "BW" + THex::ByteToString(bitPos) +  THex::ByteToString(data);
  }
 else CmdError() ;  
}  

void TreatReadBitCmd(void)
// Lecture d'une entrée numérique
// Syntaxe: BRbb où bb= bit nn=00 ou 01

{
  byte bitPos,data ;
  if (THex::StringToByte(bitPos,rxCmd,2) && rxCmd.length()==4)
  {  
    pinMode(bitPos,INPUT) ;
    digitalWrite(bitPos,enablePullups) ; // enable#disable pull up
    data = digitalRead(bitPos) ;
    txCmd = "BR" + THex::ByteToString(bitPos) +  THex::ByteToString(data);
  }
 else CmdError() ;  
}  

void TreatEnablePullups(void)
// Enable/disable pull-ups sur lecture
// Syntaxe: BPbb où bb= 00 (disable) 01=enalble

{
  byte data ;
  
  if (THex::StringToByte(data,rxCmd,2) && rxCmd.length()==4)
  {  
    enablePullups = (data != 0) ;
    txCmd = "BP" + THex::ByteToString(data) ;
  }
 else CmdError() ;  
}  

void TreatBitCmd(void)
// Aiguillage des commandes de lecture de bits

{
  switch (rxCmd[1])
  {
    case 'W' :
      TreatWriteBitCmd() ;
      break ;
    case 'R' :
      TreatReadBitCmd() ;
      break ;
    case 'P':
      TreatEnablePullups() ;
      break ;
    default:  
      CmdError();          
  }
}

// ----------------------------------------------
// Point d'entrée traitement de la commande reçue
// ----------------------------------------------

void TreatCommand(String cmd)
// point d'entrée des commandes
{
  rxCmd = cmd.toUpperCase()  ;
  switch (rxCmd[0])
  {
    case 'B' :
      TreatBitCmd() ;
      break ;
    default:  
      CmdError();
      break ;      
  }  
  comProt.Send(txCmd) ;
}

// -------------------
// Programme principal
// -------------------

void setup()
{
  comProt.Setup(9600,TreatCommand) ;
  enablePullups = true ;
}

void loop()
{
  comProt.TreatReceive() ;
}


Prochain module de la librairie: un petit gestionnaire de tâches périodiques.

Bonne journée!
Logged

Montréal
Offline Offline
Newbie
*
Karma: 0
Posts: 48
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Là, j'avoue que j'ai hâte de vous le montrer, ce module. En fait, c'est fonctionnel, faut juste que je documente ça un peu mieux. Je suis sûr que je suis pas le premier qui fait ça* ici, mais vous savez comment c'est, quand ça fonctionne, on a tellement hâte de le dire au moins à quelqu'un!

En gros, le module permet l'appel automatique de fonctions. La période d'appel  est spécifique à chaque fonction.

Voici le code de mon programme test. Il transmet à toutes les secondes au port sériel  un compte  qui est incrémenté aux 5 millisecondes, pendant que la fameuse LED pin 13 change d'état à toutes les 250 millisecondes:

Code:
#include <TaskManager.h>
#include <TgeComProt.h>
#include <THex.h>

long cnt ;

void incCnt(void)
{
  cnt++ ;
}  
void blinker(void)
{
  static boolean flag ;

  flag = !flag ;
  digitalWrite(13,flag) ;
}

void transmit(void)
{
  Serial.print(cnt) ;
  Serial.print("\n") ;  
}  

void setup()
{
   Serial.begin(9600) ;
   pinMode(13,OUTPUT) ;
   TaskManager.Setup(TM_5MS)  ;
   TaskManager.Add(incCnt, 1) ;  
   TaskManager.Add(blinker, 50) ;
   TaskManager.Add(transmit, 200) ;  
}

void loop()
{
  // fait rien!
}


A+

* Effectivement, mon Guyt, t'es pas le premier qui fait ça:

http://www.arduino.cc/playground/Code/Timer1

Mince consolation, ta version fait la gestion de plusieurs fonctions de callback (mais si tu cherches encore un peu plus, tu vas sûrement trouvé mieux  smiley-razz)


« Last Edit: December 01, 2010, 09:14:16 pm by Guyt54 » Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 90
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello

Tres sympa ces codes. Mais qlq petites questions quand même, juste sur ton protocole:
  • Pourquoi tu remets la commande envoyée en echo, et pas simplement un "1" pour dire que le module a bien reçu et compris la commande ?
  • Pourquoi utiliser comme base 5ms pour l'appel périodique d'une fonction ?
  • A quand une gestion des taches/fonctions prioritaires ? (je suis sur que tu dois avoir ça dans un carton smiley-wink )

Sinon, merci de partager smiley
Logged

Montréal
Offline Offline
Newbie
*
Karma: 0
Posts: 48
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Bonjour erlik,

D'abord, merci pour le feedback positif.

Voici la réponse à tes questions

1) Pourquoi tu remets la commande envoyée en echo, et pas simplement un "1" pour dire que le module a bien reçu et compris la commande ?

T'as raison, mais faut que tu gardes en tête que le code que je mets ici, c'est du code de prof. Alors faut que je fasse attention pour être consistant avec les principes que je tente d'inculquer à mes élèves. Si on est pour faire du contrôle par lien de communication, on a intérêt à utiliser un protocole solide. Celui proposé permet une certaine validation des données transmises et du travail qui est supposé avoir été effectué.

 
2) Pourquoi utiliser comme base 5ms pour l'appel périodique d'une fonction ?

Pour le 5 ms, c'est simplement un exemple. La fonction "Setup" de "TaskManager" peut recevoir d'autres base de temps en paramètre. Je vais documenté ca bientôt.

3) A quand une gestion des taches/fonctions prioritaires ? (je suis sur que tu dois avoir ça dans un carton Wink )

C'est une grosse job, que je garderais pour la retraite, mais comme je compte surtout faire du windsurfing ( www.guyt54.blogspot.com), y a peu de chance que je le fasse.

D'ailleurs, le terme "TaskManager" que j'utilise est abusif, car ce que je propose est loin d'être un gestionnaire de tâche. J'avais pensé au terme "Scheduler (Ordonnanceur)" mais suis pas certain non plus. Quelqu'un a une suggestion?
Logged

Pages: [1] 2   Go Up
Jump to: