Utilisation du langage C pour programmer des circuits d'entrees/sorties
Sommaire
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:
p = &c; /*l'adresse de c est rangee dans p. */
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 *:
char c;
char *p;
p = &c; /* l'adresse de c est rangee dans p */
*p = 'd'; /* c vaut maintenant le caractere 'd' */
int tab[10];
int *pt;
pt = &tab[5]; /* l'adresse de la case 5 du tableau tab est rangee dans pt */
*pt = 12; /* la case 5 de tab vaut maintenant 12 */
pt = pt + 1; /* pt pointe maintenant la case 6 de tab */
*pt = 34; /* la case 6 de tab vaut maintenant 34 */
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:
On peut initialiser (faire pointer) PtRegPIA soit dans le code
principal avec l'instruction:
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:
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.
#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).
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.
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 */
.
.
.
}
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
----------------------------------------------------------
| 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 | |
----------------------------------------------------------