技术热线: 4007-888-234

pic单片机的模拟I2C通信

更新时间: 2019-03-21
阅读量:338

****************************************

*         PIC单片机的模拟I2C通信       *

****************************************


;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

;                      Copyright (C) 1997 by Innovatus

; This code may be distributed and used freely provided that this

; copyright notice stays intact and that any modifications are noted.

; For more information about Innovatus: http://www.innovatus.com

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

;    File Name: i2c_low.asm

;       Author: Alan G. Smith

;      Purpose: This code is borrowed from Microchip with all of the fancy

;               stuff taken out.

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


InitI2CBusMaster


;************************************************************

TxmtStartBit

   bsf    Bus_Busy                       ; on a start condition bus is busy

   bsf    STATUS, RP0                    ; Select page 1

   bsf    _SDA                           ; set SDA high

   bsf    _SCL                           ; clock is high

   call   Delay40uSec                    ; This is necessary for setup time

   bcf    _SDA                           ; This gives a falling edge on SDA while clock is high

   call   Delay47uSec                    ; Necessary for START HOLD time

   return

;************************************************************

TxmtStopBit

   bsf    STATUS, RP0                    ; Select page 1

   bcf    _SCL                           ; clock is low

   bcf    _SDA                           ; set SDA low

   bsf    _SCL                           ; clock is pulled up

   call   Delay40uSec                    ; Setup time for STOP condition

   bsf    _SDA                           ; rising edge on SDA while CLOCK is high

   call   Delay47uSec                    ; makes sure a START isn't sent immediately after a STOP

   bcf    Bus_Busy                       ; The bus isn't busy anymore

   return

;************************************************************

AbortI2C

   call   TxmtStopBit                    ; Send a stop bit

   bsf    Abort                          ; set the abort bit

   return

;************************************************************

TxmtSlaveAddr

   movf   SlaveAddr, w                   ; Move slave address to W

   bcf    ACK_Error                      ; reset Acknowledge error bit

   movwf  I2CData                        ; move W to I2C Data

   bcf    I2CData, LSB                   ; Set for write

   btfsc  Slave_RW                       ; If skip then write operation

   bsf    I2CData, LSB                   ; Clear for read

   call   SendData                       ; send the address

   btfss  Txmt_Success                   ; skip if successful

   goto   AddrSendFail                   ; Oops, we failed

   retlw  TRUE                           ; return true

AddrSendFail

   btfss  ACK_Error                      ; was there an error acknowledging

   retlw  FALSE                          ; No, so return 0

   call   TxmtStopBit                    ; Address not acknowleged, so send STOP bit

   retlw  FALSE                          ; Unsuccessful, so return 0


;************************************************************

SendData

; We might should make a copy of the data here, the example does but I don't see why!!!

   bsf    Txmt_Progress                  ; We are in the middle of transmitting

   bcf    Txmt_Success                   ; reset success bit

   movlw  0x08

   movwf  I2CBitCount                    ; Set I2C Bit Count to 8

   bsf    STATUS, RP0                    ; Select page 1

TxmtNextBit:

   bcf    _SCL                           ; Set clock Low

   rlf    I2CData, F                     ; MSB First, Note that I2CData is Destroyed

   bcf    _SDA                           ; Set clock based on what the MSB is

   btfsc  STATUS,C                       ; Was the MSB a 1

   bsf    _SDA                           ; Nope set it high

   call   Delay47uSec                    ; guarantee min LOW TIME tLOW & Setup time

   bsf    _SCL                           ; set clock high

   call   Delay40uSec                    ; guarantee min HIGH TIME tHIGH

   decfsz I2CBitCount, F                 ; are we done yet

   goto   TxmtNextBit                    ; nope, send the next bit

;

; Check For Acknowledge

;

   bcf    _SCL                           ; reset clock

   bsf    _SDA                           ; Release SDA line for Slave to pull down

   call   Delay47uSec                    ; guarantee min LOW TIME tLOW & Setup time

   bsf    _SCL                           ; clock for slave to ACK

   call   Delay40uSec                    ; guarantee min HIGH TIME tHIGH

   bcf    STATUS, RP0                    ; Select PAGE 0 to test SDA pin

   btfsc  SdaPin                         ; SDA should be pulled low by slave if OK

   goto   TxmtErrorAck                   ; Uh oh, slave isn't behaving (or isn't there)

   bsf    STATUS, RP0                    ; Select PAGE 1

   bcf    _SCL                           ; reset clock

   bcf    Txmt_Progress                  ; reset progress bit in Bus Status

   bsf    Txmt_Success                   ; Transmission successful

   bcf    ACK_Error                      ; ACK OK

   return

TxmtErrorAck

   bsf    STATUS,RP0                     ; select page 1

   bsf    _SDA                           ; tristate SDA

   bsf    _SCL                           ; tristate SCL

   bcf    Txmt_Progress                  ; reset progress bit in Bus Status

   bcf    Txmt_Success                   ; Transmission NOT successful

   bsf    ACK_Error                      ; No ACK From Slave

   return


;************************************************************

GetData

   bsf    Rcv_Progress                   ; set Bus status for txmt progress

   bcf    Rcv_Success                    ; reset status bit

   movlw  0x08

   movwf  I2CBitCount

RcvNextBit

   bsf    STATUS, RP0                    ; page 1 for TRIS manipulation

   bcf    _SCL                           ; lower clock

   bcf    _SDA                           ; lower data line

   call   Delay47uSec                    ; guarantee min LOW TIME tLOW & setup time

   bsf    _SCL                           ; clock high, data sent by slave

   call   Delay40uSec                    ; guarantee min HIGH TIME tHIGH

   bcf    STATUS, RP0                    ; select page 0 to read Ports

   bcf    STATUS, C                      ; 0 out Status

   btfsc  SdaPin                         ; Check state of pin

   bsf    STATUS, C                      ; Pin was high, set status

   rlf    I2CData, F                     ; left Shift data (MSB first)

   decfsz I2CBitCount, F                 ; Are we done yet

   goto   RcvNextBit                     ; Nope, go get the next one

;

; Generate ACK bit if not last byte to be read,

; if last byte Gennerate NACK ; do not send ACK on last byte, main routine will send a STOP bit

;

   bsf    STATUS, RP0                    ; Page 1 for TRIS manipulation

   bcf    _SCL                           ; pull SCL low

   bcf    _SDA                           ; ACK by pulling SDA low

   btfsc  Last_Byte_Rcv                  ; Is it the last byte to receive

   bsf    _SDA                           ; If so, send NACK by setting SDA high

   call   Delay47uSec                    ; guarantee min LOW TIME tLOW & Setup time

   bsf    _SCL                           ; Raise Clock back up

   call   Delay40uSec                    ; guarantee min HIGH TIME tHIGH

RcvEnd:

   bcf    _SCL                           ; reset clock

   bcf    Rcv_Progress                   ; reset bit in Bus Status

   bsf    Rcv_Success                    ; transmission successful

   bcf    ACK_Error                      ; ACK OK

   return


Delay47uSec:

   movlw ((_47uS_Delay-5)/3 + 1)         ; move delay into W

DlyK

   movwf DelayCount                      ; move what is in W to DelayCount

   decfsz   DelayCount, F                ; Decrement DelayCount

   goto  $-1                             ; Loop until 0

   return                                ; return


Delay40uSec:

   movlw ((_40uS_Delay-8)/3 + 1)         ; move delay into W

   goto  DlyK                            ; goto DlyK loop



以下为测试程序(pic16f84)


;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

;                   Copyright (C) 1997 by Innovatus

;                        All Rights Reserved.

; This code may be distributed and used freely provided that this

; copyright notice stays intact and that any modifications are noted.

; For more information about Innovatus: http://www.innovatus.com

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

;    File Name: testI2C.asm

;       Author: Alan G. Smith

;      Purpose: This is testing out the I2C code.

;

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    LIST P=16f84, F=INHX8M, C=100, N=59


#include "p16f84.inc"


XTAL_FREQ       equ     10000000        ; the crystal frequency we are using

ClkOut          equ     XTAL_FREQ / 4   ; the number of cycles per second


_40uS_Delay set   (ClkOut/250000)

_47uS_Delay set   (ClkOut/212766)

_50uS_Delay set   (ClkOut/200000)


#define SclPin  PORTA, 0                ; Pin for SCL (I2C)

#define SdaPin  PORTA, 1                ; Pin for SDA (I2C)


#define _SCL    TRISA, 0                ; How do we toggle SCL

#define _SDA    TRISA, 1                ; How do we toggle SDA


#define MSB   7

#define LSB   0

#define TRUE  1

#define FALSE 0


InitTrisA       equ     0x07            ; The Initial state to TRIS port A.


#define  Bus_Busy      BusStatus,0

#define  Abort         BusStatus,1

#define  Txmt_Progress BusStatus,2

#define  Rcv_Progress  BusStatus,3

#define  Txmt_Success  BusStatus,4

#define  Rcv_Success   BusStatus,5

#define  Fatal_Error   BusStatus,6

#define  ACK_Error     BusStatus,7


#define  Slave_RW      BusControl,0

#define  Last_Byte_Rcv BusControl,1

#define  SlaveActive   BusControl,2


CBLOCK     0x0C                        ; I2C Ram needed

   BusStatus                            ; The I2C Status register

   BusControl                           ; The I2C Control register

   I2CBitCount                          ; Number of bits left to send (or receive)

   I2CData                              ; Data (note: This is DESTROYED when sending)

   SlaveAddr                            ; Slave Address

ENDC


CBLOCK

   DelayCount                           ; used to figure out precise time delays

ENDC



   org 0                                 ; Reset Vector

   goto   start                          ; Goto Start


start

   bcf    INTCON, GIE                    ; Turn off interrupts in this critical part of code!

   bcf    STATUS, RP0                    ; Select Page 0 of registers

   movlw  0x0C                           ; Make sure there are 0's on SCL and SDA

   movwf  PORTA                          ; We write 1's to TX since 0 is a start bit

   bsf    STATUS, RP0                    ; Select Page 1 of registers

   movlw  InitTrisA                      ; Load W with the value for TRIS A

   movwf  TRISA                          ; movw the value from W into TRIS A

;*************** DEBUG CODE (let us use LEDs) *******************

   clrf   TRISB

;****************************************************************

   clrf   BusStatus                      ; Let's clear out busStatus before we start

   clrf   BusControl                     ; Let's clear out busControl before we start

;*************** TEST CODE *******************

   clrf   PORTB

main

   movlw  0xB0                           ; address of EEPROM

   movwf  SlaveAddr                      ; move into SlaveAddress register

   call   IsSlaveActive                  ; Check and see if the slave is active

   movlw  0xFF                           ; move FF into w (turn all LED's on)

   btfss  SlaveActive                    ; If the slave is active, leave it

   movlw  0xAA                           ; We didn't find it, turn off half.

   bcf    STATUS, RP0                    ; Select page 0 of registers

   movwf  PORTB                          ; move W to PortB


done                                     ; Game over man!

   goto   done                           ; endless loop


IsSlaveActive

   bcf    Slave_RW                       ; set for write operation

   call   TxmtStartBit                   ; Transmit Start Bit

   call   TxmtSlaveAddr                  ; Transmit Slave Address

   bcf    SlaveActive                    ; Assume not present

   btfss  ACK_Error                      ; skip if NACK, device is not present or not responding

   bsf    SlaveActive                    ; ACK received, device present & listening

   call   TxmtStopBit

   return


#include "i2c_low.asm"


END