Programme pour générer quelques phases d'adressage pour observation à l'oscilloscope: // -------------------------------------- // 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 // http://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. // // // This sketch tests the standard 7-bit addresses // Devices with higher bit address might not be seen properly. // #include void setup() { Wire.begin(); Serial.begin(9600); Serial.println("\nI2C Scanner"); } void loop() { byte error, address; int nDevices; Serial.println("Scanning..."); nDevices = 0; for(address = 0x65; address < 0x6B; address++ ) { // Serial.println(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("Unknow 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(100); // wait 5 seconds for next scan } Projet pour programmer les maquettes pour recopier du pcf8574 Ox3F vers 0x3E: wget https://bvdp.inetdoc.net/files/iut/td2_capt/demo_8574_IUT.ino.hex avrdude -p m328p -c arduino -b 115200 -P /dev/ttyACM0 -U flash:w:demo_8574_IUT.ino.hex (voir https://skyduino.wordpress.com/2011/12/02/tutoriel-avrdude-en-ligne-de-commande/ ) code source: https://bvdp.inetdoc.net/files/iut/td2_capt/demo_8574_IUT.ino ===== TD/TP Bus de communication : Utilisation du bus Série synchrone I2C sur Arduino ===== cours bus de com {{https://bvdp.inetdoc.net/files/iut/cours_bus_terrain_livret_portrait.pdf}} Complément d'informations sur le bus I2C: http://i2c.info/i2c-bus-specification Comparaison entre le bus I2C et le SMBUS: https://www.maximintegrated.com/en/app-notes/index.mvp/id/476 et https://www.totalphase.com/support/articles/200349186-Differences-between-I2C-and-SMBus === Objectifs === * Utilisation de la maquette Arduino * Utilisation haut niveau d'une communication série synchrone I2C * Compréhension des notions d'adressage, (sur le bus et dans le composant) * Réalisation de librairies pour accès à des périphériques externes (port E/S, horloge temps réel, ROM) ==== Carte d'extension pour le TD/TP === {{https://bvdp.inetdoc.net/files/iut/td2_capt//CARTECOM.png}} Schéma de la carte principale: {{https://bvdp.inetdoc.net/files/iut/td2_capt/schemaCom.png}} Schéma de la carte amovible: {{https://bvdp.inetdoc.net/files/iut/td2_capt/moduleRTC.jpg}} fichiers eagle: https://bvdp.inetdoc.net/files/iut/td2_capt/carte-arduino-8574-12.sch et https://bvdp.inetdoc.net/files/iut/td2_capt/carte-arduino-8574-12.brd La carte d'extension utilisée pour ce TD/TP permet de connecter plusieurs périphériques à l'Arduino. Elle dispose: - de deux port E/S sur bus I2C dont un est piloté par des interrupteurs et l'autre pilote des leds. Le composant utilisé est un circuit PCF 8574A, dont la documentation est à l'adresse: https://bvdp.inetdoc.net/files/iut/td2_capt/pcf8574.pdf (et version simplifiée: https://bvdp.inetdoc.net/files/comm/datasheet/PCF8574_TD.pdf ). L'adresse de chacun de ces composants est en partie configurable via 3 cavaliers dont la fermeture revient à imposer un 0 logique sur le bit correspondant. - d'une EEPROM SPI AT25040 (documentation: http://www.atmel.com/images/doc3348.pdf ) qui sera utilisée dans un prochain TP concernant le bus SPI: https://bvdp.inetdoc.net/wiki/doku.php?id=tdethindus1 - d'une led infrarouge pour assurer la fonction de télécommande - d'un module amovible assurant la fonction de RTC via le composant DS1307 (documentation: http://datasheets.maximintegrated.com/en/ds/DS1307.pdf ) et EEPROM I2C AT24C32N (documentation: http://www.atmel.com/images/doc0336.pdf ). Une sonde de températeur DS18B20 sur bus OneWire est également présente (documentation: http://datasheets.maximintegrated.com/en/ds/DS18B20.pdf ) et sera utilisée dans un prochain TP concernant le bus OneWire: https://bvdp.inetdoc.net/wiki/doku.php?id=tp_one_wire ===== Partie TD===== ====Généralités sur le Bus I2C==== - Quels sont les niveaux électriques possibles sur un bus i2c ? - Dans quels états peut se trouver une broche (par exemple SDA) d'un composant i2c ? - Quels composants discrets doivent être connectés au bus i2c pour assurer son fonctionnement ? - Combien d'esclaves maximum peuvent être théoriquement connectés sur un bus I2c pour pouvoir les adresser tous individuellement? - Le bus i2C est-il half-duplex ou full-duplex ? justifier votre réponse. La documentation de la librairie Arduino permettant d'utiliser l'interface I2C est accessible à l'adresse: https://www.arduino.cc/en/reference/wire . Lire les descriptions des fonctions suivantes: **begin()**, **requestFrom()**, **beginTransmission()**, **endTransmission()**, **write()**, **available()**, **read()**, **SetClock()** ====Composant PCF8574(A)==== Dans un premier temps, lire la documentation suivante: {{https://bvdp.inetdoc.net/files/comm/datasheet/PCF8574_TD.pdf}} Pour les questions suivantes, on considère les prototypes de fonctions suivants: char writePort8574(char addr, char value); // addr, l'adresse du PCF8574 sur 7 bits // value, la valeur à écrire sur le port // retourne -1 si échec 0 sinon char readPort8574(char addr, char * ptr_value); // addr, l'adresse du PCF8574 sur 7 bits // ptr_value, pointeur pour renvoyer la valeur lue sur le port // retourne -1 si échec 0 sinon - Quel est le rôle de ce composant? - Quel est le rôle des brôches A2, A1 et A0? - Quelle différence existe-t-il entre les références PCF8574 et PCF8574A et pourquoi le constructeur a-t-il fait ces deux références? Quelle est la plage d'adresses I2C utilisables pour la référence de composant PCF8574A? Dans la suite, des composants PCF8574A seront considérés. - Décrire l'algorithme de la fonction **readPort8574(...)** en indiquant ce qu'elle fait en français (phase d'adressage, d'échange des données etc...). - Implémenter en C++ la fonction **readPort8574(...)** en utilisant les fonctions de la librairie **Wire**. - Tracer le chronogramme des signaux SDA et SCL si l’on appelle **readPort8574(0x2,&val)**. Indiquer quelle est la valeur retournée par la fonction. - Tracer le chronogramme des signaux SDA et SCL si l’on appelle **readPort8574(0x38,&val)** et que les valeurs de A2-0=000 et les entrées du 8574A sont à 0x27. Indiquer quelle est la valeur retournée par la fonction. - Décrire la fonction **writePort8574(...)** en indiquant ce qu'elle fait en français (phase d'adressage, d'échange des données etc...). - Implémenter en C++ la fonction **writePort8574(...)** en utilisant les fonctions de la librairie **Wire**. - Tracer le chronogramme des signaux SDA et SCL si l’on appelle **writePort8574(0x39,0x82)** et que les valeurs de A2-0=001. Indiquer quelle est la valeur retournée par la fonction. - Décrire un programme permettant de lire les entrées d'un PCF8574A dont les bits d'adresse sont reliés à 0 et de recopier cette valeur sur un PCF8574A dont les bits d'adresse forment la valeur 3. ====Composant DS13072==== Dans un premier temps, lire la documentation suivante: {{https://bvdp.inetdoc.net/files/comm/datasheet/DS1307.pdf}} Pour les questions suivantes, on considère les prototypes de fonctions suivants: char writeRegRTC(char I2Caddr, char Regaddr, char value) // I2Caddr, l'adresse du DS13072 sur le bus sur 7 bits // Regaddr, l'adresse du registre du DS13072 à écrire // value,la valeur à écrire dans le registre // retourne -1 si échec 0 sinon char readRegRTC(char I2Caddr, char Regaddr, char * ptr_value) // I2Caddr, l'adresse du DS13072 sur le bus sur 7 bits // Regaddr, l'adresse du registre du DS13072 à lire // ptr_value, pointeur pour renvoyer la valeur lue dans le registre // retourne -1 si échec 0 sinon - Quel est le rôle de ce composant? - Du point de vue de la communication, qu'est ce qui le différencie du PCF8574(A)? - Le composant se comporte comme une série de compteurs mis en cascade. Dessiner la mise en cascade des compteurs de l'unité de seconde jusqu'à la dizaine d'heure telle que réalisée dans le composant. Expliquer l'intérêt de l'utilisation de la base Binary Coded Decimal (Décimal Codé en Binaire) pour les valeurs de comptage. - Décrire l'algorithme de la fonction **writeRegRTC(...)** en indiquant ce qu'elle fait en francais (phase d'adressage, d'échange des données etc...). - Implémenter en C++ la fonction **writeRegRTC(...)** en utilisant les fonctions de la librairie **Wire**. - Tracer le chronogramme des signaux SDA et SCL si l’on appelle **writeRegRTC(0x68,0x2,0x52)**. Indiquer quelle est la valeur retournée par la fonction. - Décrire l'algorithme de la fonction **readRegRTC(...)** en indiquant ce qu'elle fait en francais (phase d'adressage, d'échange des données etc...). - Implémenter en C++ la fonction **readRegRTC(...)** en utilisant les fonctions de la librairie **Wire**. - Tracer le chronogramme des signaux SDA et SCL si l’on appelle **readRegRTC(0x68,0x1,&val)** et que la valeur lue est 0x21. Indiquer quelle est la valeur retournée par la fonction. - Proposer un schéma de câblage pour un montage connectant la plateforme Arduino à une RTC DS13072 et deux expandeurs de ports I2C PCF8574. ===== Partie TP===== ===== Exercice 1 : Détermination des adresses des esclaves I2C ===== {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Programmer l'arduino avec le sketch suivant et ouvrir la console arduino en 9600 8N1. (sketch récupéré sur http://playground.arduino.cc/Main/I2cScanner ) // -------------------------------------- // 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 // http://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. // // // This sketch tests the standard 7-bit addresses // Devices with higher bit address might not be seen properly. // #include void setup() { Wire.begin(); Serial.begin(9600); Serial.println("\nI2C Scanner"); } void loop() { 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("Unknow 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(5000); // wait 5 seconds for next scan } Vous devriez obtenir un affichage ressemblant à: {{https://bvdp.inetdoc.net/files/iut/td2_capt/scanI2C.png}} {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Débrancher et rebrancher le module amovible RTC et interpréter le résultat. Jouez sur les cavaliers permettant de configurer l'adresse matérielle des 8574. En déduire l'adresse de tous les composants que vous allez utiliser. ===== Exercice 2 : Utilisation du PCF8574 ===== {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Programmer l'arduino avec le sketch suivant et ouvrir la console arduino en 9600 8N1. #include //adresses codées sur 7bits #define SLAVE_ADDR_8574_A COMPLETER #define SLAVE_ADDR_8574_B COMPLETER ////////////////////////////////////////// char readPort8574(char addr, char * ptr_value) /*addr, l'adresse du PCF8574 ptr_value, pointeur pour renvoyer la valeur lue sur le port retourne -1 si échec 0 sinon*/ { Wire.requestFrom((byte)addr, (byte)1);// demande la lecture d'1 octet depuis l'adresse du pérpiphérique if (Wire.available()==1) //si l'octet est disponible { (* ptr_value) = Wire.read(); // lire l'octet return 0; } else { (* ptr_value) =0; //valeur par défaut si le composant n'a pas acquité return -1; } } ////////////////////////////////////////// char writePort8574(char addr, char value) /*addr, l'adresse du PCF8574 value, la valeur à écrire sur le port retourne -1 si échec 0 sinon */ { Wire.beginTransmission((byte)addr);//démarre la transmission avec l'adresse du pérpiphérique Wire.write((byte)value); //envoie la donnée if (Wire.endTransmission()==0) //stoppe la transmission return 0; else return -1; } ////////////////////////////////////////// void setup() { Serial.begin(9600); // start serial port at 9600 bps: Serial.print("Bonjour"); Wire.begin(); // joindre le bus i2c en tant que maître writePort8574( SLAVE_ADDR_8574_B , 0xff); //configure le composant B en entrée } ////////////////////////////////////////// void loop() { } {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Compléter ce sketch pour écrire sur le port de sortie successivement les valeurs 0x55 et 0xAA. Inclure un délai suffisant pour pouvoir observer à l'oeil le clignotement des leds. Solution pour chenillard 8 bits, avec vitesse de défilement réglée par les dil sur les entrées du PCF 8574 B: #include //adresses codées sur 7bits #define SLAVE_ADDR_8574_A 0x3e #define SLAVE_ADDR_8574_B 0x3f ////////////////////////////////////////// char readPort8574(char addr, char * ptr_value) /*addr, l'adresse du PCF8574 ptr_value, pointeur pour renvoyer la valeur lue sur le port retourne -1 si échec 0 sinon*/ { Wire.requestFrom((byte)addr, (byte)1);// demande la lecture d'1 octet depuis l'adresse du pérpiphérique if (Wire.available()==1) //si l'octet est disponible { (* ptr_value) = Wire.read(); // lire l'octet return 0; } else { (* ptr_value) =0; //valeur par défaut si le composant n'a pas acquité return -1; } } ////////////////////////////////////////// char writePort8574(char addr, char value) /*addr, l'adresse du PCF8574 value, la valeur à écrire sur le port retourne -1 si échec 0 sinon */ { Wire.beginTransmission((byte)addr);//démarre la transmission avec l'adresse du pérpiphérique Wire.write((byte)value); //envoie la donnée if (Wire.endTransmission()==0) //stoppe la transmission return 0; else return -1; } ////////////////////////////////////////// void setup() { Serial.begin(9600); // start serial port at 9600 bps: Serial.print("Bonjour"); Wire.begin(); // joindre le bus i2c en tant que maître writePort8574( SLAVE_ADDR_8574_B , 0xff); //configure le composant B en entrée } ////////////////////////////////////////// ////////////////////////////////////////// void loop() { unsigned int val; char vald=128; unsigned int d=128; for (val=1; val<=128;val=val*2) { writePort8574(SLAVE_ADDR_8574_A, ~val); if (readPort8574(SLAVE_ADDR_8574_B,&vald)==0) d=(vald&0xFF)*10; delay(d); } for (val=64; val>1;val=val/2) { writePort8574(SLAVE_ADDR_8574_A, ~val); if (readPort8574(SLAVE_ADDR_8574_B,&vald)==0) d=(vald&0xFF)*10; delay(d); } } {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Modifier le sketch pour effectuer la recopie du port d'entrée sur le port de sortie (écrire uniquement si la lecture a été possible). Afficher des messages sur la console pour indiquer si les 8574 n'ont pas bien acquitté. Jouer sur la configuration des cavaliers pour vérifier que vous arrivez à détecter l'acquittement et le non acquittement. Solution: #include //adresses codées sur 7bits #define SLAVE_ADDR_8574_A 0x38+6 #define SLAVE_ADDR_8574_B 0x38+7 ////////////////////////////////////////// char readPort8574(char addr, char * ptr_value) /*addr, l'adresse du PCF8574 ptr_value, pointeur pour renvoyer la valeur lue sur le port retourne -1 si échec 0 sinon*/ { Wire.requestFrom((byte)addr, (byte)1);// demande la lecture d'1 octet depuis l'adresse du pérpiphérique if (Wire.available()==1) //si l'octet est disponible { (* ptr_value) = Wire.read(); // lire l'octet return 0; } else { (* ptr_value) =0; //valeur par défaut si le composant n'a pas acquité return -1; } } ////////////////////////////////////////// char writePort8574(char addr, char value) /*addr, l'adresse du PCF8574 value, la valeur à écrire sur le port retourne -1 si échec 0 sinon */ { Wire.beginTransmission(addr);//démarre la transmission avec l'adresse du pérpiphérique Wire.write((byte)value); //envoie la donnée if (Wire.endTransmission()==0) //stoppe la transmission return 0; else return -1; } ////////////////////////////////////////// void setup() { Serial.begin(9600); // start serial port at 9600 bps: Serial.print("Bonjour"); Wire.begin(); // joindre le bus i2c en tant que maître writePort8574( SLAVE_ADDR_8574_B , 0xff); //configure le composant B en entrée } ////////////////////////////////////////// void loop() { char val; if (readPort8574(SLAVE_ADDR_8574_B,&val)==0) { Serial.print("lecture 8574 OK, "); if (writePort8574(SLAVE_ADDR_8574_A,val)==0) Serial.println("ecriture 8574 OK"); else Serial.println("ecriture 8574 NOK"); } else Serial.println("lecture 8574 NOK,"); /* writePort8574(SLAVE_ADDR_8574_A, 0x55); delay(500); writePort8574(SLAVE_ADDR_8574_A, 0xAA); delay(500); */ } Question supplémentaire envisageable: déclencher une interruption en cas de changement de l'état d'une broche du port d'entrée du PCF8574. {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Observer les signaux SDA et SCL à l'oscilloscope. Déterminer la fréquence d'horloge utilisée pour le signal SCL. Repérer les phases d'adressage et d'échange des données et vérifier la valeur du bit d’acquittement lorsque l'adresse utilisée est correcte et lorsqu'elle est incorrecte. {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Appeler la fonction Wire.setClock() ( https://www.arduino.cc/en/Reference/WireSetClock ) dans setup() pour configurer l'horloge I2C à une fréquence de 400KHz et vérifier les signaux à l'oscilloscope. Comparer avec la fréquence d'horloge maximum autorisée pour le PCF8574 et conclure. ===== Exercice 3 : Utilisation de la RTC ===== #include //adresses codées sur 7bits #define SLAVE_ADDR_DS13072 COMPLETER ////////////////////////////////////////// char readRegI2C(char I2Caddr, byte Regaddr, byte * ptr_value) /*I2Caddr, l'adresse du DS13072 sur le bus Regaddr, l'adresse du registre du DS13072 à lire ptr_value, pointeur pour renvoyer la valeur lue dans le registre retourne -1 si échec 0 sinon */ { Wire.beginTransmission(I2Caddr);//démarre la transmission avec l'adresse du pérpiphérique //A COMPLETER } ////////////////////////////////////////// char writeRegI2C(char I2Caddr, byte Regaddr, byte value) /* I2Caddr, l'adresse du DS13072 sur le bus Regaddr, l'adresse du registre du DS13072 à écrire value,la valeur à écrire dans le registre retourne -1 si échec 0 sinon */ { Wire.beginTransmission(I2Caddr);//démarre la transmission avec l'adresse du pérpiphérique //A COMPLETER } ////////////////////////////////////////// void displayTime() { byte val=0; if (0) //A COMPLETER pour lire les heures { Serial.print(val, HEX); Serial.print(" : "); } if (0) //A COMPLETER pour lire les minutes { Serial.print(val, HEX); Serial.print(" : "); } if (0) //A COMPLETER pour lire les secondes { Serial.print(val, HEX); Serial.println(" sec"); } } ////////////////////////////////////////// void dumpDS13072() { byte val,add; //A COMPLETER pour lire toutes les cases mémoire et afficher leurs adresses et contenus } ////////////////////////////////////////// void setTime(byte hour,byte minute,byte sec) { if (0) //A COMPLETER pour régler les heures Serial.println(" problème réglage heure"); if (0) //A COMPLETER pour régler les minutes Serial.println(" problème réglage minute"); if (0) //A COMPLETER pour régler les secondes Serial.println(" problème réglage sec"); } ////////////////////////////////////////// void setup() { Serial.begin(9600); // start serial port at 9600 bps: Serial.print("Bonjour"); Wire.begin(); // joindre le bus i2c en tant que maître //activation de l'oscillateur interne, remet également les secondes à 0 if (writeRegI2C(SLAVE_ADDR_DS13072, 0, 0x00)==-1) Serial.println(" problème activation oscillateur"); //activation sortie SQ à 1hz if (0) //A COMPLETER pour activation sortie SQ à 1hz Serial.println(" problème réglage SquareWave"); //réglage de l'heure //A COMPLETER pour régler l'heure à 11h59 et 30 secondes } ////////////////////////////////////////// void loop() { //A COMPLETER selon les exercices } ////////////////////////////////////////// {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Compléter le sketch précédent pour activer la broche SQW/OUT successivement à 1 et à 0 en changeant toutes les secondes dans la fonction loop. Pour cela implémenter la fonctions writeRegI2C. {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Modifier le sketch pour demander le clignotement à 1hz de la sortie SQW/OUT dans la fonction setup et commenter le clignotement manuel dans loop. {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Modifier le sketch pour écrire la valeur 0xB3 dans la case d'adresse 0x12 puis pour relire cette même case et afficher son contenu chaque seconde dans la fonction loop. {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Compléter le sketch pour: - Régler l'heure à 11h59 et 30 secondes dans l'initialisation. Pour cela, implémenter la fonction setTime(...) et l'appeler avec les bons paramètres depuis setup(). - Afficher l'heure deux fois par seconde dans la console. Quand l'heure s'affiche correctement dans la console, débrancher le module RTC et vérifier que l'heure arrête de s'afficher. Lorsque vous rebranchez le module RTC, l'heure affichée doit avoir évoluée grâce à la batterie du module. {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Implémenter la fonction dumpDS13072() pour qu'elle permette de visualiser le contenu de chacune des 64 cases de la mémoire du DS13072 précédé de son adresse en héxadécimal. Solution: #include //adresses codées sur 7bits #define SLAVE_ADDR_DS13072 0x68 ////////////////////////////////////////// char readRegI2C(char I2Caddr, byte Regaddr, byte * ptr_value) /*I2Caddr, l'adresse du composant sur le bus Regaddr, l'adresse du registre à lire ptr_value, pointeur pour renvoyer la valeur lue dans le registre retourne -1 si échec 0 sinon */ { Wire.beginTransmission(I2Caddr);//démarre la transmission avec l'adresse du pérpiphérique Wire.write((byte)Regaddr); //envoie l'adresse de la case if (Wire.endTransmission()!=0) //stoppe la transmission return -1; Wire.requestFrom((byte)I2Caddr,(byte) 1);// demande la lecture d'1 octet depuis l'adresse du pérpiphérique if (Wire.available()==1) //si l'octet est disponible { (* ptr_value) = Wire.read(); // lire l'octet return 0; } else return -1; } ////////////////////////////////////////// char writeRegI2C(char I2Caddr, byte Regaddr, byte value) /* I2Caddr, l'adresse du composant sur le bus Regaddr, l'adresse du registre à écrire value,la valeur à écrire dans le registre retourne -1 si échec 0 sinon */ { Wire.beginTransmission(I2Caddr);//démarre la transmission avec l'adresse du pérpiphérique Wire.write((byte)Regaddr); //envoie l'adresse de la case Wire.write((byte)value); //envoie la donnée if (Wire.endTransmission()==0) //stoppe la transmission return 0; else return -1; } ////////////////////////////////////////// void displayTime() { byte val; if (readRegI2C(SLAVE_ADDR_DS13072, 2, &val)==0) { Serial.print(val>>4, HEX); Serial.print(val&0xf, HEX); Serial.print(":"); } if (readRegI2C(SLAVE_ADDR_DS13072, 1, &val)==0) { Serial.print(val>>4, HEX); Serial.print(val&0xf, HEX); Serial.print(":"); } if (readRegI2C(SLAVE_ADDR_DS13072, 0, &val)==0) { Serial.print(val>>4, HEX); Serial.print(val&0xf, HEX); Serial.println(" sec"); } } ////////////////////////////////////////// void displayDate() { byte val; if (readRegI2C(SLAVE_ADDR_DS13072, 4, &val)==0) { Serial.print(val>>4, HEX); Serial.print(val&0xf, HEX); Serial.print("/"); } if (readRegI2C(SLAVE_ADDR_DS13072, 5, &val)==0) { Serial.print(val>>4, HEX); Serial.print(val&0xf, HEX); Serial.print("/"); } if (readRegI2C(SLAVE_ADDR_DS13072, 6, &val)==0) { Serial.print(val>>4, HEX); Serial.print(val&0xf, HEX); Serial.println(""); } } ////////////////////////////////////////// void dumpDS13072() { //A COMPLETER pour lire toutes les cases mémoire et afficher leurs adresses et contenus byte val,add, col; col=1; for(add=0x00 ; add<=0x3f; add++) { if (readRegI2C(SLAVE_ADDR_DS13072, add, &val)==-1) Serial.println(" problème affichage mémoire"); else { if ( (add %8)==0) { Serial.print(add>>4,HEX); Serial.print(add & 0xf,HEX); Serial.print(" : "); } Serial.print(val>>4,HEX); Serial.print(val & 0xf,HEX); Serial.print(' '); if ( (add %8)==7) { Serial.println(); } } } Serial.println(); } ////////////////////////////////////////// void setTime(byte hour,byte minute,byte sec) { if (writeRegI2C(SLAVE_ADDR_DS13072, 2, hour)!=0) Serial.println(" problème réglage heure"); if (writeRegI2C(SLAVE_ADDR_DS13072, 1, minute)!=0) Serial.println(" problème réglage minute"); if (writeRegI2C(SLAVE_ADDR_DS13072, 0, sec)!=0) Serial.println(" problème réglage sec"); } ////////////////////////////////////////// void setDate(byte day,byte month,byte year) { if (writeRegI2C(SLAVE_ADDR_DS13072, 4, day)!=0) Serial.println(" problème réglage jour"); if (writeRegI2C(SLAVE_ADDR_DS13072, 5, month)!=0) Serial.println(" problème réglage mois"); if (writeRegI2C(SLAVE_ADDR_DS13072, 6, year)!=0) Serial.println(" problème réglage annee"); } ////////////////////////////////////////// void setup() { Serial.begin(9600); // start serial port at 9600 bps: Serial.print("Bonjour chargeur batterie"); Wire.begin(); // joindre le bus i2c en tant que maître if (0) { //activation de l'oscillateur interne, remet les secondes à 0 if (writeRegI2C(SLAVE_ADDR_DS13072, 0, 0x00)==-1) Serial.println(" problème activation oscillateur"); //réglage de l'heure setTime(0x14,0x00,0x00); setDate(0x17,0x02,0x18); } //activation sortie SQ à 1hz if ( writeRegI2C(SLAVE_ADDR_DS13072, 7, (byte)0x10)!=0) Serial.println(" problème réglage SquareWave"); } ////////////////////////////////////////// void loop() { /* //alterner sortie SQ clignote 1hz et allumée en continu writeRegI2C(SLAVE_ADDR_DS13072, 7, (byte)0x10); delay(5000); writeRegI2C(SLAVE_ADDR_DS13072, 7, (byte)0x0); delay(5000); */ /* //commande de SQ comme une GPIO writeRegI2C(SLAVE_ADDR_DS13072, 7, (byte)0x80); delay(1000); writeRegI2C(SLAVE_ADDR_DS13072, 7, (byte)0x00); delay(1000); */ displayTime(); delay(500); //dumping de toute la mémoire // dumpDS13072(); } ////////////////////////////////////////// ===fonction dump avec auto incrémentation d'adresse=== ////////////////////////////////////////// void dumpDS13072(byte *ptr_val) { //lire toutes les cases mémoire et afficher leurs adresses et contenus int cpt = 0; Wire.beginTransmission((byte)SLAVE_ADDR_DS13072);//démarre la transmission avec l'adresse du pérpiphérique Wire.write((byte)0x00); if (Wire.endTransmission()!=0) //stoppe la transmission { Serial.print(" erreur "); return; } int nbmax=32; //nombre maximum d'octets lisibles en 1 fois Wire.requestFrom((byte)SLAVE_ADDR_DS13072, (byte)nbmax);// demande la lecture de 64 octets depuis l'adresse du pérpiphérique do{ if (Wire.available()>=1) //si il y a au moins octet disponible { ptr_val[cpt] = Wire.read(); // lire l'octet Serial.print(cpt); Serial.print(" : "); Serial.println(ptr_val[cpt]); } cpt++; }while(cpt {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Consulter la documentation du composant DS1337 ( https://datasheets.maximintegrated.com/en/ds/DS1337-DS1337C.pdf ) pour comprendre la fonction supplémentaire qu'il propose par rapport au DS1307 et proposer un moyen d'exploiter cette fonctionnalité. Faire valider à l'enseignant votre proposition. ===== Exercice 4 : Utilisation de l' EEPROM I2C AT24C32===== {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Modifier le sketch précédent pour lire et écrire des cases dans la mémoire AT24C32. La documentation de ce composant est disponible ici: http://pdf.datasheetcatalog.com/datasheet/atmel/doc0336.pdf ATTENTION, par rapport à l'exercice précédent: - Le composant est lent pour écrire individuellement des octets. Après une écriture, il a besoin de temps avant de pouvoir gérer un ordre d'écriture suivant. Pour indiquer qu'il est occupé, le composant n'acquittera pas tant qu'il ne sera pas prêt. - Le composant comportant plus de 256 cases de mémoire, l'adresse d'une case est stockée sur plus de 8 bits. Il faudra donc adapter les types utilisés pour coder les adresses ainsi que les fonctions gérant l'envoi de l'adresse de la case. ////////////////////////////////////////// void dumpEEPROM() { //A COMPLETER pour lire toutes les cases mémoire et afficher leurs adresses et contenus byte val; unsigned int add; char test; add=0; do { while(readEEPROM(SLAVE_ADDR_EEPROM, add, &val==-1); if ((add%16)==0) { Serial.println(); Serial.print((add>>12)&0xf, HEX); Serial.print((add>>8)&0xf, HEX); Serial.print((add>>4)&0xf, HEX); Serial.print((add>>0)&0xf, HEX); Serial.print(" : "); } Serial.print((val>>4)&0xf, HEX); Serial.print((val>>0)&0xf, HEX); Serial.print(" "); add=add+1; } while(add<4096); } ////////////////////////////////////////// ===Gestion écriture avec timeout=== void ecrEeprom(int addr,byte val){ unsigned long timeout; timeout=millis(); bool fini=false; while (!fini){ if(writeRegEEPROM(0x50, addr, val) == 0) fini =true; else if (millis()>=timeout+6) { Serial.println("Problème écriture"); fini =true; } else Serial.println("."); } } ===== Exercice 5 : Utilisation de l' EEPROM I2C M24C01===== mémoire 128 octets datasheet: https://bvdp.inetdoc.net/files/iut/datasheetM24C01.pdf programme solution avec écriture de chaines terminées par 0 à partir d'une certaine adresse en mémoire. Gestion d'un delai de 5ms entre les écritures. Dump avec affichage en Hexa et en Ascii: #include //adresses codées sur 7bits #define SLAVE_ADDR_M24C01 0x50 ////////////////////////////////////////// char readRegI2C(char I2Caddr, byte Regaddr, byte * ptr_value) /*I2Caddr, l'adresse du composant sur le bus Regaddr, l'adresse du registre à lire ptr_value, pointeur pour renvoyer la valeur lue dans le registre retourne -1 si échec 0 sinon */ { Wire.beginTransmission(I2Caddr);//démarre la transmission avec l'adresse du pérpiphérique Wire.write((byte)Regaddr); //envoie l'adresse de la case if (Wire.endTransmission()!=0) //stoppe la transmission return -1; Wire.requestFrom((byte)I2Caddr,(byte) 1);// demande la lecture d'1 octet depuis l'adresse du pérpiphérique if (Wire.available()==1) //si l'octet est disponible { (* ptr_value) = Wire.read(); // lire l'octet return 0; } else return -1; } ////////////////////////////////////////// char writeRegI2C(char I2Caddr, byte Regaddr, byte value) /* I2Caddr, l'adresse du composant sur le bus Regaddr, l'adresse du registre à écrire value,la valeur à écrire dans le registre retourne -1 si échec 0 sinon */ { Wire.beginTransmission(I2Caddr);//démarre la transmission avec l'adresse du pérpiphérique Wire.write((byte)Regaddr); //envoie l'adresse de la case Wire.write((byte)value); //envoie la donnée if (Wire.endTransmission()==0) //stoppe la transmission return 0; else return -1; } ////////////////////////////////////////// void dumpM24C01() { byte val,add,off; for (add=0;add<128;add+=16) { if (add<0x10) Serial.print("0"); Serial.print(add, HEX); Serial.print(" : "); for (off=0;off<16;off++) { if (readRegI2C(SLAVE_ADDR_M24C01, add+off, &val)==0) { if (val<0x10) Serial.print("0"); Serial.print(val, HEX); } Serial.print(" "); } for (off=0;off<16;off++) { if (readRegI2C(SLAVE_ADDR_M24C01, add+off, &val)==0) { if ((val>=0x20) && (val<=0x7e)) Serial.write(val); else Serial.write('.'); } } Serial.println(" "); } Serial.println(" "); /* for (add=0;add<128;add++) if (readRegI2C(SLAVE_ADDR_M24C01, add, &val)==0) { if (add%16==0) { if (add<0x10) Serial.print("0"); Serial.print(add, HEX); Serial.print(" : "); } else Serial.print(" "); if (val<0x10) Serial.print("0"); Serial.print(val, HEX); if (add%16==15) Serial.println(" "); } Serial.println(" ");*/ } ////////////////////////////////////////// void writeStringM24C01(char * string, byte add) { while ((*string)!=0) { delay(5); //the doc states that at most 5ms are required after each write cycle if (writeRegI2C(SLAVE_ADDR_M24C01, add, *string)!=0) Serial.println(" problème écriture"); add++; string++; } } ////////////////////////////////////////// void setup() { Serial.begin(9600); // start serial port at 9600 bps: Serial.print("Bonjour"); Wire.begin(); // joindre le bus i2c en tant que maître writeStringM24C01("01234",0x3); writeStringM24C01("bonjour",0x17); writeStringM24C01("A",0x7e); writeStringM24C01("B",0x7f); // writeStringM24C01("\0x19",0x7f); } ////////////////////////////////////////// void loop() { delay(500); //dumping de toute la mémoire dumpM24C01(); } ////////////////////////////////////////// ===== Exercice 5 : DAC Microchip 4725===== Lire la documentation: https://www.sparkfun.com/datasheets/BreakoutBoards/MCP4725.pdf {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} Déterminer l'adresse du composant sur le bus. Proposer un schéma pour le connecter à l'arduino. Indiquer le contenu de la fonction permettant d'écrire une valeur sur la sortie du DAC. Indiquer la valeur à utiliser pour obtenir 2V en sortie du DAC si il est alimenté en 5V. Proposer un programme pour générer une rampe sur la sortie du DAC. documentation du module: https://cdn-learn.adafruit.com/downloads/pdf/mcp4725-12-bit-dac-tutorial.pdf librairie disponible: https://github.com/adafruit/Adafruit_MCP4725 Solution: #include //adresses codées sur 7bits #define SLAVE_ADDR_MCP_4725 0x62 ////////////////////////////////////////// char writeMcp4725(char addr, uint16_t value) /*addr, l'adresse du PCF8574 value, la valeur à écrire sur le port retourne -1 si échec 0 sinon */ { Wire.beginTransmission(addr);//démarre la transmission avec l'adresse du pérpiphérique Wire.write((byte)(0x40)); //envoie la donnée Wire.write((byte)(value>>4) & 0xff); //envoie la donnée Wire.write((byte)(value & 0x4)<<4); //envoie la donnée if (Wire.endTransmission()==0) //stoppe la transmission return 0; else return -1; } ////////////////////////////////////////// void setup() { Serial.begin(9600); // start serial port at 9600 bps: Serial.print("Bonjour"); Wire.begin(); // joindre le bus i2c en tant que maître TWBR=12;//I2C à 400Khz } ////////////////////////////////////////// void loop() { static uint16_t val=0; if (writeMcp4725(SLAVE_ADDR_MCP_4725,val)!=0) { Serial.println("pb ecriture MCP_4725 "); } // val++; val=val+256; if (val >4095) val=val-4096; } ===== Problème des cartes===== === cartes à réparer=== leds carte 7 s'allument tres peu -> il faut mettre un réseau de 470 ohm au lieu de 4700 ohm... A CHANGER inter dil foireux, il faut appuyer dessus, ce n'est pas un problème de support -> A CHANGER!!!! === cartes réparées=== pb led 7 carte 2 (entrée), c'est la rangée d'inter dil qui avait du vernis noir sur les broches, à décaper au cutter carte 10 led 1 toujours allumée (pb sortie) --> defaut PCB, réparé par cours jus avec la masse pb inter carte 14: inter dil foireux, réparée =====autres composants===== expander de ports I2C/SPI avec réglage d'adresse parmi 16 avec seulement 2 broches: http://www.nxp.com/documents/data_sheet/PCA9502.pdf =====PWM x16 via I2C PCA9685===== module adafruit et librairie: https://learn.adafruit.com/16-channel-pwm-servo-driver/using-the-adafruit-library et: https://www.adafruit.com/product/815 datasheet: https://cdn-shop.adafruit.com/datasheets/PCA9685.pdf cd ~/sketchbook/ wget https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library/archive/master.zip unzip Adafruit-PWM-Servo-Driver-Library-master.zip mv Adafruit-PWM-Servo-Driver-Library-master PWMServoDriver arduino La librairie apparaît dans Croquis->Importer bibliothèque->PWMServoDriver sketch inspiré de servo.ino présent dans le dossier example de la librairie. Modifié pour faire la commande pwm sur la voie 0 seulement et avec des TON de 1 et 2ms: /*************************************************** This is an example for our Adafruit 16-channel PWM & Servo driver Servo test - this will drive 16 servos, one after the other Pick one up today in the adafruit shop! ------> http://www.adafruit.com/products/815 These displays use I2C to communicate, 2 pins are required to interface. For Arduino UNOs, thats SCL -> Analog 5, SDA -> Analog 4 Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! Written by Limor Fried/Ladyada for Adafruit Industries. BSD license, all text above must be included in any redistribution ****************************************************/ #include #include // called this way, it uses the default address 0x40 Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(); // you can also call it with a different address you want //Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41); uint8_t servonum = 0; void setup() { Serial.begin(9600); Serial.println("16 channel Servo test!"); pwm.begin(); pwm.setPWMFreq(53); //pour 50Hz effectif } void loop() { servonum=0; Serial.println(servonum); pwm.setPWM(servonum, 0, 200); //1ms delay(2000); pwm.setPWM(servonum, 0, 400); //2ms delay(2000); } ===Autre librairie=== mieux faite, avec possibilité de rentrer les valeurs de Ton dans un tableau et de les appliquer en une fois. https://forum.arduino.cc/index.php?topic=417809.0