Exercices I2C ************* Programme N°3b -------------- En branchant un potentiomètre comme dans l'exercice précédent, réaliser une fonction "allumage crépusculaire progressif" avec hytérésis. Variante : A chaque allumage des lampes, on inverse l'ordre d'allumage des lampes (pour les faire consommer de manière uniforme). Le potentiomètre simule une LDR (Claire=faible valeur. Obscure = forte valeur) Le principe du programme est que l'on commande deux lampes (représentées chacune par un groupe de 8 leds, en fonction de la luminosité. Lorsque la luminosité commence à baisser, on allume une lampe, puis, lorsque la luminosité a encore baissé un peu, on allume les deux lampes. Pour réaliser l'hystérésis, il faut que les niveaux d'allumage soient plus "obscures" que les niveaux d'extinction. (Sinon, on risque un clignotement de la lampe) Le K8000 est commuté en position 0 (OFF-OFF) Circuit Type Adresse du circuit __________________________________________Fixe_A3_A2_A1_A0__Déc._ IC17 : PCF8574A n°0 IO-chip no: 0 0111 0 0 0 X = 112+X IC18 : PCF8574A n°1 IO-chip no: 1 0111 0 0 1 X = 114+X IC19 : TDA8444 DAC chip 0100 0 0 0 X = 32+X 'Comme dans l'exercice précédent, (Exercice 3a2) nous allons travailler 'en fonction des différents états. Reportez-vous à cet exercice pour la 'discussion concernant les différents états. 'Il faudra y apporter la modification suivante : 'Lors de l'allumage d'une seule lampe, il nous suffira d'allumer '"l'autre lampe", et pour cela il suffit de mettre en mémoire quelle 'est la dernière lampe que nous avons allumé (Variable oldlamp). 'Voici maintenant notre petite liste de tâches : 'Début de boucle "infinie" ' Lecture du Pot ' Si Etat = 0 ' Alors Si Pot <= B ' Alors Etat = 0 ' Sinon Etat = 1 ' Si Etat = 1 ' Alors Si Pot <= A ' Alors Etat = 0 ' Sinon Si Pot compris entre A et D ' Alors Etat = 1 ' Sinon Etat = 2 ' Si Etat = 2 ' Alors Si Pot <= C ' Alors Etat = 1 ' Sinon Etat = 2 ' Allumer les lampes en fonction de Etat ' et si une seule lampe alors allumer "l'autre lampe" 'Fin de la boucle "infinie" 'Si nous appliquons par rapport au programme précedent la seule 'modification concernant l'allumage d'une lampe, nous remarquerons 'qu'il y a un clignotement des leds lorsque l'on se trouve dans 'l'état 1. Ceci est du au fait que nous nous trouvons dans une boucle '"infinie" et que nous faisons un très grand nombre de lecture en permanence. 'Et évidement, comme nous venons de le définir, à chaque lecture, 'nous allumons "l'autre lampe", d'où le clignotement. 'Pour résoudre ce problème, il suffit d'ajouter une variable (oldetat) 'qui reprendra l'état précédent, et nous permetra de ne rien faire s'il 'n'y a pas de changement d'état. 'D'ailleur, ceci nous fait remarquer que le programme précédent était '"incomplet" (pour ne pas dire faux), puisqu'il provoquait en fait 'un clignotement, mais comme s'était toujours la même lampe, nous ne l'avions 'pas remarqué. '******************************************************************************* ' DEBUT DU PROGRAMME ' ****************** BEGIN 'Début du programme ' Définition des variables utilisées ' ********************************** DECLARE i2cadr AS byte 'Adresse du circuit I2C DECLARE i2cio AS byte 'Valeur lue ou écrite dans le composant I2C DECLARE i2cdata AS byte 'Octet de valeur lue ou écrite sur le bus I2C DECLARE i2cack AS bit 'Bit "ACK" du bus I2C DECLARE i2csda AS nibble 'Pin du circuit qui sert de "I2C DATA" DECLARE i2cscl AS nibble 'Pin du circuit qui sert de "I2C CLOCK" DECLARE i2cad1WR AS byte 'Adresse d'écriture du premier PCF8574 DECLARE i2cad2WR AS byte 'Adresse d'écriture du deuxième PCF8574 DECLARE i2cad3RD AS byte 'Adresse de lecture du PCF8591 DECLARE Seuil1 AS byte 'Premier seuil (voir ci-dessus) DECLARE Seuil2 AS byte 'Deuxième seuil (voir ci-dessus) DECLARE Hys AS nibble 'Hystérésis (voir ci-dessus) DECLARE Pot AS byte 'Lecture de l'entrée DECLARE Etat AS nibble 'Etat des lampes (0, 1 ou 2) DECLARE Oldetat AS nibble 'Etat précédent (0, 1 ou 2) DECLARE Oldlamp AS bit 'Lampe "précédente" (0 ou 1) DECLARE I AS byte 'Variable de travail DECLARE J AS byte 'Variable de travail DECLARE K AS byte 'Variable de travail DECLARE L AS byte 'Variable de travail ' Définition des constantes ' ************************* LET i2csda = 12 LET i2cscl = 13 LET i2cad1WR = 112 LET i2cad2WR = 114 LET i2cad3RD = 145 LET Seuil1 = 120 LET Seuil2 = 150 LET Hys = 10 ' Programme Principal ' ******************* LET oldlamp=0 'Initialisation de la variable oldlamp LET oldetat=0 'Initialisation de la variable oldetat DO WHILE k=k 'Début de la boucle "infinie" LET i2cadr = i2cad3RD 'Adresse du circuit PCF8591 GOSUB READBYTE 'Lecture du premier convertisseur LET Pot = i2cio 'Mise en mémoire de la valeur du POT IF Etat = 0 'Si pas de lampes allumées (S0) IF Pot <= Seuil1 'Si pot <= seuil1 (B) LET Etat = 0 'Etat d'extinction des lampes (S0) ELSE 'Si pot > seuil1 (B) LET Etat = 1 'Etat d'allumage d'une lampe (S1) ENDIF ' ELSE ' IF Etat = 1 'Si une lampe allumée (S1) IF Pot <= Seuil1-Hys 'Si pot <= seuil1-Hys (A) LET Etat = 0 'Etat d'extinction des lampes (S0) ELSE 'Si pot > seuil1-Hys (A) IF Pot <= Seuil2 'Si pot <= seuil2 (D) LET Etat = 1 'Etat d'allumage d'une lampe (S1) ELSE 'Si pot > seuil2 (D) LET Etat = 2 'Etat d'allumage des deux lampes (S2) ENDIF ' ENDIF ' ELSE 'Si les deux lampes allumées (S2) IF Pot <= Seuil2-Hys 'Si pot <= seuil2-Hys (C) LET Etat = 1 'Etat d'allumage d'une lampe (S1) ELSE 'Si pot > seuil2-Hys (C) LET Etat = 2 'Etat d'allumage des deux lampes ENDIF ' ENDIF ' ENDIF ' IF Etat = 0 'Si Etat = 0 IF oldetat <> 0 'Si changement d'état GOSUB Lamp0 'Extinction des lampes Oldetat = 0 'Mise à jour de Oldetat ENDIF ' ELSE 'Si Etat > 0 IF Etat = 1 'Si Etat = 1 IF oldetat <> 1 'Si changement d'état GOSUB LAMP1 'Allumage d'une lampe Oldetat = 1 'Mise à jour de Oldetat ENDIF ' ELSE 'Si Etat = 2 IF oldetat <> 2 'Si changement d'état GOSUB LAMP2 'Allumage des deux lampes Oldetat = 2 'Mise à jour de Oldetat ENDIF ' ENDIF ' ENDIF ' LET Etatp = Etat 'Mise à jour de l'état précedent ENDDO 'Fin de la boucle infinie END 'Routine d'extinction des deux lampes (groupe de leds) SUB LAMP0 LET i2cio = 0 'Valeur pour éteindre les leds LET i2cadr = i2cad1WR 'Adresse du premier PCF8574 GOSUB WRITEBYTE 'Envoi vers le bus I2C LET i2cio = 0 'Valeur pour éteindre les leds LET i2cadr = i2cad2WR 'Adresse du deuxième PCF8574 GOSUB WRITEBYTE 'Envoi vers le bus I2C ENDSUB 'Routine d'allumage de l'autre lampe (groupe dee leds) SUB LAMP1 IF oldlamp = 1 'Test si Oldlamp = deuxième GOSUB lamp1a 'Allumage de lampe n°1 LET oldlamp = 0 'Oldlamp = première ELSE 'Si Oldlamp = première GOSUB lamp1b 'Allumage de la lampe n°2 LET oldlamp = 1 'Oldlamp = deuxième ENDIF ' ENDSUB 'Routine d'allumage de la premiere lampe (groupe de leds) SUB LAMP1a LET i2cio = 255 'Valeur pour allumer les leds LET i2cadr = i2cad1WR 'Adresse du premier PCF8574 GOSUB WRITEBYTE 'Envoi vers le bus I2C LET i2cio = 0 'Valeur pour éteindre les leds LET i2cadr = i2cad2WR 'Adresse du deuxième PCF8574 GOSUB WRITEBYTE 'Envoi vers le bus I2C END SUB 'Routine d'allumage de la deuxième lampe (groupe de leds) SUB LAMP1b LET i2cio = 255 'Valeur pour allumer les leds LET i2cadr = i2cad2WR 'Adresse du premier PCF8574 GOSUB WRITEBYTE 'Envoi vers le bus I2C LET i2cio = 0 'Valeur pour éteindre les leds LET i2cadr = i2cad1WR 'Adresse du deuxième PCF8574 GOSUB WRITEBYTE 'Envoi vers le bus I2C END SUB 'Routine d'allumage des deux lampes (groupe de leds) SUB LAMP2 LET i2cio = 255 'Valeur pour allumer les leds LET i2cadr = i2cad1WR 'Adresse du premier PCF8574 GOSUB WRITEBYTE 'Envoi vers le bus I2C LET i2cio = 255 'Valeur pour allumer une led LET i2cadr = i2cad2WR 'Adresse du deuxième PCF8574 GOSUB WRITEBYTE 'Envoi vers le bus I2C ENDSUB ' Routine d'écriture de i2cio à l'adresse i2cadr sur le bus I2C SUB WRITEBYTE GOSUB i2cstart 'Mise du bus en condition "START" LET i2cdata = i2cadr 'Valeur de l'adresse à écrire GOSUB i2ctx 'Ecriture de l'adresse I2C GOSUB ackrx 'Attente de l'ACK LET i2cdata = i2cio 'valeur de la donnée à écrire GOSUB i2ctx 'Ecriture de la donnée I2C GOSUB ackrx 'Attente de l'ACK GOSUB i2cstop 'Mise du bus en condition "STOP" ENDSUB ' Routine de lecture dans i2cio à l'adresse i2cadr sur le bus I2C ' Attention, cette routine ne lit qu'un seul octet, ce qui dans ' le cas du PCF8591 ne permet de lire que la valeur du premier ' convertisseur analogique-digital. (Ce qui est demandé içi) SUB READBYTE GOSUB i2cstart 'Mise du bus en condition "START" LET i2cdata = i2cadr 'Valeur de l'adresse à écrire GOSUB i2ctx 'Ecriture de l'adresse I2C GOSUB ackrx 'Attente de l'ACK GOSUB i2crx 'Lecture de la donnée I2C GOSUB acktx 'ACK du "master" LET i2cio = i2cdata 'Mise a jour de la valeur lue GOSUB i2cstop 'Mise du bus en condition "STOP" ENDSUB 'Définitions des subroutines I2C '******************************* ' I2CSTART : Mise du bus I2C dans la condition "START" SUB I2CSTART Mise à 1 de la pin définie par i2csda 'I2C data line Mise à 1 de la pin définie par i2cscl 'I2C clock line Mise à 0 de la pin définie par i2csda 'I2C data line Mise à 0 de la pin définie par i2cscl 'I2C clock line ENDSUB ' I2CSTOP : Mise du bus I2C dans la condition "STOP" SUB I2CSTOP Mise à 0 de la pin définie par i2csda 'I2C data line Mise à 1 de la pin définie par i2cscl 'I2C clock line Mise à 1 de la pin définie par i2csda 'I2C data line ENDSUB ' I2CTX : Emission d'un octet sur le bus I2C SUB I2CTX LET i = 0 DO WHILE i < 8 LET j = (i)ème bit de i2cdata Mise à la valeur j de la pin définie par i2csda Mise à 1 de la pin définie par i2cscl PAUSE 1ms Mise à 0 de la pin définie par i2cscl Mise à 0 de la pin définie par i2csda ENDDO ENDSUB ' I2CRX : Réception d'un octet sur le bus I2C SUB I2CRX LET i = 0 DO WHILE i < 8 Mise à 1 de la pin définie par i2cscl LET j = valeur lue sur la pin définie par i2csda LET (i)ème bit de i2cdata = j PAUSE 1ms Mise à 0 de la pin définie par i2cscl ENDDO ENDSUB ' ACKTX : Emission d'un "ACK" sur le bus I2C SUB ACKTX Mise à 1 de la pin définie par i2cscl Mise "haute-impédance" de la pin définie par i2csda PAUSE 1ms Mise à 0 de la pin définie par i2csda PAUSE 1ms Mise à 0 de la pin définie par i2cscl ENDSUB ' ACKRX : Réception d'un "ACK" sur le bus I2C SUB ACKRX Mise à 1 de la pin définie par i2cscl Mise "haute-impédance" de la pin définie par i2csda PAUSE 1ms Attente d'un 0 sur la pin définie par i2csda PAUSE 1ms Mise à 0 de la pin définie par i2cscl ENDSUB '****************************************************************************** 'Programme pour le STAMP '******************************************************************* '* Exercice 5-3b : Réaliser un allumage crépusculaire (variante). * '* Le pot simule une ldr et la variation lumineuse * '* A chaque allumage des lampes, on inverse * '* l'ordre d'allumage des lampes * '******************************************************************* ' 'Utilisation de la carte Micro-Info et le K8000 'La ligne SDA est connectée en P12 'La ligne SCL est connectée en P13 'Notez que la carte MI est allimentée par le K8000 'Description du K8000 'Circuit Type Adresse du circuit '_________________________________________Fixe_A3_A2_A1_A0__Décimal_ 'IC17 : PCF8574A n°0 IO-chip no: 0 0111 0 0 0 X = 112+X 'IC18 : PCF8574A n°1 IO-chip no: 1 0111 0 0 1 X = 114+X 'IC20 : PCF8591 ADDA chip 1001 0 0 0 X = 144+X 'Pour faciliter l'écriture de notre programme, nous utiliserons 'les routines "ECR17" et "ECR18", qui en fait seront la routine '"WRITEBYTE" de la structurée, mais avec les paramètres d'adresse 'respectivement de IC17 et IC18 du K8000. En plus, la routine '"READBYTE" sera ici remplacée par une routine "LEC20" qui 's'occupera d'écrire l'octet dans le PCF8591 'De plus, les routines Lamp0, Lamp1 et Lamp2 sont ajutées. 'Elles allumes respectivement 0, 1 ou 2 "lampes" 'Liste des routines I2C '---------------------- 'I2CSTART : Mise du bus I2C dans la condition "START" 'I2CSTOP : Mise du bus I2C dans la condition "STOP" 'I2CTX : Emission d'un octet sur le bus I2C 'I2CRX : Reception d'un octet sur le bus I2C 'ACKTX : Emission d'un "ACK" sur le bus I2C 'ACKRX : Reception d'un "ACK" sur le bus I2C 'Définition des entrées-sorties OUTPUT 0 OUTPUT 1 OUTPUT 2 OUTPUT 3 OUTPUT 4 OUTPUT 5 OUTPUT 6 OUTPUT 7 OUTPUT 8 OUTPUT 9 INPUT 10 OUTPUT 11 INPUT 12 INPUT 13 INPUT 14 INPUT 15 'Initialisation du système INS = 0 OUTS = 0 'Définition des variables de travail I VAR BYTE J VAR BYTE K VAR BYTE L VAR BYTE 'Definition des variables I2C i2caddr var byte 'Mot de l'adresse du composant I2C i2cdata var byte 'Octet de valeur lue ou ecrite sur le bus I2C i2cack var bit 'Bit "ACK" du bus I2C i2csda con 12 'Pin I2C data du Stamp2 i2cscl con 13 'Pin I2C clock du Stamp2 i2cad1WR con %01110000 'Adresse d'ecriture du 1er PCF8574A i2cad2WR con %01110010 'Adresse d'ecriture du 2eme PCF8574A i2cad3RD con %10010001 'Adresse de lecture du PCF8591 i2cio var byte 'Valeur lue ou ecrite dans le composant i2c 'Définition des autres variables pot var Byte 'Valeur de la "ldr" etat var Nib 'Etat des lampes oldetat var Nib 'Etat précedent oldlamp var Bit 'Etat antérieur de la lampe 'Definition des constantes seuil1 con 100 'Premier seuil de lumière seuil2 con 150 'deuxième seuil de lumière hys con 30 'Valeur de l'hystérésis 'Debut du programme principal '**************************** oldlamp=1 'Initialisation de la variable oldlamp oldetat=0 'Initialisation de la variable oldetat deb: GOSUB lec20 'Lecture du Potentiomètre IF etat > 0 Then s1 'Test inverse si pas de lampe allumée IF pot > seuil1 THEN s2 'Si pot > seuil1 (B) etat = 0 'Etat d'extinction des lampes GOTO s3 'Aller à commande des lampes s2: etat = 1 'Etat d'allumage d'une lampe GOTO s3 'Aller à commande des lampes s1: IF etat > 1 THEN s4 'Test inverse si une lampe allumée IF pot > seuil1-hys THEN s5 'Si pot > seuil1-hys (A) etat = 0 'Etat d'extinction des lampes GOTO s3 'Aller à commande des lampes s5: IF pot > seuil2 THEN s6 'Si pot > seuil2 (D) etat = 1 'Etat d'allumage d'une lampe GOTO s3 'Aller à commande des lampes s6: etat = 2 'Etat d'allumage des deux lampes GOTO s3 'Aller à commande des lampes s4: IF pot > seuil2-hys THEN s7 'Si pot > seuil2-hys (C) etat = 1 'Etat d'allumage d'une lampe GOTO s3 'Aller à commande des lampes s7: etat = 2 'Etat d'allumage des deux lampes s3: IF etat > 0 THEN s8 'Début de la commande des lampes IF oldetat=0 THEN deb 'Bouclage si pas de changement d'état GOSUB lamp0 'Extinction des lampes si etat = 0 oldetat=0 'Mise à jour de l'état précédent GOTO deb: 'bouclage s8: IF etat > 1 THEN s9 'Test inverse pour etat = 1 IF oldetat=1 THEN deb 'Bouclage si pas de changement d'état GOSUB lamp1 'Allumage d'une lampe oldetat=1 'Mise à jour de l'état précédent GOTO deb 'bouclage s9: IF oldetat=2 THEn deb 'Bouclage si pas de changement d'état GOSUB lamp2 'Allumage des deux lampes si etat = 2 oldetat=2 'Mise à jour de l'état précédent GOTO deb 'bouclage STOP 'ECR17 : Ecriture de la valeur contenue dans i2cio dans IC17 '*********************************************************** ecr17: GOSUB i2cstart 'Mise du bus en condition "START" i2cdata=i2cad1WR 'Valeur a envoyer = ecriture 1er PCF8574A GOSUB i2ctx 'Envoi de i2cdata sur le bus i2c GOSUB ackrx 'Attente de l'ACK i2cdata=i2cio 'Valeur a envoyer = i2cio GOSUB i2ctx 'Envoi de i2cdata sur le bus i2c GOSUB ackrx 'Attente de l'ACK GOSUB i2cstop 'Mise du bus en condition "STOP" RETURN 'ECR18 : Ecriture de la valeur contenue dans i2cio dans IC18 '*********************************************************** ecr18: GOSUB i2cstart 'Mise du bus en condition "START" i2cdata=i2cad2WR 'Valeur a envoyer = ecriture 2eme PCF8574A GOSUB i2ctx 'Envoi de i2cdata sur le bus i2c GOSUB ackrx 'Attente de l'ACK i2cdata=i2cio 'Valeur a envoyer = i2cio GOSUB i2ctx 'Envoi de i2cdata sur le bus i2c GOSUB ackrx 'Attente de l'ACK GOSUB i2cstop 'Mise du bus en condition "STOP" RETURN 'LEC20 : Lecture du premier convertisseur AD du PCF8591 : IC20 '************************************************************* LEC20: GOSUB i2cstart 'Mise du bus en condition "START" i2cdata=i2cad3RD 'Valeur a envoyer = lecture PCF8591 GOSUB i2ctx 'Envoi de i2cdata sur le bus i2c GOSUB ackrx 'Attente de l'ACK GOSUB i2crx 'lecture de l'octet i2cio=i2cdata 'Mise en mémoire de la valeur GOSUB i2cstop 'Mise du bus en condition "STOP" pot = i2cio 'Mise à jour de la valeur de POT RETURN 'LAMP0 : Extinction des deux "lampes" '************************************ lamp0: i2cio = 255 GOSUB ecr17 GOSUB ecr18 RETURN 'LAMP1 : Allumage d'une "lampe" '****************************** lamp1: IF oldlamp=0 then lmp1 GOSUB lamp1a oldlamp=0 GOTO lmp2 lmp1: GOSUB lamp1b oldlamp=1 lmp2: RETURN 'LAMP2 : Allumage des deux "lampes" '********************************** lamp2: i2cio = 0 GOSUB ecr17 GOSUB ecr18 RETURN 'LAMP1a : Allumage de la lampe n°1 '********************************* lamp1a: i2cio = 0 GOSUB ecr18 i2cio = 255 GOSUB ecr17 RETURN 'LAMP1b : Allumage de la lampe n°2 '********************************* lamp1b: i2cio = 0 GOSUB ecr17 i2cio = 255 GOSUB ecr18 RETURN '************************************************************************** 'Liste des routines I2C '********************** 'I2CSTART : Mise du bus I2C dans la condition "START" '**************************************************** i2cstart: HIGH i2csda 'Mise a 1 de I2C data HIGH i2cscl 'Mise a 1 de I2C clock LOW i2csda 'Mise a 0 de I2C data RETURN 'I2CSTOP : Mise du bus I2C dans la condition "STOP" '************************************************** i2cstop: LOW i2csda 'Mise a 0 de I2C data line HIGH i2cscl 'Mise a 1 de I2C clock line HIGH i2csda 'Mise a 1 de I2C data line RETURN 'I2CTX : Emission d'un octet sur le bus I2C '****************************************** i2ctx: SHIFTOUT i2csda,i2cscl,1,[i2cdata] RETURN 'I2CRX : Reception d'un octet sur le bus I2C '******************************************* i2crx: SHIFTIN i2csda,i2cscl,0,[i2cdata] RETURN 'ACKTX : Emission d'un "ACK" sur le bus I2C '****************************************** acktx: SHIFTOUT i2csda,i2cscl,1,[%0\1] RETURN 'ACKRX : Reception d'un "ACK" sur le bus I2C '******************************************* ackrx: SHIFTIN i2csda,i2cscl,0,[i2cack\1] RETURN 'FIN DES ROUTINES I2C '**************************************************************************