Florent de LAMOTTE
12 septembre 2002
Cette documentation a pour but de permettre au lecteur de mieux comprendre l'organisation du code source de Snooky.
Le code de la carte principale de Snooky est, à l'exception du code de démarrage crt0.s, entièrement écrit en C.
Ce code est localisé dans un seul répertoire contenant à la fois le code source, le Makefile permettant d'obtenir des binaires, le script pour l'éditeur de lien ... en fait tout ce qui concerne le développement pour la carte.
Le code à été décomposé en modules pour apporter une meilleure structuration. Pour chaque module, on trouve un fichier source écrit en C, ainsi que son header qui contient des déclarations de types, les variables globales ainsi que les prototypes des fonctions qui peuvent être appellées à l'exterieur de ces modules.
Le fichier Changelog présent dans le répertoire du code contient un récapitulatif des changements effectués sur ce même code. Le fichier TODO contenait lui les différentes choses qu'il restait à implémenter. Ces deux fichiers n'ont pas forcément été très bien mis à jour1 mais reflètent les grandes modifications qui ont été apportées au code.
Des archives de ce code ont été faites de manière régulière et on été timestampées, il devrait être assez aisé de retrouver le code tel qu'il était pendant les différentes phases de conception de la carte. Les dates d'archivage correspondent normalement à l'écriture d'une nouvelle entrée dans le Changelog.
Trois modules s'occupent de fonctions de bas niveau :
Le code de l'interruption timer se situe dans le module timer.c.
L'action principale effectuée lors de cette interruption concerne le polling sur le bus I2c :
test_pic leds ^= LED_ROUGE; if (get_status()==0xFF) leds &= ~LED_VERTE1; else leds |= LED_VERTE1; if (get_meca_status()==0xFF) leds &= ~LED_VERTE2; else leds |= LED_VERTE2; get_meca_couleur_boule(); release_pic
Comme on peut le voir dans ce bout de code tiré de l'interruption, on récupère le status de la carte d'asservissement et de la carte méca. Suivant l'état de chacune des cartes, on positionne correctement les leds. Les fonctions get_status et get_meca_status mettent aussi à jour des variables concernant la carte mécanique et la carte électronique, utilisées dans les fonctions du programme.
L'appel à get_meca_couleur_boule permet de récupérer la couleur des boules présentes dans le barillet et mets donc à jour les variables correspondant pour des calculs de stratégie.
L'appel aux macros test_pic2 et releases_pic est obligatoire pour s'assurer que l'on accède pas au pic dans le code alors que l'on y accède dans une interruption et vice-versa.
Cette partie du code est aussi responsable du clignotement de la led rouge qui indique que la liaison i2c est fonctionnelle3.
On demande aussi dans l'interruption des nouvelles de notre ennemi, principalement pour des raisons stratégiques :
test_pic get_info_adv(); release_pic if (adv_nearby_panier) paniers_visites_adv[adv_panier] = 1;
La fonction get_info_adv s'occupe en effet de demander des informations concernant l'ennemi à la carte d'asservissement et de mettre à jour les variables correspondantes.
Ici, si l'adversaire est proche d'un panier, on positionne un flag indiquant qu'il est allé le visiter. Ce code permet de savoir quels paniers on peut aller piller.
La deuxième fonction de ce code d'interruption concerne le debugging.
Tout d'abord, en ce qui concerne les cartes sur le bus I2C, on positionne les leds selon l'état (connecté ou déconnecté) de chaque carte, les leds correspondantes sont les deux leds vertes.
Ensuite, on utilise la led jaune pour refléter l'état actuel de la connection avec la caméra.
switch (cam_error) { case 0: leds |= LED_JAUNE; break; case 100: if (system_time % 2 == 0) leds ^= LED_JAUNE; break; case 101: leds &= ~LED_JAUNE; default: leds &= ~LED_JAUNE; break; }
Le code suivant n'est effectif que si le dernier jumper est relevé. Il permet d'envoyer dans ce cas, au travers de la liaison série, des informations au programme simu_cam [1].
if ((system_time % 2 == 0) && ((SIM.PORTE0 & 0x10))) { test_pic get_position(); get_position_adv(); get_meca_mode_fonctionnement(); release_pic send_position(); send_asser_status(); send_meca_status(); send_info_adv(); }
La dernière partie de cette interruption permet à l'utilisateur du robot de savoir si le robot est prêt à recevoir la tirette. Dans ce cas, les leds perdent leur signification normalent et changent d'etat toutes ensemble toutes les secondes.
if (ready) { LEDS = leds; } else { if (system_time % 4 == 0) { LEDS = ~LEDS & (leds | LED_ROUGE); } }
L'initialisation de la carte principale s'effectue dans la fonction main, localisée dans le module snooky_brain.
Les lignes qui suivent tentent de décortiquer la phase d'initialisation de la carte principale. j'avouerais que cette phase d'initialisation est un peu chaotique. Avec un peu d'explications, j'espère que ça deviendra un peu plus clair.
/* * Initialisation du 332 */ empty_i2c_buffer(); get_pic
On commence par intialiser les routines i2c et on réserve le pic. Celui-ci est réservé pendant toute la phase d'initialisation du programme.
// Fsysteme = Fquartz[4*(60+1)*2]=15990784 Hz SIM_SetClock (0, 1, 55); // SIM_SetClock (0, 1, 60); SCI_Init ();
Les routines SIM_SetClock et SCI_Init fait partie des routines de la libESEO. On ajuste la vitesse du 68332 à 15Mhz pour pouvoir ensuite faire fonctionner la liaison série à 115200 bps.
La fonction SCI_Init fixe les paramètres par défaut du port série.
exception_table.auto_vector[4] = (longword) I2C_int; /* Configuration de cs5 pour repondre a l'int */ SIM.CSBAR5 = 0xFFF8; SIM.CSOR5 = 0x2C0B; SIM.CSPAR0 |= 0x0800; // exception_table.user[2] = (longword) int_serial; exception_table.user[2] = (longword) int_cam; QSM_SetIntVector(66); SCI_SetIntLevel(6); //SCI_ReceiverInt(ENABLE); QSM.SCCR1Low.RIE = 1;
Il nous faut configurer les interruptions. Dans le fichier header mc68332.h nous avons défini une correspondante entre le tableau auto_vector et les interruptions autovectorisées.
Il faut faire attention, le premier vecteur auto vectorisé correspond à la première entrée dans le tableau. Ceci signifie que l'auto-vecteur 1 correspond à la variable exception_table.auto_vector[0].
Ici, on assigne donc à l'interruption I2C_int l'interruption 5.
Le code utilisé pour gérer l'interruption timer est int_cam, c'est une version améliorée de int_serial qui peut gérer directement une partie de la communication avec la caméra. Cette possibilité n'est par contre pas utilisée. (voir la partie concernant la caméra)
De plus pour autoriser l'interruption série, on écrit directement dans le registre, sans passer par les librairies (la fonction utilisant la librairie est composée). Ceci est un vieux relicat des longues heures de debugging.
/* pe7 en sortie (reset du PIC) */ SIM.DDRE |= 0x80; SIM.PORTE0 &= 0x7F; /* pf3467 en sortie */ SIM.PFPAR |= 0x20; SIM.DDRF |= 0xD8; SIM.PORTF0 = 0; /* on eteinds les leds */
Ensuite on configure les ports d'entrée-sortie pour le carte externe.
/* Ajustement du masque d'interruption */ asm("move.w #0x2200, %sr"); /* force la vitesse du port serie * 9 => 57400 (F = 16MHz) * 8 => 57400 (F = 14MHz) * 5 => 115200 (F = 14MHz) */ QSM.SCCR0 = 4; // 115200@14MHz exception_table.user[0] = (longword) int_timer; SIM_SetIntVector(64); /* 64 : 1er vecteur utilisateur */ SIM_SetIntLevel(4); // 4 pour l'enabler SIM.PITR = 0x0102; // periode = PITM/16
On configure l'interruption timer.
/* * Initialisation du programme */ init_terrain(); I2C_reset(); cam_init(); release_pic
Finalement, on initialise le programme et on relache le pic.
Les routines qui permettent de gérer la carte d'asservissement sont localisées dans le module deplacement.c.
Ces routines utilisent les routines de gestion du bus I2C dont les prototypes sont dans l'header i2c.h.
Les messages envoyés sur le bus sont composés de la commande, suivie des différents paramètres. Le tableau suivant présente ces différentes commandes, explicite les paramètres et donne le nom des fonctions utilisant ces commandes.
COMMANDE | PARAMETRES | Description | Fonction |
---|---|---|---|
GOTOXY | x (2) y (2) options(1) | Demande à la carte d'asservissement de déplacer le robot jusqu'à une position souhaitée | goto_position |
GOTO_REVERSE | deplacement | demande au robot d'effectuer un déplacement en marche arrière | goto_reverse |
GOTO_PANIER | panier | Demande à la carte d'asservissement de déplacer le robot jusqu'au panier | goto_panier |
ROTATION_RAW | angle (2) | Demande à la carte d'asservissement de faire effectuer au robot une rotation | rotate_robot |
STOP | aucun | Arrête le robot | stop_robot |
RESET_POS | aucun | Réinitialise la position du robot | reset_position |
SET_VITESSE_TRAJ | vitesse acceleration | Configure la vitesse du robot en lignes droites | set_traj_params |
SET_VITESE_ROT | vitesse acceleration | Configure la vitesse du robot en rotation | set_rot_params |
On peut aussi récupérer des informations en provenance de cette même carte d'asservissement.
Les types de retour pour ces informations sont les suivants.
Type de retour | Données retournées | Description | fonction | |||||||
---|---|---|---|---|---|---|---|---|---|---|
POSITION |
|
Retourne la position actuelle du robot ainsi que son angle. | get_position | |||||||
POSITION_BALISE |
|
Retourne la position actuelle du robot adverse ainsi que son angle. | get_position_adv | |||||||
CHECK_BUSY |
|
Retourne l'état de la carte d'asservissement | get_status | |||||||
INFO_ADV |
|
Retourne les informations sur le robot adverse | get_info_adv |
Les ordres que l'on envoie à la caméra sont toujours constitués de deux octets.
Voici les deux commandes que l'on peut envoyer à la carte méca :
COMMANDE | PARAMETRES | Description | Fonction |
---|---|---|---|
CHANGER_MODE | nouveau mode | Permet de changer le mode dans lequel est la carte méca. | set_meca_mode_fonctionnement |
ENVOI_ORDRE | ordre | Envoie un ordre à la carte méca. Ces ordres ne sont effectifs que si la carte méca est en mode panier. | set_meca_ordre |
Voici les différents états de la carte méca.
Etat | Description |
---|---|
RAMASSAGE | Mode dans lequel le robot ramasse les boules sur le terrain. Les bras sont ouverts et les tapis sont en fonctionnement. |
COQUILLE | Dans ce mode les deux bras sont fermés et les moteurs sont arrêtés. C'est un mode de protection dans lequelles les balles ne peuvent pas entrer dans le robot. |
PANIER | C'est le mode dans lequel est le robot quand il décharge les boules. Dans ce mode, la carte méca peut recevoir des ordres, sinon ces ordres sont rejetés. |
Et les différentes commandes qui peuvent êtres reçues par le robot quand il est en mode panier.
Commande | Description |
---|---|
RAMASSER_BOULE | Lorsque le robot est en mode panier, fais fonctionner l'avalement pour avaler une boule sous un panier. |
DECHARGER_BR | Décharge une boule rouge. |
DECHARGER_BN | Décharge une boule noire. |
Il est aussi possible de récupérer l'état acutel de la carte méca. Voici les différentes informations qui peuvent être récupérées.
Type de retour | Données retournées | Description | fonction | ||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
CHECK_BUSY |
|
Retourne le status de la caméra. | get_meca_status | ||||||||
ETAT |
|
Renvoie l'état du barillet (nombre de boules de chaque couleur). | get_meca_couleur_boule | ||||||||
MODE | mode | Renvoie le mode actuel du robot. | get_meca_mode |