Un apercu du code de la carte principale de Snooky

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.

Organisation du code

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.

Les fonctions de bas niveau

Trois modules s'occupent de fonctions de bas niveau :

L'interruption timer

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

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.

La gestion de la carte d'asservissement

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
position_x (2)
position_y (2)
angle (2)
Retourne la position actuelle du robot ainsi que son angle. get_position
POSITION_BALISE
position_x (2)
position_y (2)
angle (2)
Retourne la position actuelle du robot adverse ainsi que son angle. get_position_adv
CHECK_BUSY
status
PB_GRAVE
BUSY_PANIER
BUSY_GOTO_PANIER
CHOC_PANIER(NA)
CHOC_BORD(NA)
PB_MOTEUR
Retourne l'état de la carte d'asservissement get_status
INFO_ADV
data_retour
panier(2 bits)
GOING_PANIER
NEARBY_PANIER
GOING_ROBOT
NEARBY_ROBOT
Retourne les informations sur le robot adverse get_info_adv

La gestion de la carte meca

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
status
PB_GRAVE
BUSY_BARILLET
RAMASSE_TERRAIN
RAMASSE_PANIER
DECHARGE_PANIER
BUSY_TAPIS
BARILLET_PLEIN
Retourne le status de la caméra. get_meca_status
ETAT
boules rouges (4 bits faibles)
boules noires (4 bits forts)
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

La gestion de la camera

Gestion du terrain

La machine d'état

Bibliographie

1
F. de Lamotte.
Les outils utilisés pour développer et débugger le code de la carte principale.
http://n00n.free.fr, 2002.

2
F. de Lamotte.
Mise en place d'une communication entre un 68332 et un pic pour dialoguer sur un bus i2c.
http://n00n.free.fr, 2002.


Notes

... jour1
surtout en ce qui concerne le fichier TODO et pour les modifications de dernière minute
... test\_pic2
Dans l'interruption on utilise test_pic qui n'est pas blocante pour justement ne pas bloquer le processeur alors que dans le code principale, on utilise get_pic qui lui est bloquant
... fonctionnelle3
La signification réelle du clignotement de cette led est le code concernant l'i2c dans l'interruption timer est executé, ce qui signifie effectivement que l'i2c n'est pas bloqué quelque part dans la boucle principale.


Florent de LAMOTTE 2002-09-18