****************************************
* 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