-
Notifications
You must be signed in to change notification settings - Fork 1
/
i2c.c
199 lines (178 loc) · 6.11 KB
/
i2c.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
#include <xc.h>
#include "i2c.h"
#include "file.h"
#include "test.h"
File fileEmission;
/**
* @return 255 / -1 si il reste des données à émettre.
*/
unsigned char i2cDonneesDisponiblesPourEmission() {
if (fileEstVide(&fileEmission)) {
return 0;
}
return 255;
}
/**
* Rend le prochain caractère à émettre.
* Appelez i2cCommandeCompletementEmise avant, pour éviter
* de consommer la prochaine commande.
* @return
*/
unsigned char i2cRecupereCaracterePourEmission() {
return fileDefile(&fileEmission);
}
typedef enum {
I2C_MASTER_EMISSION_ADRESSE,
I2C_MASTER_PREPARE_RECEPTION_DONNEE,
I2C_MASTER_RECEPTION_DONNEE,
I2C_MASTER_EMISSION_DONNEE,
I2C_MASTER_EMISSION_STOP,
I2C_MASTER_FIN_OPERATION,
} EtatMaitreI2C;
static EtatMaitreI2C etatMaitre = I2C_MASTER_EMISSION_ADRESSE;
/**
* Prépare l'émission de la commande indiquée.
* La fonction revient immédiatement, sans attendre que la commande
* soit transmise.
* @param adresse Adresse de l'esclave. Si le bit moins signifiant
* est 1, le maître lit sur l'esclave (lecture).
* @param valeur Valeur associée. Dans une opération de lecture, cette
* valeur n'a pas d'effet.
*/
void i2cPrepareCommandePourEmission(I2cAdresse adresse, unsigned char valeur) {
fileEnfile(&fileEmission, adresse);
fileEnfile(&fileEmission, valeur);
if (etatMaitre == I2C_MASTER_EMISSION_ADRESSE) {
SSP2CON2bits.SEN = 1;
}
}
/**
* Fonction de rappel de commandes par défaut.
*/
void faitRienDuTout(unsigned char adresse, unsigned char valeur) {
// Rien à faire.
}
/**
* Adresse de la fonction à appeler pour compléter
* l'exécution d'une commande I2C.
* Par défaut elle pointe sur une fonction qui ne fait rien.
*/
static I2cRappelCommande rappelCommande = faitRienDuTout;
/**
* Établit la fonction à appeler pour compléter l'exécution
* d'une commande I2C.
* Le maître appelle cette fonction pour terminer l'exécution
* d'une commande de lecture. L'esclave appelle cette fonction pour
* gérer l'exécution d'une commande d'écriture.
* @param r La fonction à appeler.
*/
void i2cRappelCommande(I2cRappelCommande r) {
rappelCommande = r;
}
void i2cMaitre() {
static unsigned char adresse; // Adresse associée à la commande en cours.
switch (etatMaitre) {
case I2C_MASTER_EMISSION_ADRESSE:
if (i2cDonneesDisponiblesPourEmission()) {
adresse = i2cRecupereCaracterePourEmission();
if (adresse & 1) {
etatMaitre = I2C_MASTER_PREPARE_RECEPTION_DONNEE;
} else {
etatMaitre = I2C_MASTER_EMISSION_DONNEE;
}
SSP2BUF = adresse;
}
break;
case I2C_MASTER_EMISSION_DONNEE:
etatMaitre = I2C_MASTER_EMISSION_STOP;
SSP2BUF = i2cRecupereCaracterePourEmission();
break;
case I2C_MASTER_PREPARE_RECEPTION_DONNEE:
etatMaitre = I2C_MASTER_RECEPTION_DONNEE;
i2cRecupereCaracterePourEmission();
SSP2CON2bits.RCEN = 1; // MMSP en réception.
break;
case I2C_MASTER_RECEPTION_DONNEE:
etatMaitre = I2C_MASTER_EMISSION_STOP;
// Le maître doit gérer la valeur rendue par l'esclave
rappelCommande(adresse, SSP2BUF);
SSP2CON2bits.ACKDT = 1; // NACK
SSP2CON2bits.ACKEN = 1; // Transmet le NACK
break;
case I2C_MASTER_EMISSION_STOP:
etatMaitre = I2C_MASTER_FIN_OPERATION;
SSP2CON2bits.PEN = 1;
break;
case I2C_MASTER_FIN_OPERATION:
etatMaitre = I2C_MASTER_EMISSION_ADRESSE;
if (i2cDonneesDisponiblesPourEmission()) {
SSP2CON2bits.SEN = 1;
}
break;
}
}
/**
* Le bit moins signifiant de SSPxBUF contient R/W.
* Il faut donc décaler l'adresse de 1 bit vers la droite.
* @param adresse Adresse reçue par le bus I2C.
* @return Adresse locale.
*/
unsigned char convertitEnAdresseLocale(unsigned char adresse) {
adresse >>= 1;
adresse &= I2C_MASQUE_ADRESSES_LOCALES;
return adresse;
}
/**
* L'esclave rendra la valeur indiquée à prochaine lecture de
* l'adresse indiquée sur le bus I2C.
* @param adresse Adresse locale, entre 0 et 4 (l'adresse locale
* est constituée des 2 bits moins signifiants de l'adresse
* demandée par le maître).
* @param valeur La valeur.
*/
void i2cExposeValeur(unsigned char adresse, unsigned char valeur) {
i2cValeursExposees[adresse & I2C_MASQUE_ADRESSES_LOCALES] = valeur;
}
/**
* Automate esclave I2C.
* @param valeursLecture Liste de valeurs à rendre en cas d'opération
* de lecture.
*/
void i2cEsclave() {
static unsigned char adresse;
// Machine à état extraite de Microchip AN00734b - Appendice B
if (SSP2STATbits.BF) {
if (SSP2STATbits.RW2) {
// État 4 - Opération de lecture, dernier octet transmis est une donnée:
// Jamais utilisé si les commandes ont un seul octet associé.
if (SSP2STATbits.DA2) {
SSP2BUF = i2cValeursExposees[adresse];
SSP2CON1bits.CKP = 1;
}
// État 3 - Opération de lecture, dernier octet reçu est une adresse:
else {
adresse = convertitEnAdresseLocale(SSP2BUF);
SSP2BUF = i2cValeursExposees[adresse];
SSP2CON1bits.CKP2 = 1;
}
} else {
// État 2 - Opération d'écriture, dernier octet reçu est une donnée:
if (SSP2STATbits.DA2) {
// L'esclave doit traiter la donnée reçue:
rappelCommande(adresse, SSP2BUF);
}
// État 1 - Opération d'écriture, dernier octet reçu est une adresse:
else {
adresse = convertitEnAdresseLocale(SSP2BUF);
}
}
SSP2STATbits.BF = 0;
}
}
/**
* Réinitialise la machine i2c.
*/
void i2cReinitialise() {
etatMaitre = I2C_MASTER_EMISSION_ADRESSE;
fileReinitialise(&fileEmission);
}