📄 i2c.asm
字号:
.title STA013 I2C Communications Routines
.sbttl Copyright (C) 2005 by Spare Time Gizmos. All rights reserved.
;++
; i2c.a51
;
; Copyright (C) 2005 by Spare Time Gizmos. All rights reserved.
;
; This file is part of the Spare Time Gizmos' MP3 Player firmware.
;
; Thisfirmware is free software; you can redistribute it and/or modify it under
; the terms of the GNU General Public License as published by the Free Software
; Foundation; either version 2 of the License, or (at your option) any later
; version.
;
; This program is distributed in the hope that it will be useful, but WITHOUT
; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
; more details.
;
; You should have received a copy of the GNU General Public License along with
; this program; if not, write to the Free Software Foundation, Inc., 59 Temple
; Place, Suite 330, Boston, MA 02111-1307 USA
;
; DESCRIPTION:
; This module contains low level I2C routines for talking with the STA013
; decoder chip. These really aren't generic I2C routines and probably can't
; be used to talk to any other device!
;
; REVISION HISTORY:
; dd-mmm-yy who description
; 15-May-05 RLA New File
; 3-Jul-05 RLA Clean everything up for publication.
; 15-Jul-05 DJA ported to ASX8051/SDCC
;--
.module I2C
.optsdcc -mmcs51 --model-small
.radix d
.include "player.inc"
.globl _Write013, _Read013
; Parameters for the Write013() and Read013() routines, SDCC style....
.globl _Write013_PARM_2,_Read013_PARM_2
.area OSEG (OVR,DATA)
_Write013_PARM_2::
.ds 1
_Read013_PARM_2::
.ds 1
.area CSEG (CODE)
;
;++
; BOOL Write013 (BYTE bAddr, BYTE bValue);
;
; DESCRIPTION:
; This function will write the 8 bit value specified by bValue to the STA013
; internal register specified by bAddr. It returns TRUE if the STA013 ACKs the
; transmission, and FALSE if the STA013 NACKs or times out...
;
; NOTE:
; DPL = bAddr
; bValue is _Write013_PARM_2
; on return, DPL = 0xFF (TRUE) or DPL = 0x00 (FALSE).
;-
_Write013:
LCALL START ; generate an I2C start condition
JC WRERR ; jump if any error (i.e. stuck I/O pin)
MOV A, #STA013_ADDR ; send the STA013 address
LCALL TXBYTE ; ...
JC WRERR ; jump if NACK
MOV A, DPL ; now send the STA013 register address
LCALL TXBYTE ; ...
JC WRERR ; ...
MOV A,_Write013_PARM_2; and finally send the register value
LCALL TXBYTE ; ...
JC WRERR ; ...
MOV DPL, #0xFF ; return TRUE for success
SJMP STOP ; generate I2C stop condition and return
; Here if an error occurs while sending...
WRERR: MOV DPL, #0 ; return FALSE for failre
SJMP STOP ; then generate a stop and return
;
;++
; BOOL Read013 (BYTE bAddr, PIBYTE pbValue);
;
; DESCRIPTION:
; This function will read the STA013 register specified by bAddr and return
; the 8 bit value in the IDATA variable pointed to by pbValue. It returns TRUE
; if all is well and FALSE if the STA013 NACKS the transmission or times out.
;
; NOTE:
; DPL = bAddr
; Read013_PARM_2 = pbValue
; on return, DPL = 0xFF (TRUE) or DPL = 0x00 (FALSE).
;-
_Read013:
LCALL START ; generate an I2C start condition
JC RDERR ; jump if error (e.g. a stuck bit)
MOV A, #STA013_ADDR ; send the STA013 address
LCALL TXBYTE ; ...
JC RDERR ; jump if NACK or other error
MOV A, DPL ; now send the STA013 register address
LCALL TXBYTE ; ...
JC RDERR ; ...
; We just sent the desired STA013 register address along with an I2C WRITE
; function just to set the STA013's internal register address register. Now,
; instead of actually sending a data byte, we generate a new start condition
; (the I2C specification calls this a "repeated start") along with the STA013
; I2C address again and a READ function. This aborts the previous transaction
; and allows us to read from the previously selected register. All of this
; is just because there's no way to send a STA013 internal register address
; as part of a READ function - all we can send is the STA013 I2C address.
LCALL START ; generate a repeated start
MOV A,#STA013_ADDR+1; send the STA013 address + LSB (for a READ)
LCALL TXBYTE ; ...
JC RDERR ; ...
; Note that the STA013 specifically does NOT want an ack from us after we
; read the data byte; it wants a NACK. Basically, the NACK tells it that we
; won't be reading any more bytes. Although this might seem wrong, it's
; fairly common of parts that support multiple sequential reads to work this
; way. The NACK tells the STA013 that we won't be reading any more registers;
; if we send an ACK, it will expect to transmit the next sequential register
; and it'll get confused when we send a stop.
SETB C ; send a nack!
LCALL RXBYTE ; after we read one byte
MOV R0,_Read013_PARM_2; return the byte we read
MOV @R0, A ; ... in *pbValue
MOV DPL, #0xFF ; and return TRUE to signal success
SJMP STOP ; generate a real I2C stop and we're done
; Here if something goes wrong while reading....
RDERR: MOV DPL, #0 ; in this case return FALSE
SJMP STOP ; but still generate a stop before returning
;
; I2C bit banging primitives...
;++
; This routine will generate a short (very short) delay for stretching
; the I2C signals...
DELAY: NOP
NOP
RET
;++
; This routine will generate an I2C start condition. It returns with
; C=1 if something goes wrond (e.g. an I/O pin is stuck low).
;--
START: SETB I2C_SDA ; be sure both SCL and SDA are high
SETB I2C_SCL ; ...
LCALL DELAY ; wait a moment for them to settle
; If either I/O pin is stuck low, then give up..
JNB I2C_SDA, STUCK ; ...
JNB I2C_SCL, STUCK ; ...
; An I2C start condition is signalled by SDA and then SCL going low. The
; order of these transitions is critical!
CLR I2C_SDA ; SDA goes low first
LCALL DELAY ; wait for it...
CLR I2C_SCL ; then SCL goes low next
CLR C ; return C=0 for success
LJMP DELAY ; let the signals settle and return
; Here if either the SCL and/or SDA pin is stuck low...
STUCK: SETB C ; return C=1 for failure
LJMP DELAY ; wait and then return
;++
; This routine will generate an I2C stop conditon. Unlike START, this one
; does not detect any error conditions. Note that a STOP condition starts
; with both SCL and SDA low, and then SCL goes high followed by SDA. Once
; again, the order is the thing!
;--
STOP: CLR I2C_SCL ; be sure both SCL and SDA are low
CLR I2C_SDA ; ...
LCALL DELAY ; wait for them to settle
SETB I2C_SCL ; then SCL goes high first
LCALL DELAY ; ...
SETB I2C_SDA ; followed by SDA
CLR C ; always return C=0 no matter what
LJMP DELAY ; wait for the signals to settle and exit
;++
; This routine will send an I2C ACK to the slave device...
;--
SACK: CLR I2C_SDA ; set SDA=0 for ACK
; JMP CLOCK ; and then clock it out like a data bit
;++
; This routine will clock out one data bit to or from the I2C slave.
; When sending a bit, the caller should put the desired data on SDA
; before calling us, and when reading a bit, this routine returns the
; state of SDA in C...
CLOCK: SETB I2C_SCL ; be sure the clock is high
JNB I2C_SCL, CLOCK ; and allow slow devices to stretch the clock
LCALL DELAY ; wait for the clock to settle
MOV C, I2C_SDA ; sample the data bit from the slave
CLR I2C_SCL ; then release the clock
SETB I2C_SDA ; release SDA in case we were transmitting
LJMP DELAY ; wait for these to settle and we're done
;++
; This routine will receive one complete byte (eight whole bits) from the
; slave device. The byte read is returned in the AC. After reading the
; byte, we send either an ACK or a NACK as determined by the state of the C
; bit (C=0 -> ACK, C=1->NACK)...
RXBYTE: MOV R0, #8 ; set bit counter
SETB I2C_SDA ; release SDA just in case
RLC A ; put the ACK/NACK bit in the AC for a while
RX1: LCALL CLOCK ; read a bit
RLC A ; store it in AC
DJNZ R0, RX1 ; read 8 of them
MOV I2C_SDA, C ; send an ACK or NACK
LJMP CLOCK ; ... and quit
;++
; This routine will transmit one byte from the AC to the slave device.
; It returns the ACK/NACK bit, which is sent back to us by the slave, in
; C (C=0 --> ACK!)...
TXBYTE: MOV R0, #8 ; set the bit counter
TX1: RLC A ; send the msb first
MOV I2C_SDA, C ; set the data bit
LCALL CLOCK ; and clock it out
DJNZ R0, TX1 ; do all eight bits
SETB I2C_SDA ; let the slave drive the bus next
LJMP CLOCK ; and read the ACK bit
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -