Cours:InfoS2 tdI2c : Différence entre versions

De troyesGEII
Aller à : navigation, rechercher
(Utilisation des 2 targets)
(Envoyer des données vers le controller de la target i2c)
 
(22 révisions intermédiaires par 2 utilisateurs non affichées)
Ligne 1 : Ligne 1 :
https://www.tinkercad.com/things/faLhqwsut7w
+
{{EnTeteTdInfoS2|InfoS2 tdI2c prof}}
  
 +
 +
Fichiers pour simulide : [[Media:infoI2cTd1.zip|I2cTd1.zip]]
 +
 +
{{Rouge|**************************************************}}
 +
{{Rouge|***  Attention, programme dans un fichier .ino ***}}
 +
{{Rouge|**************************************************}}
  
 
=Framework Arduino=
 
=Framework Arduino=
Ligne 23 : Ligne 29 :
 
<source lang=cpp>
 
<source lang=cpp>
 
PORTH|=(1<<PH5);
 
PORTH|=(1<<PH5);
digialWrite(8,1);{{Rouge|
+
digialWrite(8,1);
 
</source>
 
</source>
  
 
L'intérêt est ici évident de l'utilisation du {{Rouge|framework}} (ensemble de fonctions) arduino qui permet d'écrire un {{Rouge|programme quasiment indépendant de la carte (cible) utilisée}}.
 
L'intérêt est ici évident de l'utilisation du {{Rouge|framework}} (ensemble de fonctions) arduino qui permet d'écrire un {{Rouge|programme quasiment indépendant de la carte (cible) utilisée}}.
  
Si nous parlons {{Rouge|d'efficacité}}, par contre la fonction {{Rouge|digialWrite}} est environ {{Rouge|20 fois moins rapide}} !
+
Si nous parlons {{Rouge|d'efficacité}} par contre, la fonction {{Rouge|digialWrite}} est environ {{Rouge|20 fois moins rapide}} !
  
  
 
Il existe quelques framework pour les cibles {{Rouge|avr}} (famille des µcontroleurs aTmega ATtiny), nous utiliserons le {{Rouge|framework arduino}} pour utiliser la {{Rouge|liaison i2c}}, l'utilisation du périphérique du µcontroleur étant assez fastidieux à configurer.
 
Il existe quelques framework pour les cibles {{Rouge|avr}} (famille des µcontroleurs aTmega ATtiny), nous utiliserons le {{Rouge|framework arduino}} pour utiliser la {{Rouge|liaison i2c}}, l'utilisation du périphérique du µcontroleur étant assez fastidieux à configurer.
  
 +
== En pratique sur Simulide ==
 +
 +
* Télécharger l'archive <code>I2cTd1.zip</code> (en haut de ce document)
 +
* Dans Simulide : ouvrir le fichier <code>schemaI2cTd1.sim</code>
 +
* Le programme doit être écrit dans un fichier d'extension <code>.ino</code> et enregistré dans le dossier décompressé (qui ne doit pas contenir d'autres fichier .c ou .cpp)
 +
* La ligne habituelle <code>// Compiler: Avrgcc device: nomDuMicrocontroleur</code> est ici inutile
 +
* Dans <code>compiler/settings</code>, vous devez indiquer le chemin du ''tool path'' Arduino : <code>/opt/arduino/</code> (avec les <code>/</code>). Contrôler que la carte est bien une 'Uno'.
 +
* Pour ouvrir un moniteur série : clic droit sur le composant et sélectionner ''ouvrir le moniteur série'', puis ''USart1''
  
 
=Trouver les adresses des "targets" (esclave i2c)=
 
=Trouver les adresses des "targets" (esclave i2c)=
Ligne 49 : Ligne 63 :
  
 
<source lang=cpp>
 
<source lang=cpp>
// --------------------------------------
 
// i2c_scanner
 
//
 
// Version 1
 
//    This program (or code that looks like it)
 
//    can be found in many places.
 
//    For example on the Arduino.cc forum.
 
//    The original author is not know.
 
// Version 2, Juni 2012, Using Arduino 1.0.1
 
//    Adapted to be as simple as possible by Arduino.cc user Krodal
 
// Version 3, Feb 26  2013
 
//    V3 by louarnold
 
// Version 4, March 3, 2013, Using Arduino 1.0.3
 
//    by Arduino.cc user Krodal.
 
//    Changes by louarnold removed.
 
//    Scanning addresses changed from 0...127 to 1...119,
 
//    according to the i2c scanner by Nick Gammon
 
//    https://www.gammon.com.au/forum/?id=10896
 
// Version 5, March 28, 2013
 
//    As version 4, but address scans now to 127.
 
//    A sensor seems to use address 120.
 
// Version 6, November 27, 2015.
 
//    Added waiting for the Leonardo serial communication.
 
//
 
//
 
// This sketch tests the standard 7-bit addresses
 
// Devices with higher bit address might not be seen properly.
 
//
 
 
 
#include <Wire.h>
 
#include <Wire.h>
+
int main()
 
void setup()
 
{
 
  Wire.begin();
 
 
  Serial.begin(9600);
 
  while (!Serial);            // Leonardo: wait for serial monitor
 
  Serial.println("\nI2C Scanner");
 
}
 
 
 
void loop()
 
 
{
 
{
  byte error, address;
+
Serial.begin(115200);
  int nDevices;
+
Serial.println("I2C Scanner\n");
+
Wire.begin();
  Serial.println("Scanning...");
+
sei();
+
while(1)
  nDevices = 0;
+
{
  for(address = 1; address < 127; address++ )
+
byte error, address;
  {
+
int nDevices;
    // The i2c_scanner uses the return value of
+
Serial.println("Scanning...");
    // the Write.endTransmisstion to see if
+
nDevices = 0;
    // a device did acknowledge to the address.
+
for(address = 1; address < 127; address++ )
    Wire.beginTransmission(address);
+
{
    error = Wire.endTransmission();
+
// The i2c_scanner uses the return value of
+
// the Write.endTransmisstion to see if
    if (error == 0)
+
// a device did acknowledge to the address.
    {
+
Wire.beginTransmission(address);
      Serial.print("I2C device found at address 0x");
+
error = Wire.endTransmission();
      if (address<16)
+
if (error == 0)
        Serial.print("0");
+
{
      Serial.print(address,HEX);
+
Serial.print("I2C device found at address 0x");
      Serial.println("  !");
+
if (address<16) Serial.print("0");
+
Serial.print(address,HEX);
      nDevices++;
+
Serial.println("  !");
    }
+
nDevices++;
    else if (error==4)
+
}
    {
+
else if (error==4)
      Serial.print("Unknown error at address 0x");
+
{
      if (address<16)
+
Serial.print("Unknown error at address 0x");
        Serial.print("0");
+
if (address<16) Serial.print("0");
      Serial.println(address,HEX);
+
Serial.println(address,HEX);
    }  
+
}
  }
+
}
  if (nDevices == 0)
+
if (nDevices == 0) Serial.println("No I2C devices found\n");
    Serial.println("No I2C devices found\n");
+
else Serial.println("done\n");
  else
+
_delay_ms(100);          // wait 5 seconds for next scan
    Serial.println("done\n");
+
}
 
  delay(5000);          // wait 5 seconds for next scan
 
 
}
 
}
 
</source>
 
</source>
  
=Envoyer des données du controller à la target i2c=
+
=Ecrire sur le slave : Envoyer des données du controller à la target i2c=
  
 
Nous utilisons la librairie [https://www.arduino.cc/en/Reference/Wire Wire] du framework arduino.
 
Nous utilisons la librairie [https://www.arduino.cc/en/Reference/Wire Wire] du framework arduino.
Ligne 149 : Ligne 120 :
 
uint8_t value = 0b10101010;
 
uint8_t value = 0b10101010;
  
void setup()
+
int main()
 
{
 
{
 
   Wire.begin(); // join i2c bus (address optional for master)
 
   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
  
void loop()
+
    _delay_ms(100);
{
+
   }
  Wire.beginTransmission(slaveAdress); // transmit to device #4
 
   Wire.write(value);              // sends one byte 
 
  Wire.endTransmission();    // stop transmitting
 
 
 
  delay(100);
 
 
}
 
}
 
</source>
 
</source>
  
{{Question|Modifier le programme pour envoyer la donnée à la carte arduino sur laquelle sont branchées les leds, et faire en sorte que la leds allumées changent toutes les 200ms par ex}}
+
{{Question|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=
+
=Lire sur le slave : la target envoie des données au controller=
  
 
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 :
 
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 :
Ligne 179 : Ligne 150 :
 
uint8_t value;
 
uint8_t value;
  
void setup() {
+
int main()
 +
{
 
   Wire.begin();        // join i2c bus (address optional for master)
 
   Wire.begin();        // join i2c bus (address optional for master)
 
   Serial.begin(9600);  // start serial for output
 
   Serial.begin(9600);  // start serial for output
}
+
  sei();              // pour la librairie Wire
 +
  while(1)
 +
  {
 +
    Wire.requestFrom(slaveAdress, 1);  // request 1 bytes(octet) from peripheral device @4
  
void loop() {
+
    while (Wire.available())
  Wire.requestFrom(slaveAdress, 1);  // request 1 bytes(octet) from peripheral device @4
+
    {                                 // peripheral may send less than requested
 
+
      value = Wire.read();            // receive a byte as character
  while (Wire.available()) {         // peripheral may send less than requested
+
      Serial.println(value);          // print the character
    value = Wire.read();            // receive a byte as character
+
    }
    Serial.println(value);          // print the character
+
    _delay_ms(500);
 
   }
 
   }
  delay(500);
 
 
}
 
}
 
</source>
 
</source>
Ligne 211 : Ligne 185 :
 
**2 cartes avec des leds
 
**2 cartes avec des leds
 
**2 cartes avec un potentiomètre
 
**2 cartes avec un potentiomètre
*copier les programmes en modifiant les @ i2c
+
*pour modifier l'@ des cibles i2c, modifier le niveau logique sur les broches PB0 et PB1
  
 
{{Question|Modifier votre programme pour que chaque potentiomètre pilote une série de leds différentes.}}
 
{{Question|Modifier votre programme pour que chaque potentiomètre pilote une série de leds différentes.}}

Version actuelle datée du 15 avril 2024 à 17:30

Fiche résumé

Retour à la liste des Tds/Tps

Éléments de correction

simuler avec simulIDE

Pensez à mettre sur la 1ère ligne de votre code :
// Compiler: Avrgcc device: nomDuMicrocontroleur


Fichiers pour simulide : I2cTd1.zip
**************************************************
***  Attention, programme dans un fichier .ino ***
**************************************************

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.

CoucheAbstraction.png

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.

En pratique sur Simulide

  • Télécharger l'archive I2cTd1.zip (en haut de ce document)
  • Dans Simulide : ouvrir le fichier schemaI2cTd1.sim
  • Le programme doit être écrit dans un fichier d'extension .ino et enregistré dans le dossier décompressé (qui ne doit pas contenir d'autres fichier .c ou .cpp)
  • La ligne habituelle // Compiler: Avrgcc device: nomDuMicrocontroleur est ici inutile
  • Dans compiler/settings, vous devez indiquer le chemin du tool path Arduino : /opt/arduino/ (avec les /). Contrôler que la carte est bien une 'Uno'.
  • Pour ouvrir un moniteur série : clic droit sur le composant et sélectionner ouvrir le moniteur série, puis USart1

Trouver les adresses des "targets" (esclave i2c)

On programmera pour le moment uniquement la carte "controller" (identifiée carte n°2 sur tinkercad).

Question.jpg 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.

Question.jpg 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");
	Wire.begin();
	sei();
	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
	}
}

Ecrire sur le slave : 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_ms(100);
  }
}

Question.jpg 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

Lire sur le slave : la target envoie des données au controller

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;

int main()
{
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
  sei();               // pour la librairie Wire
  while(1)
  {
    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_ms(500);
  }
}


Question.jpg 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

Question.jpg 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

Todo.jpg 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
  • pour modifier l'@ des cibles i2c, modifier le niveau logique sur les broches PB0 et PB1

Question.jpg Modifier votre programme pour que chaque potentiomètre pilote une série de leds différentes.