Dans ce TD, nous allons brancher un accéléromètre à notre carte Arduino Uno et communiquer avec. Nous allons mettre en oeuvre l'ensemble des points abordés dans le cours pour cela.
Nous allons travailler sur un accéléromètre.
Que mesure ce capteur, et dans quelle unité ?
Donnez des exemples d'applications de ce capteur.
À l'aide des datasheets, déterminer les branchements à réaliser entre l'accéléromètre (pensez à consulter le Pin mapping UNO/ATmega328P)
Demandez à votre enseignant de valider vos branchements avant de passer à la suite.
Dans la datasheet du MMA8451, chercher la valeur maximale de la bitrate pour la communication I2C.
Écrivez le code d'une fonction i2c_init()
qui initialise la communication I2C
à la bitrate maximale.
Vous utiliserez les registres TWBR
et TWSR
pour cela.
Cf page 180 de la datasheet de l'ATmega328P.
Comme vu dans le cours, une transaction I2C est composée de plusieurs étapes:
Nous vous fournissons les primitives suivantes:
uint8_t i2c_wait() {
while (!(TWCR & (1 << TWINT)))
;
return TWSR & 0xF8;
}
uint8_t i2c_start() {
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
return i2c_wait();
}
uint8_t i2c_stop() {
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
}
uint8_t i2c_send(uint8_t data) {
TWDR = data;
TWCR = (1 << TWINT) | (1 << TWEN);
return i2c_wait();
}
uint8_t i2c_receive(uint8_t ack) {
if (ack) {
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA);
} else {
TWCR = (1 << TWINT) | (1 << TWEN);
}
i2c_wait();
return TWDR;
}
À l'aide de la datasheet de l'ATmega328P, répondez aux questions suivantes:
i2c_wait()
?i2c_wait()
?0xF8
dans la fonction i2c_wait()
?ack
de la fonction i2c_receive()
?
(dans quel cas faut-il envoyer un ACK?)TWEA
dans le registre TWCR
?Nous allons tout d'abord déclarer une structure i2c_message
qui représente
le contenu d'une transaction I2C:
struct i2c_message {
uint8_t read; // Lecture ?
char *data; // Données à transmettre
uint8_t size; // Taille des données (en octets)
};
Implémentez la fonction suivante:
void i2c_transfer(uint8_t addr, struct i2c_message *messages, uint8_t size)
{
}
Qui transfère l'ensemble des messages contenus dans le tableau messages
au
périphérique I2C d'adresse addr
.
La fonction i2c_transfer()
doit donc pour chaque message:
data
de la structure i2c_message
OU recevoir les données dans le champ data
de la structure i2c_message
En lisant la datasheet du MMA8451, déterminer l'adresse I2C de l'accéléromètre.
Toujours à l'aide de la datasheet, déterminer l'adresse et la valeur
attendue du registre WHO_AM_I
Écrivez maintenant la fonction:
uint8_t mma8451_read_byte(uint8_t register_addr)
{
}
Qui utilise la fonction i2c_transfer()
pour lire un octet à l'adresse register_addr
de l'accéléromètre.
Testez votre fonction en lisant le registre WHO_AM_I
de l'accéléromètre et en vérifiant
que la valeur retournée est bien celle attendue en l'envoyant sur le port série.
Au démarrage, l'accéléromètre ne prend pas de mesure (cf page 11). Lisez le registre
SYSMOD
pour vérifier que l'accéléromètre est bien en mode STANDBY.
Implémentez maintenant la fonction:
void mma8451_write_byte(uint8_t register_addr, uint8_t data)
{
// TODO
}
Qui utilise la fonction i2c_transfer()
pour écrire un octet data
à l'adresse register_addr
Écrivez la valeur adéquate dans le registre CTRL_REG1
pour démarrer les mesures à
la fréquende de 100Hz.
Lisez le registre SYSMOD
pour vérifier que l'accéléromètre est bien en mode ACTIVE.
Nous représenterons une mesure par la structure suivante:
struct mma8451_data {
int16_t x;
int16_t y;
int16_t z;
};
Écrivez la fonction:
struct mma8451_data mma8451_read()
{
// TODO
}
Qui déclenche une lecture I2C afin d'obtenir les mesures de l'accéléromètre,
et les retourne sous la forme d'une structure mma8451_data
.
Au lieu de lire les mesures à la demande, nous allons utiliser les interruptions afin d'être notifié lorsque de nouvelles mesures sont disponibles.
Nous allons brancher la broche INT0
de l'ATmega328P sur la broche INT1
de l'accéléromètre.
Déterminez le branchement nécessaire.
À l'aide de la datasheet de l'ATmega328P, notamment la section 12, paramétrez
l'interruption INT0
pour qu'elle soit déclenchée sur un front descendant.
À l'aide des registres CTRL_REG4
et CTRL_REG5
de l'accéléromètre, paramétrez
l'interruption INT1
pour qu'elle soit déclenchée lorsque de nouvelles mesures
sont disponibles.
Attention: les registres CTRL_REG4
et CTRL_REG5
sont vérouillés
lorsque le capteur est en mode ACTIVE. Il faut donc le mettre en mode STANDBY
avant de pouvoir les modifier.
Dans la fonction d'interruption, déclenchez une lecture I2C afin d'obtenir les mesures.
Paramétrez le Timer1
de l'ATmega328P afin qu'un incrément de 1 correspond à 4µs.
Combien de temps faudrait t-il pour que le timer déborde si on ne le réinitialise pas ?
Combien de temps est supposé durer une mesure ?
À l'aide de TCNT1
, mesurez le temps entre deux interruptions et affichez le sur le port série.