; I2C.ASM ;*************************************************************************** ;* Listing du 03.07.98 * ;* Permet au PIC16C84 de communiquer sur un bus i2c en tant que composant * ;* esclave * ;* Ce listing est la base de tous les autres projets avec des PIC16C84 * ;* se connectant … l'interface universelle PC<-->Bus i2c * ;* * ;* (c) GROSSE Christophe 1998 * ;* email : chrisg@easynet.fr * ;* URL : http://perso.easynet.fr/~chrisg * ;* * ;* mmm * ;* (o o) * ;* ÄÄÄÄÄÄoOOÄÄ(_)ÄÄOOoÄÄÄÄ * ;* * ;*************************************************************************** List P=16c84 Include p16c84.inc ;************************************************************ ;* D‚finition des variables globales et de leurs adresses * ;************************************************************ constant vars = 0ch ;Adresse de la premiŠre variable globale constant adr_fix = 30h ;L'adresse de base de ce composant dans la ;chaine i2c sera 24 ( a modifier selon le projet ) ;------- Variables globales utilis‚es pour la transmition i2c ---------- device_adr EQU vars+0 ;Adresse du composant ( partie fixe + partie variable ) status_temp EQU vars+1 ;Sauvegarde des flags d'‚tat w_temp EQU vars+2 ;Sauvegarde du registre w i_byte EQU vars+3 ;Tampon octet lu sur le bus i2c o_byte EQU vars+4 ;Tampon octet a envoyer sur le bus i2c _n EQU vars+5 ;index ( compteur de bits ) dir EQU vars+6 ;Direction de la communication com EQU vars+7 ;Octet de commande dat EQU vars+8 ;Octet de donn‚e reg0 EQU vars+9 ;Registre Nø0 reg1 EQU vars+10 ;Registre Nø1 reg2 EQU vars+11 ;Registre Nø2 reg3 EQU vars+12 ;Registre Nø3 reg4 EQU vars+13 ;Registre Nø4 reg5 EQU vars+14 ;Registre Nø5 reg6 EQU vars+15 ;Registre Nø6 reg7 EQU vars+16 ;Registre Nø7 _reg EQU vars+17 ;Compteur pour restauration des registres ;----- Variables globales utilis‚es dans l'application utilisateur ------- ;***************************** ;* D‚finition d'‚quivalents * ;***************************** ;--------------- Equivalents utilis‚s pour la transmition i2c ------------- sda EQU 0 ;Ligne RB0/INT , ligne de donnée du bus I2C scl EQU 1 ;Ligne RB1 , Ligne d'horloge du bus I2C A0 EQU 4 ;Ligne RB4 , Ligne d'adresse du composant A1 EQU 5 ;Ligne RB5 , Ligne d'adresse du composant A2 EQU 6 ;Ligne RB6 , Ligne d'adresse du composant adr_mask EQU 70h ;masque pour les lignes RB4 … RB6 ;----------- Equivalent utilis‚s par l'application utilisateur ------------ org 0 ;Le programme commence … l'adresse START goto start ;************************************************* ; Le gestionnaire d'interruption commence ici * ;************************************************* org 4 ;Le gestionnaire d'interruption commence au label handler handler: movwf w_temp ;On sauve le registre W est le registre STATUS swapf STATUS,W ;Dans les variables W_TEMP et STATUS_TEMP movwf status_temp ; bsf STATUS,RP0 ;bank 1 btfss INTCON,INTF ;Test interruption externe INT ? goto inter bcf STATUS,RP0 ;bank 0 btfsc PORTB,scl ;On test si scl est haut goto i2c ;Si oui , une condition de start arrive ... bcf INTCON,INTF ;Efface le drapeau d'interruption externe bsf INTCON,INTE ;Autorise a nouveau l'interruption externe bsf STATUS,RP0 ;Bank 1 inter: btfsc INTCON,RBIF ;Test interruption RB INT call init_adr ;Si oui , initialise l'adresse du composant bcf STATUS,RP0 ;Bank 0 swapf status_temp,W ;Restaure les valeurs originales des registres movwf STATUS ;W et STATUS swapf w_temp,F swapf w_temp,W ; retfie ;Fin de la routine d'interuption ;Le GLOBAL INTERUPT est r‚activ‚ ;***************************************** ;* Le programme principal commence ici * ;***************************************** start: bcf STATUS,RP0 ;Bank 0 bcf INTCON,GIE ;Interdiction de toute interuption ;On lit l'adresse 8 de l'EEPROM pour ;savoir si l'on restaure son contenu lors ;du d‚marage movlw .8 ;adresse en EEPROM vaut 8 movwf EEADR ;On initialise l'adresse de l'EEPROM bsf STATUS,RP0 ;Bank 1 bsf EECON1,RD ;On lit l'EEPROM bcf STATUS,RP0 ;Bank 0 nop movfw EEDATA btfss EEDATA,4 ;On teste le bit 4 goto noload ;On ne restaure pas les registres ;On restaure tous les registres a partir de l'EEPROM movlw 0 ;On commence par REG0 movwf EEADR ;On initialise l'adresse de l'EEPROM bcf STATUS,C ;On annule la retenue addlw .21 ;On ajoute l'offset du registre REG0 movwf FSR ;On stocke l'adresse du registre voulu dans FSR movlw .8 movwf _reg ;On initialise l'index de comptage … 8 loop9: bsf STATUS,RP0 ;Bank 1 bsf EECON1,RD ;On lit l'EEPROM bcf STATUS,RP0 ;Bank 0 nop ;Un petit temps d'attente movfw EEDATA movwf INDF ;On copie la donn‚e dans le registre point‚ par FSR incf FSR,F ;On pointe sur le registre suivant incf EEADR,F ;On pointe sur l'adresse suivante en EEPROM decfsz _reg,F goto loop9 ;On boucle tant que l'index n'est pas nul noload: bsf STATUS,RP0 ;Bank 1 bcf OPTION_REG,INTEDG ;Une interruption INT sera caus‚ par une transition HAut vers BAS sur SDA bcf OPTION_REG,NOT_RBPU ;Les r‚sistances pullup sont initialis‚s pour le port RB bsf TRISB,A0 ;A0 est une entr‚e bsf TRISB,A1 ;A1 est une entr‚e bsf TRISB,A2 ;A2 est une entr‚e bsf TRISB,scl ;SCL est une entr‚e bsf TRISB,sda ;SDA est une entr‚e bcf STATUS,RP0 ;Bank 0 movlw 0 movwf dir ;mettre la variable de direction a z‚ro bcf INTCON,RBIF ;annule le flag d'interuption RB bcf INTCON,INTF ;annule le flag d'interuption INT bsf INTCON,INTE ;Autorise l'interruption externe sur INT/RB0 bsf INTCON,RBIE ;Autorise l'interruption au changement sur RB4 … RB6 bsf INTCON,GIE ;Autorise les interruptions de maniŠre globale ;initialise l'adresse du composant en fonction de A0 A1 A2. movfw PORTB ;Lecture du port B andlw adr_mask ;masque "et" b'01110000' movwf device_adr ;d‚cale trois fois … droites pour obtenir bcf STATUS,C ;Mise a z‚ro de la retenu rrf device_adr,F ;l'adresse variable pr‚sente sur RB2 … RB4 rrf device_adr,F ; rrf device_adr,F ; movlw adr_fix ;Adresse fixe du composant addwf device_adr,F ;Ajoute l'adresse fixe du composant pour obtenir . ;l'adresse complŠte call init_adr ;On initialise l'adresse du composant ;************************************************************************** ; Boucle principale du programme , selon le projet * ;************************************************************************** main: ;Boucle principale goto main ;On boucle ;**************************************************************************** ;* Cette routine initialise l'adresse du composant en fonction de A0 A1 A2. * ;**************************************************************************** init_adr: bcf STATUS,RP0 ;Bank 0 movfw PORTB ;Lecture du port B andlw adr_mask ;masque "et" b'01110000' movwf device_adr ;d‚cale trois fois … droites pour obtenir bcf STATUS,C ;Mise a z‚ro de la retenu rrf device_adr,F ;l'adresse variable pr‚sente sur RB2 … RB4 rrf device_adr,F ; rrf device_adr,F ; movlw adr_fix ;Adresse fixe du composant addwf device_adr,F ;Ajoute l'adresse fixe du composant pour obtenir . ;l'adresse complŠte bcf INTCON,RBIF ;Efface le drapeau d'interruption RB INT bsf INTCON,RBIE ;valide a nouveau l'interuption RB INT return ;on revient au gestionnaire de handle ;**************************************************** ;* Routine i2c : traite l'arriv‚e d'une trame i2c * ;**************************************************** i2c: bcf STATUS,RP0 ;bank 0 call in_byte ;lecture de l'adresse sur le bus i2c (adresse+direction) bcf dir,0 ;La direction par d‚faut est l'‚criture PC -> PIC btfsc i_byte,0 ;On teste la direction de la communication bsf dir,0 ;La direction est la lectute PC <- PIC bcf i_byte,0 ;On ne garde que l'adresse , on efface l'information de direction movfw i_byte bcf STATUS,Z ;On efface le bit ZERO xorwf device_adr,W ;on la compare avec l'adresse du composant btfss STATUS,Z goto no_acq ;La transmition actuelle ne concerne pas le composant call ack ;Ok l'adresse est bonne , on acquitte l'octet btfsc dir,0 goto lecture goto ecriture fin_transmit: bcf STATUS,RP0 ;Register BANK 0 bcf INTCON,INTF ;Efface le drapeau d'interruption externe bsf INTCON,INTE ;Autorise a nouveau l'interruption externe bsf STATUS,RP0 ;Bank 1 goto inter ;on revient au gestionnaire de handle no_acq: call no_ack goto fin_transmit lecture: call in_byte ;On lit l'octet d'adresse du registre a lire call ack ;On acquitte cet octet movfw i_byte movwf com ;Stocke cet octet dans COM btfsc com,3 ;On teste le bit3 de l'adresse goto ident ;Si il est mis , identification du composant movlw .7 ;On extrait l'adresse du registre dans COM andwf com,W bcf STATUS,C ;On annule la retenue addlw .21 ;On ajoute l'offset du registre REG0 movwf FSR ;On stocke l'adresse du registre voulu dans FSR movfw INDF ;On lit le contenu du registre point‚ par FSR movwf o_byte ;On stocke celui-ci dans o_byte call out_byte ;On envoie le contenu du registre sur le bus i2c goto fin_transmit ident: movlw .66 ;On renvoie la chaine d'identification movwf o_byte ;de l'application compos‚e de 8 caractˆres call out_byte ;Envoie la lettre 'B' movlw .97 movwf o_byte call out_byte ;Envoie la lettre 'a' movlw .115 movwf o_byte call out_byte ;Envoie la lettre 's' movlw .101 movwf o_byte call out_byte ;Envoie la lettre 'e' movlw .32 movwf o_byte call out_byte ;Envoie la lettre ' ' movlw .49 movwf o_byte call out_byte ;Envoie la lettre '1' movlw .46 movwf o_byte call out_byte ;Envoie la lettre '.' movlw .48 movwf o_byte call out_byte ;Envoie la lettre '0' goto fin_transmit ecriture: call in_byte ;On lit l'octet de commande call ack ;On acquitte cet octet movfw i_byte movwf com ;On stocke l'octet de commande dans COM call in_byte ;On lit l'octet de donn‚e call ack ;On acquitte cet octet movfw i_byte movwf dat ;On stocke l'octet de donn‚e dans DAT movlw .224 ;On traite l'octet de commande andwf com,W ;On ne garde que les bits b7 b6 b5 de COM movwf dir ;On stocke ceux ci dans dir movlw .0 bcf STATUS,Z ;On annule le flag Z xorwf dir,W ;On teste si on appelle la fonction Nø0 btfsc STATUS,Z goto fonc_0 ;On execute la fonction Nø0 movlw .32 xorwf dir,W ;On teste , fonction 1 ? btfsc STATUS,Z goto fonc_1 ;On execute la fonction Nø1 movlw .64 xorwf dir,W ;On teste , fonction 2 ? btfsc STATUS,Z goto fonc_2 ;On execute la fonction Nø2 movlw .96 xorwf dir,W ;On teste , fonction 3 ? btfsc STATUS,Z goto fonc_3 ;On execute la fonction Nø3 movlw .128 xorwf dir,W ;On teste , fonction 4 ? btfsc STATUS,Z goto fonc_4 ;On execute la fonction Nø4 movlw .160 xorwf dir,W ;On teste , fonction 5 ? btfsc STATUS,Z goto fonc_5 ;On execute la fonction Nø5 movlw .192 xorwf dir,W ;On teste , fonction 6 ? btfsc STATUS,Z goto fonc_6 ;On execute la fonction Nø6 movlw .224 xorwf dir,W ;On teste , fonction 7 ? btfsc STATUS,Z goto fonc_7 ;On execute la fonction Nø7 goto fin_transmit ;Fin de l'analyse de la trame i2c ;***************************************************************** ; Fonction Nø0 : Octet de donn‚e -> registre point‚ par b2 b1 b0 * ;***************************************************************** fonc_0: movlw .7 ;On extrait l'adresse du registre dans COM andwf com,W bcf STATUS,C ;On annule la retenue addlw .21 ;On ajoute l'offset du registre REG0 movwf FSR ;On stocke l'adresse du registre voulu dans FSR movfw dat movwf INDF ;on charge le registre point‚ par FSR avec l'octet de donn‚e goto fin_transmit ;******************************************************* ; Fonction Nø1 : Octet de donn‚e -> Tous les registres * ;******************************************************* fonc_1: movlw .21 movwf FSR ;On charge FSR avec l'offset du registre REG0 movlw .8 movwf _n ;On charge l'index de comptage … 8 loop3: movfw dat movwf INDF ;Octet de donn‚e -> REG0 incf FSR,F ;registre suivant decfsz _n,F ;on boucle tant que l'index n'est pas nul goto loop3 goto fin_transmit ;******************************************************** ; Fonction Nø2 : Copie du registre point‚ dans l'EEPROM * ;******************************************************** fonc_2: movlw .7 ;On extrait l'adresse du registre dans COM andwf com,W movwf EEADR ;On initialise l'adresse de l'EEPROM bcf STATUS,C ;On annule la retenue addlw .21 ;On ajoute l'offset du registre REG0 movwf FSR ;On stocke l'adresse du registre voulu dans FSR movfw INDF ;On lit la donn‚e contenue dans le registre point‚ par FSR movwf EEDATA ;On initialise la donn‚ a ‚crire dans l'EEPROM bsf STATUS,RP0 ;Bank 1 bsf EECON1,WREN ;Autorise l'‚criture dans l'EEPROM movlw 55h ;On execute la s‚quence d'‚criture en EEPROM movwf EECON2 movlw .170 movwf EECON2 bsf EECON1,WR ;Le cycle d'‚criture en EEPROM commence... bcf EECON1,WREN loop4: btfss EECON1,EEIF ;On teste si l'‚criture de l'EEPROM est termin‚ goto loop4 ;Non , on boucle bcf EECON1,EEIF ;On efface le flag indiquant que le cycle est termin‚ bcf STATUS,RP0 ;Bank 0 goto fin_transmit ;*********************************************************** ; Fonction Nø3 : Copie de tous les registres dans l'EEPROM * ;*********************************************************** fonc_3: movlw 0 ;On commence par REG0 movwf EEADR ;On initialise l'adresse de l'EEPROM bcf STATUS,C ;On annule la retenue addlw .21 ;On ajoute l'offset du registre REG0 movwf FSR ;On stocke l'adresse du registre voulu dans FSR movlw .8 movwf _n ;On initialise l'index de comptage … 8 loop5: movfw INDF ;On lit la donn‚e contenue dans le registre point‚ par FSR movwf EEDATA ;On initialise la donn‚ a ‚crire dans l'EEPROM bsf STATUS,RP0 ;Bank 1 bsf EECON1,WREN ;Autorise l'‚criture dans l'EEPROM movlw 55h ;On execute la s‚quence d'‚criture en EEPROM movwf EECON2 movlw .170 movwf EECON2 bsf EECON1,WR ;Le cycle d'‚criture en EEPROM commence... bcf EECON1,WREN loop6: btfss EECON1,EEIF ;On teste si l'‚criture de l'EEPROM est termin‚ goto loop6 ;Non , on boucle bcf EECON1,EEIF ;On efface le flag indiquant que le cycle est termin‚ bcf STATUS,RP0 ;Bank 0 incf EEADR,F ;Adresse suivante en EEPROM incf FSR,F ;On pointe sur le registre suivant decfsz _n,F goto loop5 ;On boucle tant que l'index n'est pas nul goto fin_transmit ;****************************************************************** ; Fonction Nø4 : Restaure le registre point‚ a partir de l'EEPROM * ;****************************************************************** fonc_4: movlw .7 ;On extrait l'adresse du registre dans COM andwf com,W movwf EEADR ;On initialise l'adresse de l'EEPROM bcf STATUS,C ;On annule la retenue addlw .21 ;On ajoute l'offset du registre REG0 movwf FSR ;On stocke l'adresse du registre voulu dans FSR bsf STATUS,RP0 ;Bank 1 bsf EECON1,RD ;On lit l'EEPROM bcf STATUS,RP0 ;Bank 0 movfw EEDATA movwf INDF ;On copie la donn‚e dans le registre point‚ par FSR goto fin_transmit ;****************************************************************** ; Fonction Nø5 : Restaure tous les registres a partir de l'EEPROM * ;****************************************************************** fonc_5: movlw 0 ;On commence par REG0 movwf EEADR ;On initialise l'adresse de l'EEPROM bcf STATUS,C ;On annule la retenue addlw .21 ;On ajoute l'offset du registre REG0 movwf FSR ;On stocke l'adresse du registre voulu dans FSR movlw .8 movwf _n ;On initialise l'index de comptage … 8 loop7: bsf STATUS,RP0 ;Bank 1 bsf EECON1,RD ;On lit l'EEPROM bcf STATUS,RP0 ;Bank 0 movfw EEDATA movwf INDF ;On copie la donn‚e dans le registre point‚ par FSR incf FSR,F ;On pointe sur le registre suivant incf EEADR,F ;On pointe sur l'adresse suivante en EEPROM decfsz _n,F goto loop7 ;On boucle tant que l'index n'est pas nul goto fin_transmit ;***************************************************************** ; Fonction Nø6 : Chargement des valeurs par d‚fault contenu dans * ; l'EEPROM … la mise sous tension * ; b4 = 0 : Non * ; b4 = 1 : Oui * ;***************************************************************** fonc_6: movlw .8 ;On ‚crit dans l'adresse 8 de l'EEPROM movwf EEADR ;On initialise l'adresse de l'EEPROM movlw .16 ;On extrait b4 de COM andwf com,W movwf EEDATA ;On initialise la donn‚ a ‚crire dans l'EEPROM bsf STATUS,RP0 ;Bank 1 bsf EECON1,WREN ;Autorise l'‚criture dans l'EEPROM movlw 55h ;On execute la s‚quence d'‚criture en EEPROM movwf EECON2 movlw .170 movwf EECON2 bsf EECON1,WR ;Le cycle d'‚criture en EEPROM commence... bcf EECON1,WREN loop8: btfss EECON1,EEIF ;On teste si l'‚criture de l'EEPROM est termin‚ goto loop8 ;Non , on boucle bcf EECON1,EEIF ;On efface le flag indiquant que le cycle est termin‚ bcf STATUS,RP0 ;Bank 0 goto fin_transmit ;********************************************* ; Fonction Nø7 : A d‚finir par l'utilisateur * ;********************************************* fonc_7: goto fin_transmit ;********************************************* ;* Cette routine lit un octet sur le bus i2c * ;* Le r‚sultat est dans i_byte * ;********************************************* in_byte: clrf i_byte ;Vide le tampon d'entr‚e movlw .8 ;initialise l'index a 8 bits movwf _n in_bit: btfsc PORTB,scl ;on attend que scl est bas goto in_bit wait3: btfss PORTB,scl ;Attend que scl remonte goto wait3 btfss PORTB,sda ;on lit la donn‚ pr‚sente sur sda goto in_zero ;On lit un zero goto in_un ;On lit un un in_zero: bcf STATUS,C ;On met la retenue a z‚ro rlf i_byte,F ;On d‚cale a gauche i_byte goto suite ;Prochain bit in_un: bcf STATUS,C ;On met la retenu … z‚ro rlf i_byte,F ;On d‚cale … gauche i_byte bsf i_byte,0 ;On met le bit de poid faible … un goto suite ;Prochain bit suite: decfsz _n,F ;on d‚cr‚mente l'index goto in_bit ;on lit le bit suivant temps que l'index n'est pas 0 return ;************************************************ ;* Cette routine envoie un octet sur le bus i2c * ;* L'octet a envoyer doit ˆtre dans o_byte * ;************************************************ out_byte: movlw .8 ;initialise l'index a 8 bits movwf _n bsf STATUS,RP0 ;Bank 1 bcf TRISB,sda ;SDA est une sortie bcf STATUS,RP0 ;Bank 0 out_bit: btfsc PORTB,scl ;on attend que scl est bas goto out_bit o_wait3: btfss PORTB,scl ;Attend que scl remonte goto o_wait3 rlf o_byte,F ;On d‚cale o_byte … gauche via la retenu btfss STATUS,C ;on teste la retenu goto out_zero ;On lit un zero goto out_un ;On lit un un out_zero: bcf PORTB,sda ;on met SDA … z‚ro goto o_suite ;Prochain bit out_un: bsf PORTB,sda ;on met SDA … un goto o_suite ;Prochain bit o_suite: decfsz _n,F ;on d‚cr‚mente l'index goto out_bit ;on envoie le bit suivant temps que l'index n'est pas 0 call no_ack ;on attend une neuvieme impulsion d'horloge avant de remettre SDA en entr‚e bsf STATUS,RP0 ;Bank 1 bsf TRISB,sda ;SDA est une entr‚e bcf STATUS,RP0 ;Bank 0 return ;***************************************** ; Cette routine acquitte la donn‚e re‡ue * ;***************************************** ack: btfsc PORTB,scl goto ack ;on attent le neuvieme coup d'horloge wait1: btfss PORTB,scl ;On attend que scl remonte goto wait1 ;On acquitte l'octet re‡u en maintenant sda … z‚ro bcf PORTB,sda ;on met SDA … z‚ro bsf STATUS,RP0 ;Bank 1 bcf TRISB,sda ;SDA est une sortie ( valant 0V ) bcf STATUS,RP0 ;Bank 0 wait2: btfsc PORTB,scl goto wait2 ;on attent que scl soit bas pour remettre SDA en entr‚e ;SDA est une entr‚e bsf STATUS,RP0 ;Bank 1 bsf TRISB,sda ;SDA est une entr‚e bcf STATUS,RP0 ;bank 0 return ;*********************************************** ; Cette routine n'acquitte pas la donn‚e re‡ue * ;*********************************************** no_ack: btfsc PORTB,scl goto no_ack ;on attent le neuvieme coup d'horloge wait6: btfss PORTB,scl ;On attend que scl remonte goto wait6 ;On n'acquitte pas en laissant SDA au niveau haut wait7: btfsc PORTB,scl goto wait7 ;on attent que scl soit bas pour remettre SDA en entr‚e return end ;That's all folk !