WellBot
Le WellBot est un petit robot développé par la société Didel dans le but d'apprendre aux jeunes à faire un peu d'électronique et de micro-informatique.
Le kit peut être réalisé sans aucune notion de programmation, juste pour le plaisir de se "souder les doigts" et avoir un petit robot rigolo qu'on a eu le plaisir de faire soi-même. C'est déjà beaucoup, et certainement suffisant pour la plupart, mais en même temps on peut faire plus que cela : faire des premier pas en informatique embarquée et comprendre comment fonctionne un système informatique rudimentaire.
Il est vrai que l’on peut apprendre à programmer sur un ordinateur, c’est joli tout de suite, mais en même temps, les PC d'aujourd'hui sont des machines complexes, avec des OS complexes, des environnement de développement complexes, si bien qu’à la fin, tout est assez abstrait sur comment cela fonctionne.
Avec des kits comme le WellBot, on retourne aux débuts de l'informatique : un hardware minimaliste (même hyper simple puisque le WellBot utilise un PIC de la société Microchip[1].
Le but de ce cours est d'aller au delà de la documentation de Didel, pour essayer d'expliquer plus en détail le fonctionnement de ce Kit et de ce que l’on peut en faire. Comme toujours, faire un bon cours est long et n'est jamais achevé... d'où l’idée de le faire sur Wikiversité et que d'autres puissent corriger les erreurs et poursuivre l'effort.
Hardware
[modifier | modifier le wikicode]Le processeur
[modifier | modifier le wikicode]Le processeur est un PIC16f882, dont on trouve le databook ici [2]
Le Schéma du robot
[modifier | modifier le wikicode]Le schéma donné par Didel est le suivant:
On voit sur ce schéma que le cœur du système est un PIC 16F882. C'est à lui seul un petit ordinateur. On retrouve dans un PIC tous les concepts de base d'un ordinateur, mais en très simple.
Les moteurs pas à pas
[modifier | modifier le wikicode]On voit que le PORTB est utilisé pour piloter deux moteurs pas à pas. On se reportera à la description de Didel sur les moteurs pas à pas.
Les détecteurs de proximités
[modifier | modifier le wikicode]Le Robot peut avoir deux capteurs de distance IR.
Attention : pour une raison étrange, le pinning des capteurs ne correspond pas sur mon robot à celui des capteurs ; on voit sur la photo que j’ai dû croiser les pattes des capteurs (en mettant de la gaine thermo pour qu’elles ne se touchent pas). Le pinning des détecteurs est plus visible sur ce document.
On voit sur cette photo les deux connecteurs pour les détecteurs de distance. On voit aussi quelle I/O (RA0 et RA1) est responsable de quel détecteur. Le système adopté ici est d’utiliser la pin en sortie pour charger le condensateur, puis de la commuter en entrée et de regarder à quelle vitesse le condo se décharge via le photorécepteur IR. Plus il y a de lumière, plus la décharge est rapide et donc plus on est près d'un obstacle. La méthode complète pour lire les capteur est décrite ici.
Dans le code ci-dessous, on voit une implémentation simple de la lecture des capteurs. Cette routine doit être appelée régulièrement. La dernière distance lue se trouve dans DistGauche et DistDroite respectivement. On voit que cette implémentation n'est que partielle : elle ne tient pas compte de la lumière ambiante, ni du fait que la décharge du condensateur n’est pas linéaire avec la distance. Mais cela donne déjà des résultats corrects
//--------------------------------------------------------------------- // Capteurs de distance IR //--------------------------------------------------------------------- byte EtatIRGauche=255, DistGauche=255; byte EtatIRDroite=255, DistDroite=255;
void GererCapteursIR(void) { HP_nSENSOR = 1; // allume les leds IR EtatIRDroite++; if (EtatIRDroite==0) { SENSOR_D_IN = 0; //on recharge le condensateur en utilisant l'entrée comme sortie SENSOR_D = 1; // qu'on met à 1 } else { SENSOR_D_IN = 1; if ((SENSOR_D==0) || (EtatIRDroite ==255)) { // si la sortie est passée à 0, le capteur a laissé passer assez de courant pour décharger le condo // le temps pris est une estimation de la distance // si on a mis 255 états, on met aussi la distance à 255, cela ne sert plus à rien d'attendre (trop loin) DistDroite = EtatIRDroite; EtatIRDroite=255; // on recommence le cycle LED_SW = !LED_SW; } } EtatIRGauche++; if (EtatIRGauche==0) { SENSOR_G_IN = 0; //on recharge le condensateur en utilisant l'entrée comme sortie SENSOR_G = 1; // qu'on met à 1 } else { SENSOR_G_IN = 1; if ((SENSOR_G==0) || (EtatIRGauche ==255)) { // si la sortie est passée à 0, le capteur a laissé passer assez de courant pour décharger le condo // le temps pris est une estimation de la distance // si on a mis 255 états, on met aussi la distance à 255, cela ne sert plus a rien d'attendre (trop loin) DistGauche = EtatIRGauche; EtatIRGauche=255; // on recommence le cycle } } }
Cycle de développement
[modifier | modifier le wikicode]On se reportera à Bico64#Comprendre le cycle de développement pour une description du cycle de développement et des outils à installer
Exemples de programmes
[modifier | modifier le wikicode]Evite Les Bords
[modifier | modifier le wikicode]Ce programme est assez simple et minimaliste: il essaie d’éviter les bords d'un labyrinthe.
Il ne met en œuvre que les moteurs, les capteurs de distance et la Led.
La Led représente l'état du capteur de distance Gauche; elle clignote d'autant plus vite qu'on est près du mur.
//------------------------------------------------------------------------------------------
// Programme de démonstration WellBot qui évite les bords
//------------------------------------------------------------------------------------------
#define __16F882
#include <pic16f887.h>
//------------------------------------------------------------------------------------------
// Configuration du PIC 16F882 selon WellBot
//------------------------------------------------------------------------------------------
__code __at _CONFIG1 unsigned int Config1 =
_INTOSCIO // I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN
& _WDT_OFF // pas de Watch Dog
& _PWRTE_ON // on attend un peu au démarrage
& _MCLRE_OFF // RE3/MCLR pin function is digital input, MCLR internally tied to VDD
& _CP_OFF // Program memory code protection is disabled
& _CPD_OFF // Data memory code protection is disabled
& _BOR_OFF // BOR Disabled
& _IESO_OFF // Internal/External Switchover mode is disabled
& _FCMEN_OFF // Fail-Safe Clock Monitor is disabled
& _LVP_OFF // RB3 pin is digital I/O, HV on MCLR must be used for programming
& _DEBUG_OFF;// In-Circuit Debugger disabled, RB6/ICSPCLK and RB7/ICSPDAT are general purpose I/O pins
__code __at _CONFIG2 unsigned int Config2 =
_WRT_OFF // No prog memmory write protection
& _BOR21V; // Brown-Out Reset at 2.1V
#define byte unsigned char
#define SENSOR_D RA0
#define SENSOR_G RA1
#define BICO_DATATOBICO RA2
#define MICRO RA3
#define BICO_DATAFROMBICO RA4
#define BICO_CLK RA5
#define SENSOR_D_IN TRISA0
#define SENSOR_G_IN TRISA1
#define SWITCH_IN TRISC0
#define LED_SW RC0
#define HP_LEDIR RC1 // à 1, allume les LEDs IR des Sensors. des transitions entre 0 et 1 font du son
// sur le HP ==> faire un son perturbe la lecture de distance
#define IR RC2 // pour reception IR
// la position d'un moteur est gérée de manière un peu particulière:
// un int contient à la fois le n° de pas du moteur (de 0à5) et
// un byte contient le micro pas (une fraction de pas)
// l’idée est que pour faire tourner lentement le moteur,
// on incrémente (ou décrémente) la position d'une valeur de -256 à + 256
// Si on incrémente de 256, cela revient à incrémenter d'un pas complet, sinon, de n/256 de pas
// On pourra donc faire cela de manière régulière (p.ex tous les 300us)
typedef union {
struct {
byte MicroPas;
char Pas; //on aura le n° du pas (0..5)
};
struct {
int Pos;
};
} TPosMoteur;
TPosMoteur PosGauche,PosDroite;
void Setup(void) {
ANSEL = 0b00000000; // aucune pin en analogique
TRISA = 0b11011011;
TRISB = 0b00000000;
TRISC = 0b10111100; // RC0 peut être programmé comme entrée (lire le switch) ou comme sortie (LED)
PosGauche.Pos = 0;
PosDroite.Pos = 0;
}
byte BitsPas[6] = { // ° sur dessin de Step.pdf
0b00001100, // 0
0b00000100, // 60
0b00000111, // 120
0b00000011, // 180
0b00001011, // 240
0b00001000, // 300
};
void GererMoteur(int VitesseGauche, int VitesseDroite){
// la procedure gérer moteur doit être appelée régulièrement (max toutes les 300us)
// les vitesses doivent être comprises entre 256 et -256 (256 = 100% vitesse max)
PosGauche.Pos -= VitesseGauche; // symétrique par construction ==> faire tourner à l’envers
if (PosGauche.Pas >= 6) {
PosGauche.Pas = 0;
} else if (PosGauche.Pas < 0) {
PosGauche.Pas = 5;
}
PosDroite.Pos += VitesseDroite;
if (PosDroite.Pas >= 6) {
PosDroite.Pas = 0;
} else if (PosDroite.Pas < 0) {
PosDroite.Pas = 5;
}
PORTB = (BitsPas[PosDroite.Pas]<<4 | BitsPas[PosGauche.Pas]);
}
//---------------------------------------------------------------------
// Capteurs de distance IR
//---------------------------------------------------------------------
byte EtatIRGauche=255, DistGauche=255;
byte EtatIRDroite=255, DistDroite=255;
void GererCapteursIR(void) {
HP_LEDIR = 1; // allume les leds IR
EtatIRDroite++;
if (EtatIRDroite==0) {
SENSOR_D_IN = 0; //on recharge le condensateur en utilisant l'entrée comme sortie
SENSOR_D = 1; // qu'on met à 1
}
else {
SENSOR_D_IN = 1;
if ((SENSOR_D==0) || (EtatIRDroite ==255)) {
// si la sortie à passé à 0, le capteur a laisser passé assez de courant pour décharger le condo
// le temps pris est une estimation de la distance
// si on a mis 255 états, on met aussi la distance à 255, cela se sert plus a rien d'attendre (trop loin)
DistDroite = EtatIRDroite;
EtatIRDroite=255; // on recommance le cycle
}
}
EtatIRGauche++;
if (EtatIRGauche==0) {
SENSOR_G_IN = 0; //on recharge le condensateur en utilisant l'entrée comme sortie
SENSOR_G = 1; // qu'on met à 1
}
else {
SENSOR_G_IN = 1;
if ((SENSOR_G==0) || (EtatIRGauche ==255)) {
// si la sortie à passé à 0, le capteur a laisser passé assez de courant pour décharger le condo
// le temps pris est une estimation de la distance
// si on a mis 255 états, on met aussi la distance à 255, cela se sert plus a rien d'attendre (trop loin)
DistGauche = EtatIRGauche;
EtatIRGauche=255; // on recommance le cycle
LED_SW = !LED_SW;
}
}
}
int max=50;
int VG=256;
int VD=256;
void Loop(void) {
int i,n;
GererCapteursIR();
if ((DistDroite <= 50) && (DistGauche <= 50)) {
//on est proche d'un mur à gauche et à droite donc coincé
//le mieux est de tourner sur place pour débloquer la situation
GererMoteur(100,-100);
}
else {
// On avance avec la roue droite proportionnellement à la distance vue avec l'œil Gauche et inversement
// plus on a de place a gauche, plus la route droite va vite (donc on tourne à gauche)
// évidemment si on est loin tant à gauche qu’à droite, on va à vitesse max des deux côtés, donc tout droit
GererMoteur(DistDroite,DistGauche);
}
for (i=0;i<=max;i++) {}
}
void main(void) {
Setup();
while (1) {
Loop();
}
}
Autres Liens
[modifier | modifier le wikicode]Voici les liens vers les documents originaux de Didel, qui ne sont pas toujours très facile à trouver.
- Architecture des PICS [3]
- Tout sur les PIC donne accès à plein de liens où l’on trouve de l'information
- Apprendre à programmer avec le 16F877A [4] C'est un document pas à pas, expliquant le PIC, les instructions en CALM, mais pas fait explicitement pour le WellBot, mais plutôt pour d’autre kits.
- CALM pour PIC [5]
- Utiliser les PIC 16F et 18F