⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 i2c.asm

📁 这项工程将让您把自己的MP3播放器平台
💻 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 + -