Cours:InfoS2 tdI2c
Pensez à mettre sur la 1ère ligne de votre code : // Compiler: Avrgcc device: nomDuMicrocontroleur
Fichiers pour simulide : I2cTd1.zip
Sommaire
Framework Arduino
Lors des tds précédents, nous avons utilisé uniquement les fonctions de la bibliothèque standard libc pour avr.
L'inconvénient principal réside dans la nécessité de devoir écrire/adapter un programme pour chaque µcontroleur (aussi nommé cible). L'avantage principal est d'avoir un programme très efficient en terme de taille d'exécutable et de rapidité d'exécution, et également beaucoup plus simple à écrire.
Afin de simplifier le changement de cible, on utilise le concept de couches d'abstractions.
L'idée sous-jacente est de ne pas faire un programme qui s'occupe de gérer directement les périphériques, mais utilise des fonctions qui s'occupent de faire le lien, un exemple sera plus parlant !
PORTB|=(1<<PB0);
digialWrite(8,1);
Ces 2 instructions sont équivalentes pour une carte arduino UNO, elles mettent toutes les 2 à 1 la broche étiquetée 8 sur cette carte.
Si nous changeons de carte (arduino Mega par ex), la broche utilisée sur le µcontroleur n'est pas la même et il faut alors remplacer :
PORTH|=(1<<PH5);
digialWrite(8,1);
L'intérêt est ici évident de l'utilisation du framework (ensemble de fonctions) arduino qui permet d'écrire un programme quasiment indépendant de la carte (cible) utilisée.
Si nous parlons d'efficacité par contre, la fonction digialWrite est environ 20 fois moins rapide !
Il existe quelques framework pour les cibles avr (famille des µcontroleurs aTmega ATtiny), nous utiliserons le framework arduino pour utiliser la liaison i2c, l'utilisation du périphérique du µcontroleur étant assez fastidieux à configurer.
Trouver les adresses des "targets" (esclave i2c)
On programmera pour le moment uniquement la carte "controller" (identifiée carte n°2 sur tinkercad).
L'adresse d'un composant i2c étant codée sur 7 bits, quel est le nombre d'@ au total
Rque : certaines adresses sont réservées et non utilisables pour les périphériques.
Trouvez les adresses des targets en utilisant le programme suivant :
Rque :
- les valeurs s'affichent dans le moniteur série
- il n'est pas nécessaire de comprendre le programme, ça viendra plus tard.
#include <Wire.h>
int main()
{
Serial.begin(115200);
Serial.println("I2C Scanner\n");
sei();
Wire.begin();
while(1)
{
byte error, address;
int nDevices;
Serial.println("Scanning...");
nDevices = 0;
for(address = 1; address < 127; address++ )
{
// The i2c_scanner uses the return value of
// the Write.endTransmisstion to see if
// a device did acknowledge to the address.
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0)
{
Serial.print("I2C device found at address 0x");
if (address<16) Serial.print("0");
Serial.print(address,HEX);
Serial.println(" !");
nDevices++;
}
else if (error==4)
{
Serial.print("Unknown error at address 0x");
if (address<16) Serial.print("0");
Serial.println(address,HEX);
}
}
if (nDevices == 0) Serial.println("No I2C devices found\n");
else Serial.println("done\n");
_delay_ms(100); // wait 5 seconds for next scan
}
}
Envoyer des données du controller à la target i2c
Nous utilisons la librairie Wire du framework arduino.
Pour envoyer des données vers la target i2c, il convient de :
- indiquer l'@ de la target
- envoyer la (ou les) donnée(s)
- fermer la connection
Ce qui donne :
#include <Wire.h>
const uint8_t slaveAdress=0x04; // ou 4 ou 0b100 !
uint8_t value = 0b10101010;
int main()
{
Wire.begin(); // join i2c bus (address optional for master)
sei(); // la librairie Wire utilise des interruptions
while(1)
{
Wire.beginTransmission(slaveAdress); // transmit to device #4
Wire.write(value); // sends one byte
Wire.endTransmission(); // stop transmitting
delay(100);
}
}
Modifier le programme pour envoyer la donnée à la carte arduino sur laquelle sont branchées les leds, et faire en sorte que les leds allumées changent toutes les 200ms par ex
Envoyer des données vers le controller de la target i2c
Le transfert de données de la target vers le controller se passe de façon un peu différente. On programme le controller de la façon suivante :
- demander à un périphérique i2c d'envoyer un certain nombre de données (octets, 8 bits donc)
- tant qu'il y a des données i2c à lire :
- lire la donnée
ce qui donne :
#include <Wire.h>
const uint8_t slaveAdress=0x04; // ou 4 ou 0b100 !
uint8_t value;
void setup() {
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600); // start serial for output
}
void loop() {
Wire.requestFrom(slaveAdress, 1); // request 1 bytes(octet) from peripheral device @4
while (Wire.available()) { // peripheral may send less than requested
value = Wire.read(); // receive a byte as character
Serial.println(value); // print the character
}
delay(500);
}
Modifier le programme pour obtenir de la carte arduino sur laquelle est branchée le potentiomètre la "valeur" de celui-ci toutes les 200ms.
Utilisation des 2 targets
Réunissez vos 2 programmes pour faire en sorte que le potentiomètre serve à changer le nombre de leds allumées
Rque : on ne changera pas le programme des targets.
Ajout d'une autre target
Dupliquer les cartes arduino avec les leds et le potetiomètre
- Il y aura donc
- 2 cartes avec des leds
- 2 cartes avec un potentiomètre
- copier les programmes en modifiant les @ i2c
Modifier votre programme pour que chaque potentiomètre pilote une série de leds différentes.