Utilisation du langage C pour programmer des circuits d'entrees/sorties


Sommaire



Les pointeurs et les adresses (rappel)

Un pointeur est un groupe de cases memoires (2 ou 4 octets suivants
les implementations) pouvant contenir une adresse, exemple:

c est une variable de type char et p est un pointeur pouvant pointer
des objets de type char.

L'operateur & donne l'adresse d'un objet; pour faire pointer p
vers c il faut donc utiliser l'instruction:



L'operateur * represente l'operateur d'indirection. Si on
l'applique a un pointeur, il donne acces a l'objet pointe par ce
pointeur. Les 2 exemples suivants montrent comment utiliser les
operateurs & et *:




Un exemple applique au PIA 6821 du kit 68000

Sur le kit 68000 nous avons un circuit d'entree/sortie le PIA
6821. Sur ce kit 68000 l'adresse de base du PIA est 0x05CEF1
(le prefixe 0x precise au compilateur C que le chiffre aui suit
est en hexadecimal). En claire le premier registre du PIA est a
l'adresse 05CEF1h le second a l'adresse 05CEF3h et ainsi de suite pour
les registres suivants du PIA. On declarera donc les define
suivants:
    #define PIA 0x05CEF1 /* adresse de base du PIA sur le kit 68000 */ /* deplacement a ajouter a l'adresse PIA pour atteindre * les registres du PIA: */ #define CRA 2 /* reg de controle du port A */ #define DDRA 0 /* reg de controle du port A */ #define ORA 0 /* reg de lecture/ecriture du port A */ #define CRB 6 /* reg de controle du port B */ #define DDRB 4 /* reg de direction du port B */ #define ORB 4 /* reg de lecture/ecriture du port B */
Il faut commencer par declarer un pointeur capable de pointer les registres du PIA. Une simple question: qu'elle est le type de donnee des registres d'un circuit PIA 6821 ? (un pointeur doit pointer sur un type specifique de donnees). Evidemment il faut connaitre un minimum la structure interne du PIA (si peu). La reponse a la question se trouve dans le data book du 6821: ses registres sont sur 8 bits soit 1 octet. Quel est le type de donnee dans le langage C qui code des donnees sur 1 octet ? Et bien la reponse n'est pas trivial car elle depends de du compilateur C que vous utilisez. Un petit truc pour resoudre cette question: l'operateur sizeof applique a une variable ou bien directement a un type vous retourne la taille en octets de cette variable ou de ce type. Le progamme suivant vous montre l'utilisation de l'operateur sizeof:
    #include /stdio.h\ main() {
      char TabChar[10]; int TabInt[10]; printf("taille d'un char = %d octet\n",sizeof( char )); printf("taille d'un unsigned char = %d octet\n",sizeof( unsignedchar )); printf("taille d'un int = %d octets\n",sizeof( int )); printf("taille d'un unsigned int = %d octets\n",sizeof( unsigned int )); printf("taille d'un short int = %d octets\n",sizeof( short int )); printf("taille d'un float = %d octets\n",sizeof( float )); printf("taille d'un double = %d octets\n",sizeof( double )); printf("taille du tableau char TabChar[10] = %d octets\n",sizeof( TabChar )); printf("taille du tableau int TabInt[10] = %d octets\n",sizeof( TabInt ));
    }
Remarque: sur le kit 68000 la fonction printf n'est pas disponible il faut la remplacer par puts( uitoa( sizeof( short int ) ) ); L'execution de ce programme donne: taille d'un char = 1 octet taille d'un unsigned char = 1 octet taille d'un int = 4 octets taille d'un unsigned int = 4 octets taille d'un short int = 2 octets taille d'un float = 4 octets taille d'un double = 8 octets taille du tableau char TabChar[10] = 10 octets taille du tableau int TabInt[10] = 40 octets On prendra le type char pour coder les registres 8 bits du PIA. La declation du pointeur peut etre la suivante:
    char *PtRegPIA;
On peut initialiser (faire pointer) PtRegPIA soit dans le code principal avec l'instruction:
    PtRegPIA = (char *) PIA;
Soit directement dans la declaration:
    char *PtRegPIA = (char *) PIA;
Remarque au sujet du "cast" (char *): Ce cast est neccessaire pour signifier au compilateur que PIA est une adresse d'un objet de type char (char = 1 octets ou 8 bits pour les registres de notre PIA). Si l'on veut affecter la valeur 00 dans le registre CRB il suffit d'utiliser l'instruction suivante:
    *(PtRegPIA + CRB) = 0x00;
PtRegPia pointe sur le premier registre du PIA, on ajoute le deplacement CRB pour pointer sur CRB. L'operateur * permet de modifier le contenu pointer c'est a dire mettre la valeur 0x00 dans le registre CRB. Cette instruction est relativement lourde, mais en utilisant la notation tableau l'instruction devient limpide:
    PtRegPIA[ CRB ] = 0x00; /* qui est equivalent a *(PtRegPIA + CRB) = 0x00; */
On peut encore simplifier la notation *(PtRegPIA + CRB) en masquant sa complexite a l'aide d'un define:
    #define PtRegPIA 0x05CEF1 /* adresse de base du PIA */ #define CRB *(char *) (PtRegPIA + 6) /* reg de controle du port B */
et voici son utilisation:
    CRB = 0x00;
apres l'action du preprocesseur (qui est automatiquement lance avant la compilation) l'instruction precedente devient: est l'adresse du registre CRB. | ----------- / \
    *(char *) (0x05CEF1 + 6) = 0x00;
^ ^ ^ ^ ^ | | | | | | | | | - deplacement pour attendre le registte CRB. | | | - l'adresse de base du PIA. | | - declaration du pointeur. | - pointeur sur char. - operateur d'indirection: pour avoir acces au contenu pointe. Trouver la declaration du define est complexe mais l'utilisation devient alors extrement simple. Maintenant vous pouvez aisement manipuler les pointeurs pour atteindre les registres ou la memoire qui d'un calculateur.

Implementer une fonction comme routine d'interrutpion.

    #pragma INTERRUPT void InterruptionPTM() { . . . }
Le pragma INTERRUPT permet de specifier au compilateur C que le code de la fonction InterruptionPTM() doit etre implementer comme une routine d'interruption. Dans ce cas le compilateur termine la fonction par l'instruction RTE (retour de sous-programme d'exception) et non un RTS (retour de sous-programme).

Ecrire l'adresse d'un sous-programme dans la table des vecteurs.

Voici une maniere tres elegante de proceder:
    #define AdrTabVect 0 /* Adresse de base de la table des vecteurs * elle est toujours egale a 0 avec 68000 * mais avec un 68010 elle est egale * au contenu du registre VBR */ #define VECT_PTM 64 /* Numero d'un vecteur utilisateur * choisi dans la table des vecteurs */ main() { /* Initialisation de la table des vecteurs d'it */ static void (**TabVect)() = (void (**)()) AdrTabVect; /* Mise en place du vecteurs d'it IRQ du PTM */ TabVect[ VECT_PTM ] = InterruptionPTM; . . . } TabVect est un pointeur qui pointe vers un tableau qui contient des adresses de fonctions.

Inserer du code assembleur dans un source C.

La syntaxe est la suivante:
    main () { . . . /* Debut de la sequence assembleur */ #pragma ASM . . . Fct equ 10 move.l #01234560,a1 ; corps de la sequence assembleur move.b #08,fct(a1) . . . #pragma END_ASM /* fin de la sequence assembleur */ . . . }

Adresses des registres du PIA 6821

  R   = Acces en lecture
  W   = Acces en ecriture
  R/W = Acces en lecture ou en ecriture

Addresses Acces Description
  05CEF1   R/W    Data Direction Register A (CRA 2 = 0)
  05CEF1   R/W    Peripheral Register A     (CRA 2 = 1)

  05CEF5   R/W    Data Direction Register B (CRB 2 = 0)
  05CEF5   R/W    Peripheral Register B     (CRB 2 = 1)

  05CEF3   R/W    Control Register A
  05CEF7   R/W    Control Register B

TABLE DES VECTEURS

    ---------------------------------------------------------- | Numero| Position | | | de |----------- Affectation | |Vecteur| Dec | Hex | | ---------------------------------------------------------- | 0 | 0 | 000 | Reset: Initialisation du SSP | | | 4 | 004 | Reset: Initialisation du PC | ---------------------------------------------------------- | 2 | 8 | 008 | Erreur bus | | 3 | 12 | 00C | Erreur d'adresse | ---------------------------------------------------------- | 4 | 16 | 010 | Instruction illegale | ---------------------------------------------------------- | 5 | 20 | 014 | Division par zero | ---------------------------------------------------------- | 6 | 24 | 018 | Instruction CHK | | 7 | 28 | 01C | Instruction TRAPV | ---------------------------------------------------------- | 8 | 32 | 020 | Violation de privilege | ---------------------------------------------------------- | 9 | 36 | 024 | Trace | ---------------------------------------------------------- | 10 | 40 | 028 | Emulateur ligne 1010 | | 11 | 44 | 02C | Emulateur ligne 1111 | ---------------------------------------------------------- | 12 | 48 | 030 | Non attribue ou reserve | | 13 | 52 | 034 | | ---------------------------------------------------------- | 14 | 56 | 038 | Erreur de format | ---------------------------------------------------------- | 15 | 60 | 03C | Interruption non initialisee | ---------------------------------------------------------- | | 64 | 040 | | | 16-23 |- - -|- - - Non attribue ou Reserve | | | 92 | 05F | | ---------------------------------------------------------- | 24 | 96 | 060 | Interruption parasite | ---------------------------------------------------------- | 25 | 100 | 064 | Auto-Vecteur d'interruption Niveau 1 | | 26 | 104 | 068 | Auto-Vecteur d'interruption Niveau 2 | | 27 | 108 | 06C | Auto-Vecteur d'interruption Niveau 3 | | 28 | 112 | 070 | Auto-Vecteur d'interruption Niveau 4 | | 29 | 116 | 074 | Auto-Vecteur d'interruption Niveau 5 | | 30 | 120 | 078 | Auto-Vecteur d'interruption Niveau 6 | | 31 | 124 | 07C | Auto-Vecteur d'interruption Niveau 7 | ---------------------------------------------------------- | | 128 | 080 | | | 32-47 |- - -|- - -| Vecteurs d'instruction TRAP | | | 188 | 0BC | | ---------------------------------------------------------- | | 192 | 0C0 | | | 48-63 |- - -|- - -| Non attribue ou Reserve | | | 252 | 0FC | | ---------------------------------------------------------- | | 256 | 100 | | | 64-255|- - -|- - -| Vecteurs d'interruption utilisateur | | | 1024| 3FF | | ----------------------------------------------------------