ssous
        PUSH    PSW                     ; noter C
        MOV     R6,#8                   ; charger le compteur de bits
I2CGET1:
        SETB    C
        LCALL   I2CDOBIT                ; lire un bit sur le bus I2C
        RLC     A           ; le faire entrer dans l'accu par rotation
        MOV     R7,A                    ; sauvegarder l'accumulateur
        MOV     A,I2C_STAT  ; vrifier si dpassement du temps
        ANL     A,#I2C_TO
        JNZ     I2CGET2                 ; oui -> saut
        MOV     A,R7                    ; rcuprer l'accumulateur
        DJNZ    R6,I2CGET1              ; et reprendre la boucle

        POP     PSW                     ; rcuprer l'ancien bit C
        LJMP    I2CDOBIT          ; l'crire sur le bus (Ack / NoAck)

I2CGET2:
        POP     PSW          ; vider la pile
        RET                  ; et retour (erreur signale dans I2C_STAT)

;-------------------------------------------------------------------------
;                                                                        :
;  Routine I2CDOBIT                                                      :
;                                                                        :
;  Fonction :                                                            :
;  Emet un bit sur le bus I2C (avec une impulsion d'horloge). Lit SDA.   :
;  En criture, C contient le bit  crire. En lecture C doit tre mis   :
;   1. Au retour, C contient le bit lu.                                 :
;  La routine accepte les demandes d'tat d'attente par les esclaves au  :
;  moyen de la ligne SCL. Toutefois l'allongement maximal ne peut durer  :
;  que 4 x (I2C_CMAX) cycles. I2C_CMAX est la variable qui contient le   :
;  temps d'attente maximal. Un dpassement de ce temps lve le drapeau   :
;  Time-Out dans l'octet d'tat I2C.                                     :
;                                                                        :
;  Entre :                                                              :
;  C         -  bit  crire (1 en lecture)                              :
;  I2C_CMAX  -  temps d'attente maximal du bus I2C                       :
;                                                                        :
;  Sortie :                                                              :
;  C         -  bit lu (ignorer en criture)                             :
;                                                                        :
;  Consquences :                                                        :
;  R7, modification ventuelle de I2C_STAT                               :
;                                                                        :
;  Occupation de la pile (sans l'adresse de retour) :                    :
;  nant                                                                 :
;                                                                        :
;  Temps d'excution (CALL exclu) :                                      :
;  Meilleur cas (pas d'attente)  :  14 cycles                            :
;  Chaque attente dure :            4 cycles                             :
;  Pire des cas (Time-Out)  :  environ 4 x (I2C_CMAX)                    :
;                                                                        :
;  Remarque :                                                            :
;  La routine sous cette forme fonctionne jusqu' 14,4 MHz. Pour des     :
;  frquences d'horloge plus leves, le dlai entre SDA et SCL est trop :
;  court, modifier le code ici.                                          :
;                                                                        :
;-------------------------------------------------------------------------
I2CDOBIT:
        MOV     PI2C_SDA,C              ; SDA  1
        MOV     R7,I2C_CMAX             ; charger le compteur R7
                                        ; dpassement de temps
        SETB    PI2C_SCL                ; SCL  1

I2CDOB1:
        JB      PI2C_SCL,I2CDOB2        ; si SCL = 1 -> saut
        DJNZ    R7,I2CDOB1              ; sinon boucler (un autre occupant
                                        ; du bus retient SCL  0,
                               ; par exemple pour crer un tat d'attente)
        ORL     I2C_STAT,#I2C_TO        ; si compteur dborde -> lever
                                ; drapeau dpassement dans l'octet d'tat
        RET                             ; et retour

I2CDOB2:
        NOP                             ; 3 NOPs en guise de retard
        NOP                             ; (suffisants jusqu' 14,4 MHz)
        NOP
        MOV     C,PI2C_SDA              ; lire SDA
        CLR     PI2C_SCL                ; rabaisser SCL
        RET                             ; et retour


                :
;  Consquences :                                                        :
;  PI2C_SDA & PI2C_SCL sont hauts aprs excution                        :
;                                                                        :
;  Occupation de la pile (sans l'adresse de retour) :                    :
;  nant                                                                 :
;                                                                        :
;  Temps d'excution (CALL exclu) :                                      :
;  10 cycles                                                             :
;                                                                        :
;  Remarque :                                                            :
;  La routine sous cette forme fonctionne jusqu' 14,4 MHz. Pour des     :
;  frquences d'horloge suprieures, le dlai entre les fronts montants  :
;  de SCL et SDA est trop court. Il faut donc modifier le code ici.      :
;-------------------------------------------------------------------------
I2CSTOP:
        CLR     PI2C_SDA                ; abaisse SDA (par prcaution)
        SETB    PI2C_SCL                ; lve SCL
        NOP                             ; 5 NOP en guise de retard
        NOP                             ; (suffit jusqu' 14,4 MHz)
        NOP
        NOP
        NOP
        SETB    PI2C_SDA                ; maintenant abaisse aussi SDA
        RET

;-------------------------------------------------------------------------
;                                                                        :
;  Routine I2CSEND                                                       :
;                                                                        :
;  Fonction :                                                            :
;  Emet un octet I2C. Utilise pour cela la routine I2CDOBIT. Voir cette  :
;  routine pour les dtails bit  bit. Ragit au dpassement de temps.   :
;  Autorise l'esclave  demander des temps d'attente. L'acquiescement    :
;  de l'esclave se trouve dans C et dans l'octet d'tat I2C_STAT.        :
;                                                                        :
;  Entre :                                                              :
;  accu      -  octet  mettre                                          :
;  I2C_CMAX  -  variable qui contient la dure maximale de l'attente     :
;               (pour I2CDOBIT)                                          :
;                                                                        :
;  Sortie :                                                              :
;  C         -  0  =  l'octet est reu  (1 sinon)                        :
;                                                                        :
;  Consquences :                                                        :
;  A,R6,R7, modification de I2C_STAT                                     :
;                                                                        :
;  Occupation de la pile (sans l'adresse de retour) :                    :
;  2 octets                                                              :
;                                                                        :
;  Temps d'excution (CALL exclu) :                                      :
;  Au mieux (pas d'attente, avec/sans Ack)  :  224/225 cycles            :
;                                                                        :
;  Remarque :                                                            :
;  La frquence d'horloge maximale de cette routine dpend de I2CDOBIT.  :
;  Avec une horloge  12 MHz, la frquence d'horloge du bus est de       :
;  40,18 kHz dans le meilleur des cas (pas d'attente).                   :
;                                                                        :
;-------------------------------------------------------------------------
I2CSEND:
        CLR     PI2C_SCL                ; voir Clock ci-dessous
        MOV     R6,#8                   ; charge le compteur de bits
I2CSEND2:
        RLC     A                       ; aller chercher le bit actuel
        LCALL   I2CDOBIT                ; le placer sur le bus I2C
        MOV     R7,A                    ; sauvegarder l'accu
        MOV     A,I2C_STAT              ; vrifier si dpassement temps
        ANL     A,#I2C_TO
        JNZ     I2CSEND3                ; oui -> saut (casse-toi)
        MOV     A,R7                    ; rcupre l'accumulateur
        DJNZ    R6,I2CSEND2             ; et re-boucle

        SETB    C
        LCALL   I2CDOBIT                ; lire acquiescement
        ANL     I2C_STAT,#-I2C_ACK
        JNC     I2CSEND3                ; si Acknowledge -> ok
        ORL     I2C_STAT,#I2C_ACK       ; sinon lever le drapeau dans
I2CSEND3:                               ; l'octet d'tat
        RET                             ; et ciao bello

;-------------------------------------------------------------------------
;                                                                        :
;  Routine I2CSBLOK                                                      :
;                                                                        :
;  Fonction :                                                            :
;  Emet sur le bus I2C un bloc de donnes d'un tampon en RAM interne.    :
;  L'accumulateur contient la longueur du bloc, R0 le dbut du tampon.   :
;  La routine vrifie, avant l'mission de chaque octet, l'octet d'tat  :
;  I2C_STAT. En cas d'erreur ou de dfaut d'acquiescement, le transfert  :
;  est interrompu.  La routine appelante peut alors vrifier I2C_STAT.   :
;  Le registre R0 contient le nombre des octets non transmis, 0 si le    :
;  transfert a t men  bien.                                          :
;  La routine utilise lors de l'excution la routine I2CSEND.            :
;                                                                        :
;  Entre :                                                              :
;  accu      -  nombre des octets  transmettre                          :
;  R0        -  adresse du tampon                                        :
;  I2C_CMAX  -  variable qui indique la dure maximale de l'attente      :
;               (pour I2CSEND & I2CDOBIT)                                :
;                                                                        :
;  Sortie :                                                              :
;  I2C_STAT  -  tat des routines du  bus I2C                            :
;  R0        -  nombre des octets non transmis (0 si tout est ok)        :
;                                                                        :
;  Consquences :                                                        :
;  A,R6,R7                                                               :
;                                                                        :
;  Occupation de la pile (sans l'adresse de retour) :                    :
;  5 octets                                                              :
;                                                                        :
;-------------------------------------------------------------------------
I2CSBLOK:
        ADD     A,R0                    ; calcule l'adresse de fin du
        PUSH    ACC                     ; bloc  tansfrer et
                                        ; le ssauvegarde

;-------------------------------------------------------------------------
;  La boucle I2CSBLK2 se droule jusqu' ce que tous les octets soient   :
;  transmis ou qu'une erreur se produise ou dfaut d'acquiescement       :
;-------------------------------------------------------------------------

I2CSBLK2:
        MOV     A,I2C_STAT              ; qurir l'octet d'tat
        ANL     A,#I2C_BUSY+I2C_TO+I2C_ACK      ; vrifier si erreur
        JNZ     I2CSBLK3                ; oui -> sortie

        POP     ACC                     ; vrifier si tous les octets
        PUSH    ACC                     ; ont t transmis
        XRL     A,R0
        JZ      I2CSBLK3                ; si oui, tire-toi

        MOV     A,@R0                   ; tirer un octet du tampon
        LCALL   I2CSEND                 ; l'mettre sur le bus I2C
        INC     R0                      ; avancer le pointeur du tampon
        SJMP    I2CSBLK2                ; retourner au dbut de la boucle

I2CSBLK3:
        POP     ACC                     ; rcuprer l'adresse sauvegarde
        CLR     C                       ; soustraire l'adresse atteinte
        SUBB    A,R0                    ; rsultat nul si tous les octets
                                        ; ont t mis, sinon nombre de
                                        ; ceux qui restent
        RET                             ; et sortir

;-------------------------------------------------------------------------
;                                                                        :
;  Routine I2CGET                                                        :
;                                                                        :
;  Fonction :                                                            :
;  Lit un octet sur le bus I2C. Utilise  cette fin la routine I2CDOBIT. :
;  Voir l les dtails bit  bit. Ragit au dpassement de temps.        :
;  Accepte les demandes d'tat d'attente de l'esclave. L'octet peut tre :
;  suivi d'un aquiescement (C=0) ou non (C=1).                           :
;                                                                        :
;  Entre :                                                              :
;  C         -  neuvime bit (effectivement mis)                        :
;  I2C_CMAX  -  temps d'attente maximal (pour I2CDOBIT)                  :
;                                                                        :
;  Sortie :                                                              :
;  accu      -  octet lu                                                 :
;                                                                        :
;  Consquences :                                                        :
;  R6,R7, ventuellement modification de I2C_STAT                        :
;                                                                        :
;  Occupation de la pile (sans l'adresse de retour) :                    :
;  3 octets                                                              :
;                                                                        :
;  Temps d'excution (CALL exclu) :                                      :
;  Au mieux (sans attente)  :  230 cycles                                :
;                                                                        :
;  Remarque :                                                            :
;  La frquence d'horloge maximale de cette routine dpend de I2CDOBIT.  :
;  Avec une horloge  12 MHz, la frquence d'horloge du bus est de       :
;  39,13 kHz dans le meilleur des cas (pas d'attente).                   :
;                                                                        :
;-------------------------------------------------------------------------
I2CGET:
        CLR     PI2C_SCL                ; voir  Clock ci-dessous
        PUSH    PSW                     ; noter C
        MOV     R6,#8                   ; charger le compteur de bits
I2CGET1:
        SETB    C
        LCALL   I2CDOBIT                ; lire un bit sur le bus I2C
        RLC     A           ; le faire entrer dans l'accu par rotation
        MOV     R7,A                    ; sauvegarder l'accumulateur
        MOV     A,I2C_STAT  ; vrifier si dpassement du temps
        ANL     A,#I2C_TO
        JNZ     I2CGET2                 ; oui -> saut
        MOV     A,R7                    ; rcuprer l'accumulateur
        DJNZ    R6,I2CGET1              ; et reprendre la boucle

        POP     PSW                     ; rcuprer l'ancien bit C
        LJMP    I2CDOBIT          ; l'crire sur le bus (Ack / NoAck)

I2CGET2:
        POP     PSW          ; vider la pile
        RET                  ; et retour (erreur signale dans I2C_STAT)

;-------------------------------------------------------------------------
;                                                                        :
;  Routine I2CDOBIT                                                      :
;                                                                        :
;  Fonction :                                                            :
;  Emet un bit sur le bus I2C (avec une impulsion d'horloge). Lit SDA.   :
;  En criture, C contient le bit  crire. En lecture C doit tre mis   :
;   1. Au retour, C contient le bit lu.                                 :
;  La routine accepte les demandes d'tat d'attente par les esclaves au  :
;  moyen de la ligne SCL. Toutefois l'allongement maximal ne peut durer  :
;  que 4 x (I2C_CMAX) cycles. I2C_CMAX est la variable qui contient le   :
;  temps d'attente maximal. Un dpassement de ce temps lve le drapeau   :
;  Time-Out dans l'octet d'tat I2C.                                     :
;                                                                        :
;  Entre :                                                              :
;  C         -  bit  crire (1 en lecture)                              :
;  I2C_CMAX  -  temps d'attente maximal du bus I2C                       :
;                                                                        :
;  Sortie :                                                              :
;  C         -  bit lu (ignorer en criture)                             :
;                                                                        :
;  Consquences :                                                        :
;  R7, modification ventuelle de I2C_STAT                               :
;                                                                        :
;  Occupation de la pile (sans l'adresse de retour) :                    :
;  nant                                                                 :
;                                                                        :
;  Temps d'excution (CALL exclu) :                                      :
;  Meilleur cas (pas d'attente)  :  14 cycles                            :
;  Chaque attente dure :            4 cycles                             :
;  Pire des cas (Time-Out)  :  environ 4 x (I2C_CMAX)                    :
;                                                                        :
;  Remarque :                                                            :
;  La routine sous cette forme fonctionne jusqu' 14,4 MHz. Pour des     :
;  frquences d'horloge plus leves, le dlai entre SDA et SCL est trop :
;  court, modifier le code ici.                                          :
;                                                                        :
;-------------------------------------------------------------------------
I2CDOBIT:
        MOV     PI2C_SDA,C              ; SDA  1
        MOV     R7,I2C_CMAX             ; charger le compteur R7
                                        ; dpassement de temps
        SETB    PI2C_SCL                ; SCL  1

I2CDOB1:
        JB      PI2C_SCL,I2CDOB2        ; si SCL = 1 -> saut
        DJNZ    R7,I2CDOB1              ; sinon boucler (un autre occupant
                                        ; du bus retient SCL  0,
                               ; par exemple pour crer un tat d'attente)
        ORL     I2C_STAT,#I2C_TO        ; si compteur dborde -> lever
                                ; drapeau dpassement dans l'octet d'tat
        RET                             ; et retour

I2CDOB2:
        NOP                             ; 3 NOPs en guise de retard
        NOP                             ; (suffisants jusqu' 14,4 MHz)
        NOP
        MOV     C,PI2C_SDA              ; lire SDA
        CLR     PI2C_SCL                ; rabaisser SCL
        RET                             ; et retour


