; 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:
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; 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