Scenix Lib IO OSI2 I2C I2C_MASTER.SRC
; *****************************************************************************************
; Copyright © [01/26/1999] Scenix Semiconductor, Inc. All rights reserved.
;
; Scenix Semiconductor, Inc. assumes no responsibility or liability for
; the use of this [product, application, software, any of these products].
; Scenix Semiconductor conveys no license, implicitly or otherwise, under
; any intellectual property rights.
; Information contained in this publication regarding (e.g.: application,
; implementation) and the like is intended through suggestion only and may
; be superseded by updates. Scenix Semiconductor makes no representation
; or warranties with respect to the accuracy or use of these information,
; or infringement of patents arising from such use or otherwise.
;*****************************************************************************************
; SX Single I2C Master demo.
;
;
; Filename: i2cm_vp.src
;
; Authors: Chris Fogelklou and Bruce Wilson
; Applications Engineers
; Scenix Semiconductor Inc.
;
; Revision: 1.05a (preliminary, for review)
;
; Part: SX28AC datecode 9929AA
; Freq: 50Mhz
;
; Compiled using: SXKey28L v 1.09, SXKey52 v 1.19, SASM 1.44.6
;
; Date Written: Aug 23,1999
;
; Last Revised: May 16,2000
;
; Introduction:
; The I2C master Virtual Peripheral™ has been written to enable the user to simply and
; easily operate the SX as a master on an I2C-bus
;
; Program Description:
; This program is a very straight forward I2C master demo, which uses the I2C master
; VP as a foundation. Once SX 1 has been programmed with this code, it will begin continuously reading
; a stored string out of SX 2, the slave device. (The I2C slave SX2 has currently been assigned
; address 40h). If you wish to read data from a slave at different address simply change the i2cmSlaveAddress
; value in the constants section of the code.
; To run this demo on the Scenix I2C/UART demo board all you need to do is ensure that this code is
; programmed into the upper SX on the SX I2C/UART board (U1). If you run the program in debug mode you
; will see the data read from the I2C slave into the RAM bank 7. If you are not using the Scenix
; I2C/UART demo board then your setup should be as follows:
;
; 4.7k
; VCC ---/\/\/\--------x x------------ SDA EEPROM
; 4.7k | | (Address A0h -> AEh)
; VCC ---/\/\/\---x | | x------- SCL
; | | | |
; | | SCL | |
; MASTER RA0 ------------x-------------------x------- RB0 SLAVE
; SX 1 | SDA | SX 2
; RA1 -----------------x---------x------------ RB1 (Address 40h)
;
;
; However, the pin definitions on either SX can be easily changed.
; To run this demo ensure that the slave SX is programmed with I2CS.src and reset. By using a scope on
; the bus lines you will be able to see the transmission of data between the devices at 100kHz.
; Depending on the speed you wish to operate the I2C-bus at it may be necissary to change the pull-up resistor
; values. It is suggested that you refer to the I2C-bus specification pg. 40. This specificaiton is freely
; available at the Philips web site and has some good information on implementing the I2C specification.
;
; I2C Multi-Master VP requirements:
; - 1 RAM bank for I2C Master variables
; - 300 Bytes of Program memory
; - Up to 60 instructions every ISR
;
; Interface Pins:
;
; i2cmSclPin equ ra.0 ; I2C clock
; i2cmSdaPin equ ra.1 ; I2C data I/O
;
; Revision History:
; 1.0 Core I2C state machine implemented by Chris Fogelklou
; 1.01 Documentation and code revised and updated by Bruce Wilson
; 1.02 Improved multi-master functionality and tested on the Scenix I2C/UART board
; 1.03 Modified code for SASM assembler and SX52 compatibility
; 1.04 Tested code on the scenix EVAL and I2C/UART boards.
; Tested operation on both SX52 and SX28 with Parallax and SASM assemblers.
; 1.05a Rewritten according to VP guide 1.02 (AEH)
;
;*****************************************************************************************
;*****************************************************************************************
; Target SX
; Uncomment one of the following lines to choose the SX18AC, SX20AC, SX28AC, SX48BD,
; or SX52BD.
;*****************************************************************************************
;SX18_20
SX28
;SX48_52
;*****************************************************************************************
; Assembler Used
; Uncomment the following line if using the Parallax SX-Key assembler. SASM assembler
; enabled by default.
;*****************************************************************************************
;SX_Key ; Uncomment this line to assemble this source code using the
; Parallax Assembler
;*********************************************************************************
; Assembler directives:
; high speed external osc, turbo mode, 8-level stack, and extended option reg.
;
; SX18/20/28 - 4 pages of program memory and 8 banks of RAM enabled by default.
; SX48/52 - 8 pages of program memory and 16 banks of RAM enabled by default.
;
;*********************************************************************************
IFDEF SX_Key ;SX-Key Directives
watch i2cmRecvString,16,FSTR
IFDEF SX18_20 ;SX18AC or SX20AC device directives for SX-Key
device SX18L,oschs2,turbo,stackx_optionx
ENDIF
IFDEF SX28 ;SX28AC device directives for SX-Key
device SX28L,oschs2,turbo,stackx_optionx
ENDIF
IFDEF SX48_52 ;SX48/52/BD device directives for SX-Key
device oschs2
ENDIF
freq 50_000_000
ELSE ;SASM Directives
IFDEF SX18_20 ;SX18AC or SX20AC device directives for SASM
device SX18,oschs2,turbo,stackx,optionx
ENDIF
IFDEF SX28 ;SX28AC device directives for SASM
device SX28,oschs2,turbo,stackx,optionx
ENDIF
IFDEF SX48_52 ;SX48BD or SX52BD device directives for SASM
device SX52,oschs2
ENDIF
ENDIF
id 'I2CM' ;
reset resetEntry ; set reset vector
;*****************************************************************************************
; Macros
;*****************************************************************************************
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
;
; To support compatibility between source code written for the SX28 and the SX52,
; use macros.
;
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;*********************************************************************************
; Macro: _bank
; Sets the bank appropriately for all revisions of SX.
;
; This is required since the bank instruction has only a 3-bit operand, it cannot
; be used to access all 16 banks of the SX48/52. For this reason FSR.7 needs to be
; set appropriately, depending on the bank address being accessed. Use of this macro
; switches banks correctly, regardless of the part being compiled for.
;
; Instead of using the bank instruction to switch between banks, use _bank instead.
;
;*********************************************************************************
_bank macro 1
bank \1
IFDEF SX48_52
IF \1 & %10000000 ;SX48BD and SX52BD (production release) bank instruction
setb fsr.7 ;modifies FSR bits 4,5 and 6. FSR.7 needs to be set by software.
ELSE
clrb fsr.7
ENDIF
ENDIF
endm
;*****************************************************************************************
; Macros for SX28/52 Compatibility
;*****************************************************************************************
;*********************************************************************************
; Macro: _mode
; Sets the MODE register appropriately for all revisions of SX.
;
; This is required since the MODE (or MOV M,#) instruction has only a 4-bit operand.
; The SX18/20/28AC use only 4 bits of the MODE register, however the SX48/52BD have
; the added ability of reading or writing some of the MODE registers, and therefore use
; 5-bits of the MODE register. The MOV M,W instruction modifies all 8-bits of the
; MODE register, so this instruction must be used on the SX48/52BD to make sure the MODE
; register is written with the correct value. This macro fixes this.
;
; So, instead of using the MODE or MOV M,# instructions to load the M register, use
; _mode instead.
;
;*********************************************************************************
_mode macro 1
IFDEF SX48_52
expand
mov w,#\1 ;loads the M register correctly for the SX48BD and SX52BD
mov m,w
noexpand
ELSE
expand
mov m,#\1 ;loads the M register correctly for the SX18AC, SX20AC
noexpand ;and SX28AC
ENDIF
endm
;*****************************************************************************************
; INCP/DECP macros for incrementing/decrementing pointers to RAM
; used to compensate for incompatibilities between SX28 and SX52
;*****************************************************************************************
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
;
; To support compatibility between source code written for the SX28 and the SX52,
; use macros. This macro compensates for the fact that RAM banks are contiguous in
; the SX52, but separated by 0x20 in the SX18/28.
;
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
INCP macro 1
inc \1
IFNDEF SX48_52
setb \1.4 ; If SX18 or SX28, keep bit 4 of the pointer = 1
ENDIF ; to jump from $1f to $30, etc.
endm
DECP macro 1
IFDEF SX48_52
dec \1
ELSE
clrb \1.4 ; If SX18 or SX28, forces rollover to next bank
dec \1 ; if it rolls over. (Skips banks with bit 4 = 0)
setb \1.4 ; Eg: $30 --> $20 --> $1f --> $1f
ENDIF ; AND: $31 --> $21 --> $20 --> $30
endm
;*****************************************************************************************
; Error generating macros
; Used to generate an error message if the label is unintentionally moved into the
; second half of a page. Use for lookup tables.
;*****************************************************************************************
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
;
; Surround lookup tables with the tableStart and tableEnd macros. An error will
; be generated on assembly if the table crosses a page boundary.
;
; Example:
; lookupTable1
; add pc,w
; tableStart
; retw 0
; retw 20
; retw -20
; retw -40
; tableEnd
;
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
tableStart macro 0 ; Generates an error message if code that MUST be in
; the first half of a page is moved into the second half.
if $ & $100
ERROR 'Must be located in the first half of a page.'
endif
endm
tableEnd macro 0 ; Generates an error message if code that MUST be in
; the first half of a page is moved into the second half.
if $ & $100
ERROR 'Must be located in the first half of a page.'
endif
endm
;*****************************************************************************************
; Data Memory address definitions
; These definitions ensure the proper address is used for banks 0 - 7 for 2K SX devices
; (SX18/20/28) and 4K SX devices (SX48/52).
;*****************************************************************************************
IFDEF SX48_52
global_org = $0A
bank0_org = $00
bank1_org = $10
bank2_org = $20
bank3_org = $30
bank4_org = $40
bank5_org = $50
bank6_org = $60
bank7_org = $70
ELSE
global_org = $08
bank0_org = $10
bank1_org = $30
bank2_org = $50
bank3_org = $70
bank4_org = $90
bank5_org = $B0
bank6_org = $D0
bank7_org = $F0
ENDIF
;*****************************************************************************************
; Global Register definitions
; NOTE: Global data memory starts at $0A on SX48/52 and $08 on SX18/20/28.
;*****************************************************************************************
org global_org
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
;
; Use only these defined label types for global registers. If an extra temporary
; register is required, adhere to these label types. For instance, if two temporary
; registers are required for the Interrupt Service Routine, use the label isrTemp1
; for it.
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
flags0 equ global_org + 0 ; stores bit-wise operators like flags
; and function-enabling bits (semaphores)
flags1 equ global_org + 1 ; stores bit-wise operators like flags
; and function-enabling bits (semaphores)
localTemp0 equ global_org + 2 ; temporary storage register
; Used by first level of nesting
; Never guaranteed to maintain data
;VP_begin I2C Master
i2cRecvChar equ localTemp0 ; receive character
;VP_end
localTemp1 equ global_org + 3 ; temporary storage register
; Used by second level of nesting
; or when a routine needs more than one
; temporary global register.
localTemp2 equ global_org + 4 ; temporary storage register
; Used by third level of nesting or by
; main loop routines that need a loop
; counter, etc.
isrTemp0 equ global_org + 5 ; Interrupt Service Routine's temp register.
; Don't use this register in the mainline.
;*****************************************************************************************
; RAM Bank Register definitions
;*****************************************************************************************
;*********************************************************************************
; Bank 0
;*********************************************************************************
org bank0_org
bank0 = $
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; - Avoid using bank0 in programs written for SX48/52.
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;*********************************************************************************
; Bank 1
;*********************************************************************************
org bank1_org
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
;
; Tip 1:
; Indicate which Virtual Peripherals a portion of source code or declaration belongs
; to with a
; ";VP: VirtualPeripheralName"
; comment.
;
; Tip 2:
; All RAM location declaration names should be
; - left justified
; - less than 2 tabs in length
; - written in hungarian notation
; - prefixed by a truncated version of the Virtual Peripheral's name
;
; Examples:
;
; ;VP: RS232 Transmit
;
; rs232TxBank = $ ;RS232 Transmit bank
;
; rs232TxHigh ds 1 ;hi byte to transmit
; rs232TxLow ds 1 ;low byte to transmit
; rs232TxCount ds 1 ;number of bits sent
; rs232TxDivide ds 1 ;xmit timing (/16) counter
; rs232TxString ds 1 ;the address of the string to be sent
; rs232TxByte ds 1 ;semi-temporary serial register
;
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;VP: ISR Multithreader
isrMultiplex ds 1 ; The isrMultiplex register is used to switch to a new
; execution thread on each pass of the ISR.
;VP_begin I2C Master
i2cmBank = $ ; I2CM bank
i2cmState ds 1 ; This indicates the state that the I2CM master is currently in.
i2cmSubState ds 1 ; This indicates the substate that the I2C master is currently in.
i2cmPortBuf ds 1 ; This buffer holds the current state of the I2C port direction reg's
i2cmBitCount ds 1 ; Indicates the number of bits left to process in read/write
i2cmByte ds 1 ; The byte currently being written/read by the I2C master
i2cmFlags ds 1
i2cmNack equ i2cmFlags.0 ; This bit is set if the I2C master has received a NACK from the slave
i2cmRxFlag equ i2cmFlags.1 ; Indicates that the number of bytes requested have been received
i2cmIndex ds 1 ; The index into the I2CM buffer, used for writing
i2cmNumBytes ds 1 ; The index into the I2CM buffer, used for reading
i2cmBuffer = $ ; The buffer uses the last 7 registers of this bank (pre-increments, so put I2CM buffer here.)
i2cmAddress ds 1 ; The address to read/write to.
i2cmDataBuf ds 6 ; Data buffer, 6 bytes big
;VP_end
; These data registers could easily be placed into another bank if a more are required
;*********************************************************************************
; Bank 2
;*********************************************************************************
org bank2_org
bank2 = $
i2cmTimer = $
i2cmTimerLow ds 1 ; timer_low
i2cmTimerHigh ds 1 ; timer_high
i2cmVars = $
i2cmWritePtr ds 1 ; points to where data can be written
i2cmReadAdr ds 1 ; used to point to address of slave to read
;*********************************************************************************
; Bank 3
;*********************************************************************************
org bank3_org
bank3 = $
;*********************************************************************************
; Bank 4
;*********************************************************************************
org bank4_org
bank4 = $
;*********************************************************************************
; Bank 5
;*********************************************************************************
org bank5_org
bank5 = $
;*********************************************************************************
; Bank 6
;*********************************************************************************
org bank6_org
bank6 = $
;*********************************************************************************
; Bank 7
;*********************************************************************************
org bank7_org
bank7 = $
i2cmRecvString = $ ; where a string can be stored
IFDEF SX48_52
;*********************************************************************************
; Bank 8
;*********************************************************************************
org $80 ;bank 8 address on SX52
bank8 = $
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; - This extra memory is not available in the SX18/28, so don't use it for Virtual
; Peripherals written for both platforms.
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;*********************************************************************************
; Bank 9
;*********************************************************************************
org $90 ;bank 9 address on SX52
bank9 = $
;*********************************************************************************
; Bank A
;*********************************************************************************
org $A0 ;bank A address on SX52
bankA = $
;*********************************************************************************
; Bank B
;*********************************************************************************
org $B0 ;bank B address on SX52
bankB = $
;*********************************************************************************
; Bank C
;*********************************************************************************
org $C0 ;bank C address on SX52
bankC = $
;*********************************************************************************
; Bank D
;*********************************************************************************
org $D0 ;bank D address on SX52
bankD = $
;*********************************************************************************
; Bank E
;*********************************************************************************
org $E0 ;bank E address on SX52
bankE = $
;*********************************************************************************
; Bank F
;*********************************************************************************
org $F0 ;bank F address on SX52
bankF = $
ENDIF
;*********************************************************************************
; Pin Definitions:
;*********************************************************************************
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; - Store all initialization constants for the I/O in the same area, so
; pins can be easily moved around.
; - Pin definitions should follow the same format guidelines as RAM definitions
; - Left justified
; - Hungarian Notation
; - Less that 2 tabs in length
; - Indicate the Virtual Peripheral the pin is used for
; - Only use symbolic names to access a pin/port in the source code.
; - Example:
; ; VP: RS232 Transmit
; rs232TxPin equ ra.3
;
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
RA_latch equ %00001000 ;SX18/20/28/48/52 port A latch init
RA_DDIR equ %11110111 ;SX18/20/28/48/52 port A DDIR value
RA_LVL equ %00000000 ;SX18/20/28/48/52 port A LVL value
RA_PLP equ %11111111 ;SX18/20/28/48/52 port A PLP value
;VP_begin I2C Master
i2cmPort equ ra
i2cmScl equ 1
i2cmSda equ 0
i2cmSclPin equ i2cmPort.i2cmScl ; I2C clock
i2cmSdaPin equ i2cmPort.i2cmSda ; I2C data I/O
;VP_end
RB_latch equ %11111111 ;SX18/20/28/48/52 port B latch init
RB_DDIR equ %11111111 ;SX18/20/28/48/52 port B DDIR value
RB_ST equ %11111111 ;SX18/20/28/48/52 port B ST value
RB_LVL equ %00000000 ;SX18/20/28/48/52 port B LVL value
RB_PLP equ %11111111 ;SX18/20/28/48/52 port B PLP value
RC_latch equ %11111111 ;SX18/20/28/48/52 port C latch init
RC_DDIR equ %11111111 ;SX18/20/28/48/52 port C DDIR value
RC_ST equ %11111111 ;SX18/20/28/48/52 port C ST value
RC_LVL equ %00000000 ;SX18/20/28/48/52 port C LVL value
RC_PLP equ %11111111 ;SX18/20/28/48/52 port C PLP value
IFDEF SX48_52 ;SX48BD/52BD Port initialization values
RD_latch equ %00000000 ;SX48/52 port D latch init
RD_DDIR equ %11111111 ;SX48/52 port D DDIR value
RD_ST equ %11111111 ;SX48/52 port D ST value
RD_LVL equ %00000000 ;SX48/52 port D LVL value
RD_PLP equ %11111111 ;SX48/52 port D PLP value
RE_latch equ %00000000 ;SX48/52 port E latch init
RE_DDIR equ %01001111 ;SX48/52 port E DDIR value
RE_ST equ %11111111 ;SX48/52 port E ST value
RE_LVL equ %00000000 ;SX48/52 port E LVL value
RE_PLP equ %11111111 ;SX48/52 port E PLP value
ENDIF
;*****************************************************************************************
; Program constants
;*****************************************************************************************
;-------------------------------------------------------------------------------------
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; To calculate the interrupt period in cycles:
; - First, choose the desired interrupt frequency
; - Should be a multiple of each Virtual Peripherals sampling frequency.
; - Example: 19200kHz UART sampling rate * 16 = 307.200kHz
; - Next, choose the desired oscillator frequency.
; - 50MHz, for example.
; - Perform the calculation int_period = (osc. frequency / interrupt frequency)
; = (50MHz / 307.2kHz)
; = 162.7604
; - Round int_period to the nearest integer:
; = 163
; - Now calculate your actual interrupt rate:
; = osc. frequency / int_period
; = 50MHz / 163
; = 306.748kHz
; - This interrupt frequency will be the timebase for all of the Virtual
; Peripherals
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;int_period = 166 ; This sets the I2C bus frequency.
int_period = 83 ; This sets the I2C bus frequency.
; int_period = [clk_speed/(3 x bus_speed)] x threadRate
; eg. for 100kHz operation:
; int_period = [50Mhz/(3 x 100kHz)] x 1/2 = 83
; The threadRate is the rate the thread is running at
; compared to the ISR rate.
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; - Include all calculations for Virtual Peripheral constants for any sample
; rate.
; - Relate all Virtual Peripheral constants to the sample rate of the Virtual
; Peripheral.
; - Example:
; ; VP: 5ms Timer
; TIMER_DIV_CONST equ 192 ; This constant = timer sample rate/200Hz = 192
;
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;VP_begin I2C Master
i2cmSlaveAddress equ $a0 ; Address of the slave device that this master will
;VP_end ; read data from. 40h = SX Slave, A0h = EEPROM
;-------------------------------------------------------------------------------------
IFDEF SX48_52
;*********************************************************************************
; SX48BD/52BD Mode addresses
; *On SX48BD/52BD, most registers addressed via mode are read and write, with the
; exception of CMP and WKPND which do an exchange with W.
;*********************************************************************************
; Timer (read) addresses
TCPL_R equ $00 ;Read Timer Capture register low byte
TCPH_R equ $01 ;Read Timer Capture register high byte
TR2CML_R equ $02 ;Read Timer R2 low byte
TR2CMH_R equ $03 ;Read Timer R2 high byte
TR1CML_R equ $04 ;Read Timer R1 low byte
TR1CMH_R equ $05 ;Read Timer R1 high byte
TCNTB_R equ $06 ;Read Timer control register B
TCNTA_R equ $07 ;Read Timer control register A
; Exchange addresses
CMP equ $08 ;Exchange Comparator enable/status register with W
WKPND equ $09 ;Exchange MIWU/RB Interrupts pending with W
; Port setup (read) addresses
WKED_R equ $0A ;Read MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_R equ $0B ;Read MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_R equ $0C ;Read Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_R equ $0D ;Read Port Level setup, 0 = CMOS, 1 = TTL
PLP_R equ $0E ;Read Port Weak Pullup setup, 0 = enabled, 1 = disabled
DDIR_R equ $0F ;Read Port Direction
; Timer (write) addresses
TR2CML_W equ $12 ;Write Timer R2 low byte
TR2CMH_W equ $13 ;Write Timer R2 high byte
TR1CML_W equ $14 ;Write Timer R1 low byte
TR1CMH_W equ $15 ;Write Timer R1 high byte
TCNTB_W equ $16 ;Write Timer control register B
TCNTA_W equ $17 ;Write Timer control register A
; Port setup (write) addresses
WKED_W equ $1A ;Write MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_W equ $1B ;Write MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_W equ $1C ;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_W equ $1D ;Write Port Level setup, 0 = CMOS, 1 = TTL
PLP_W equ $1E ;Write Port Weak Pullup setup, 0 = enabled, 1 = disabled
DDIR_W equ $1F ;Write Port Direction
ELSE
;*********************************************************************************
; SX18AC/20AC/28AC Mode addresses
; *On SX18/20/28, all registers addressed via mode are write only, with the exception of
; CMP and WKPND which do an exchange with W.
;*********************************************************************************
; Exchange addresses
CMP equ $08 ;Exchange Comparator enable/status register with W
WKPND equ $09 ;Exchange MIWU/RB Interrupts pending with W
; Port setup (read) addresses
WKED_W equ $0A ;Write MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_W equ $0B ;Write MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_W equ $0C ;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_W equ $0D ;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
PLP_W equ $0E ;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
DDIR_W equ $0F ;Write Port Direction
ENDIF
;*****************************************************************************************
; Program memory ORG defines
;*****************************************************************************************
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; - Place a table at the top of the source with the starting addresses of all of
; the components of the program.
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
INTERRUPT_ORG equ $0 ; Interrupt must always start at location zero
RESET_ENTRY_ORG equ $1FB ; The program will jump here on reset.
SUBROUTINES_ORG equ $200 ; The subroutines are in this location
STRINGS_ORG equ $300 ; The strings are in location $300
PAGE3_ORG equ $400 ; Page 3 is empty
MAIN_PROGRAM_ORG equ $600 ; The main program is in the last page of program memory.
;****************************** Beginning of program space *******************************
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************
org INTERRUPT_ORG ; First location in program memory.
;*****************************************************************************************
;------------------------------------------------------------------------------
; Interrupt Service Routine
;------------------------------------------------------------------------------
; Note: The interrupt code must always originate at address $0.
;
; Interrupt Frequency = (Cycle Frequency / -(retiw value)) For example:
; With a retiw value of -166 and an oscillator frequency of 50MHz, this
; code runs every 3.32us.
;*****************************************************************************************
ISR ;3 The interrupt service routine...
;------------------------------------------------------------------------------
;VP: VP Multitasker
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; - Multi-thread the Interrupt Service Routine
; - Produces a FAR smaller worst-case cycle time count, and enables a larger number
; of VP's to run simultaneously. Also produces "empty" slots that future VP's
; can be copied and pasted into easily.
; - Determine how often your tasks need to run. (9600bps UART can run well at a
; sampling rate of only 38400Hz, so don't run it faster than this.)
; - Strategically place each "module" into the threads of the ISR. If a module
; must be run more often, just call it's module at double the rate or quadruple
; the rate, etc.…
; - Split complicated Virtual Peripherals into several modules, keeping the
; high-speed portions of the Virtual Peripherals as small and quick as possible,
; and run the more complicated, slower processing part of the Virtual Peripheral
; at a lower rate.
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;------------------------------------------------------------------------------
; Virtual Peripheral Multitasker: up to 24 individual threads, each running at
; the interrupt rate/24. Change the number of
; table entries for each thread to change the
; sample rate of that thread.
;
; Input variable(s): isr_multiplex: variable used to choose threads
; Output variable(s): None, executes the next thread
; Variable(s) affected: isr_multiplex
; Flag(s) affected: None
; Program Cycles: 9 cycles (turbo mode SX28)
;------------------------------------------------------------------------------
_bank isrMultiplex ;1 (or 2 in SX52)
inc isrMultiplex ;1 ; toggle interrupt rates
mov w,isrMultiplex ;1
; The code between the tableBegin and tableEnd statements MUST be
; completely within the first half of a page. The routines
; it is jumping to must be in the same page as this table.
tableStart ; Start all tables with this macro.
add pc,w ;3
jmp isrThread1 ;3,9 cycles.
jmp isrThread2 ;
jmp isrThread1 ;
jmp isrThread4 ;
jmp isrThread1 ;
jmp isrThread5 ;
jmp isrThread1 ;
jmp isrThread7 ;
jmp isrThread1 ;
jmp isrThread2 ;
jmp isrThread1 ;
jmp isrThread9 ;
jmp isrThread1 ;
jmp isrThread10 ;
jmp isrThread1 ;
jmp isrThread11 ;
jmp isrThread1 ;
jmp isrThread2 ;
jmp isrThread1 ;
jmp isrThread13 ;
jmp isrThread1 ;
jmp isrThread14 ;
jmp isrThread1 ;
jmp isrThread16 ;
tableEnd ; End all tables with this macro.
;------------------------------------------------------------------------------
;VP: VP Multitasker
; ISR TASKS
;------------------------------------------------------------------------------
isrThread1 ; Serviced at ISR rate / 2
;------------------------------------------------------------------------------
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; The sample rate of this section of code is the isr rate / 4, because it is jumped
; to in every 4th entry in the VP Multitaskers table. To increase the
; sample rate, put more calls to this thread in the Multitasker's jump table.
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;VP_begin I2C Master
;*********************************************************************************
; Keep running 16bit timer...
;*********************************************************************************
_bank i2cmTimer ;1
inc i2cmTimerLow ;1
snb z ;1
inc i2cmTimerHigh ;1
;=13
;*********************************************************************************
; I2C Master Virtual Peripheral
;*********************************************************************************
_bank i2cmBank ;1
mov w,#i2cmBuffer ;1 ; Switch to I2CM bank and load FSR for
mov fsr,w ;1 ; buffer look-up, if needed.
call @i2cmISR ;3 + 12/25
;=31/44
;*********************************************************************************
; Update I2C port with buffered port data
;*********************************************************************************
mov w,m ;1 ; Save the m register.
mov isrTemp0,w ;1
_mode DDIR_W ;1/2
mov w,i2cmPort ;1
and w,#%11111100 ;1 ; Clear the data latches for SCL and SDA
mov i2cmPort,w ;1
_bank i2cmBank ;1 ; Select Master I2C bank
mov w,i2cmPortBuf ;1
mov !i2cmPort,w ;1 ; Update the I2C port with the buffered port data
mov w,isrTemp0
mov m,w
;=12/13
jmp isrOut ;7 cycles until mainline program resumes execution
;=31/44 + 12/13 + 7 = 50/64
;VP_end
;------------------------------------------------------------------------------
isrThread2 ; Serviced at ISR rate / 8
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread3 ; Never serviced
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread4 ; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread5 ; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread6 ; Never serviced
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread7 ; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread8 ; Never serviced
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread9 ; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread10 ; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread11 ; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread12 ; Never Serviced
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread13 ; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread14 ; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread15 ; Never Serviced
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread16 ; Serviced at ISR rate / 24
; ; This thread must reload the isrMultiplex register
; since it is the last one to run in a rotation.
;------------------------------------------------------------------------------
_bank isrMultiplex
mov w,#255 ; Reload isrMultiplex so isrThread1 will be run
mov isrMultiplex,w ; on next interrupt.
jmp isrOut
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
isrOut
;------------------------------------------------------------------------------
mov w,#-int_period ;1 ; return and add -int_period to the RTCC
retiw ;3 ; using the retiw instruction.
;------------------------------------------------------------------------------
;*****************************************************************************************
org RESET_ENTRY_ORG
;*****************************************************************************************
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; The main program operation should be easy to find, so place it at the end of the
; program code. This means that if the first page is used for anything other than
; main program source code, a reset_entry must be placed in the first page, along
; with a 'page' instruction and a 'jump' instruction to the beginning of the
; main program.
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;------------------------------------------------------------------------------
resetEntry ; Program starts here on power-up
page _resetEntry
jmp _resetEntry
;------------------------------------------------------------------------------
;*****************************************************************************************
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; ORG statements should use predefined labels rather than literal values.
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
org SUBROUTINES_ORG
;*****************************************************************************************
; Subroutines
;*****************************************************************************************
;*****************************************************************************************
; I2C Master Interrupt Service Routines
;*****************************************************************************************
;*********************************************************************************
;VP_begin I2C Master
;
; Function: i2cmISR
;
; I2C Master Interrupt-Driven State Machine
; -------------------------------------------
; This is the I2C Master Interrupt Service Routine. It is an interrupt-
; driven state machine which allows all of the actions of the I2C Master
; controller to be carried out, virtual peripheral style, with virtually
; no interaction from the mainline program.
; i2cmIdle
; This is the state that the I2C Master is usually in when it is not in use.
; It just ensures that the i2cmPortBuf SCL and SDA are both set high
; i2cmStart
; When any mainline program wants to use the I2C master it puts the master into
; start mode. This mode creates a start condition on the I2C bus. A start
; condition is created when SDA goes from high to low while SCL stays high.
; i2cmStartWrite
; This state performs some pre-processing which allows the i2cmWrite state to
; do its work. It sets up the bit count, gets the next piece of data from the
; buffer and prepares to send it.
; i2cmWrite
; This state writes the data in i2cmByte to the I2C bus
; i2cmGetAck
; This state gets an ACK from the slave device. If no ACK is received, the I2C
; Master state machine puts a stop condition on the bus and the i2cmFlags
; register is loaded to indicate that a NACK has occurred.
; i2cmWriteRepeat
; This state determines, after one byte of data is sent, whether or not
; there is another byte to be sent. If so, this state goes back to i2cmStartWrite
; and sends the next byte.
; i2cmStop
; This state puts a stop condition on the I2C bus and resets the state machine back
; to its idle state. A stop condition is when SDA goes from low to high while SCL
; is high.
; i2cmStartRead
; This state simply loads the contents of the i2cmAddress register into the i2cmByte
; register and sets up the i2cmWrite state to output the address of the slave to read.
; i2cmReadData
; This state prepares the I2CM read routine so it can read from the slave device. It
; initializes the bit count, etc.
; i2cmRead
; This state read 8 bits of data from the slave device.
; i2cmStoreByte
; This state stores the byte just read into the buffer.
; i2cmSendAck
; This state sends an ack if there is data left to write, and a NACK if there is
; no data left to write.
;*********************************************************************************
i2cmISR
mov w,i2cmState ;1
add PC,w ;3 ;Add the state to the program counter
;and go to the state in the jump table.
;*************************************************************************
; States for I2C Master while idle
;*************************************************************************
i2cmIdleLoc = $
jmp i2cmIdle ;3 ;If I2C_state = 0, I2C is idle
;*************************************************************************
; States for I2C Master while writing
;*************************************************************************
i2cmWriteLoc = $
jmp i2cmStart ;3 ;Make SDA go low while SCL is high
jmp i2cmStartWrite ;3 ;Load a byte from the buffer and prepare to send.
jmp i2cmWrite ;3 ;Write it.
jmp i2cmGetAck ;3 ;Get an ACK signal
jmp i2cmWriteRepeat ;3 ;Check to see if we have finished sending
;(if buffer read index = buffer write index)
i2cmStopLoc = $
jmp i2cmStop ;3 ;If write_repeat determines we are finished,
;then send stop
;*************************************************************************
; States for I2C Master while reading
;*************************************************************************
i2cmReadLoc = $ ;Load this state into state machine if we
;are starting to read
jmp i2cmStart ;3 ;
jmp i2cmStartRead ;3 ;Write address used for reading
jmp i2cmWrite ;3 ;(writing address)
jmp i2cmGetAck ;3 ;
i2cmReadRptLoc = $
jmp i2cmReadData ;3 ;
jmp i2cmRead ;3 ;Keep doing this until done
jmp i2cmStoreByte ;3 ;
jmp i2cmSendAck ;3 ;
jmp i2cmStop ;3 ;
;*********************************************************************************
; State: i2cmIdle
; This is the state that the I2C Master is usually in when it is not in use.
; It just ensures that the i2cmPortBuf SCL and SDA are both set high
;*********************************************************************************
i2cmIdle setb i2cmPortBuf.i2cmSda ;1
setb i2cmPortBuf.i2cmScl ;1
retp ;3 = 5 + 7 = 12
;*********************************************************************************
; State: i2cmStart
; When any mainline program wants to use the I2C master it puts the master into
; start mode. This mode creates a start condition on the I2C bus. A start
; condition is created when SDA goes from high to low while SCL stays high.
;*********************************************************************************
i2cmStart mov w,i2cmSubState ;1
add pc,w ;3
jmp :state1 ;3
jmp :state2 ;3
jmp :state3 ;3
jmp :state4 ;3
:state1 setb i2cmPortBuf.i2cmScl ;1
setb i2cmPortBuf.i2cmSda ;1
inc i2cmSubState ;1
retp ;3 = 13 + 7 = 20
:state2 clrb i2cmPortBuf.i2cmSda ;1
inc i2cmSubState ;1
retp ;3 = 12 + 7 = 19
:state3 clrb i2cmPortBuf.i2cmScl ;1
inc i2cmSubState ;1
retp ;3 = 12 + 7 = 19
:state4 setb i2cmPortBuf.i2cmSda ;1
clr i2cmSubState ;1
inc i2cmState ;1
retp ;3 = 13 + 7 = 20
;*********************************************************************************
; State: i2cmStartWrite
; This state performs some pre-processing which allows the i2cmWrite state to
; do its work. It sets up the bit count, gets the next piece of data from the
; buffer and prepares to send it.
;*********************************************************************************
i2cmStartWrite
inc i2cmIndex ;1
mov w,i2cmIndex ;1
add fsr,w ;1
mov w,indf ;1
mov i2cmByte,w ;1
mov w,#8 ;1
mov i2cmBitCount,w ;1
inc i2cmState ;1
retp ;3 = 11 + 7 = 18
;*********************************************************************************
; State: i2cmWrite
; This state writes the data in i2cmByte to the I2C bus
;*********************************************************************************
i2cmWrite mov w,i2cmSubState ;1
add pc,w ;3
jmp :state1 ;3
jmp :state2 ;3
jmp :state3 ;3
:state1 setb i2cmPortBuf.i2cmSda ;1
rl i2cmByte ;1
sb c ;1
clrb i2cmPortBuf.i2cmSda ;1
inc i2cmSubState ;1
retp ;3 = 15 + 7 = 22
:state2 setb i2cmPortBuf.i2cmScl ;1
inc i2cmSubState ;1
retp ;3 = 12 + 7 = 19
:state3 clrb i2cmPortBuf.i2cmScl ;1
dec i2cmBitCount ;1
snb z ;1
jmp :done ;3
clr i2cmSubState ;1
retp ;3 = 15 + 7 = 22
:done clr i2cmSubState ;1
inc i2cmState ;1
retp ;3 = 18 + 7 = 25
;*********************************************************************************
; State: i2cmGetAck
; This state gets an ACK from the slave device. If no ACK is received, the I2C
; Master state machine puts a stop condition on the bus and the i2cmFlags
; register is loaded to indicate that a NACK has occurred.
;*********************************************************************************
i2cmGetAck mov w,i2cmSubState ;1
add pc,w ;3
jmp :state1 ;3
jmp :state2 ;3
jmp :state3 ;3
jmp :state4 ;3
:state1 setb i2cmPortBuf.i2cmSda ;1
inc i2cmSubState ;1
retp ;3 = 12 + 7 = 19
:state2 setb i2cmPortBuf.i2cmScl ;1
inc i2cmSubState ;1
retp ;3 = 12 + 7 = 19
:state3 sb i2cmSdaPin ;1
inc i2cmSubState ;1
sb i2cmSdaPin ;1
retp ;3 = 13 + 7 = 20 ;if no ack,
mov w,#(i2cmStopLoc-i2cmIdleLoc);1
mov i2cmState,w ;1
clr i2cmSubState ;1 ;send a stop and indicate that this didn't work.
setb i2cmNack ;1 ;set i2cmNack to show that this did not go through.
retp ;3 = 17 + 7 = 24
:state4 clrb i2cmPortBuf.i2cmScl ;1
clr i2cmSubState ;1
inc i2cmState ;1 ;move on to next state
retp ;3 = 13 + 7 = 20
;*********************************************************************************
; State: i2cmWriteRepeat
; This state determines, after one byte of data is sent, whether or not
; there is another byte to be sent. If so, this state goes back to
; i2cmStartWrite and sends the next byte.
;*********************************************************************************
i2cmWriteRepeat
mov w,i2cmNumBytes ;1 ;test the read index to see if it is = to write index.
xor w,i2cmIndex ;1 ;if it is, then we have finished writing the buffer via. I2C.
snb z ;1
jmp :i2cmWriteDone ;3
dec i2cmState ;1 ;back to get_ack
dec i2cmState ;1 ;back to write
dec i2cmState ;1 ;back to write_data
retp ;3 = 10 + 7 = 17
:i2cmWriteDone
inc i2cmState ;1 ;move on to next state (stop)
retp ;3 = 10 + 7 = 17 ;and start the stop bit
;*********************************************************************************
; State: i2cmStop
; This state puts a stop condition on the I2C bus and resets the state machine back
; to its idle state. A stop condition is when SDA goes from low to high while SCL
; is high.
;*********************************************************************************
i2cmStop mov w,i2cmSubState ;1
add pc,w ;3
jmp :state1 ;3
jmp :state2 ;3
jmp :state3 ;3
:state1 clrb i2cmPortBuf.i2cmSda ;1
inc i2cmSubState ;1
retp ;3 = 12 + 7 = 19
:state2 setb i2cmPortBuf.i2cmScl ;1
inc i2cmSubState ;1
retp ;3 = 12 + 7 = 19
:state3 setb i2cmPortBuf.i2cmSda ;1
clr i2cmSubState ;1 ;put the state machine in Idle
clr i2cmState ;1
clr i2cmNumBytes ;1
mov w,#$ff ;1
mov i2cmIndex,w ;1
retp ;3 = 16 + 7 = 23
;*****************************************************************************************
; End of I2C Master state machine
;*****************************************************************************************
;*****************************************************************************************
; Jump table
;*****************************************************************************************
i2cmSendByte jmp i2cmSendByte_ ;3
i2cmSendBytes jmp i2cmSendBytes_ ;3
i2cmGetByte jmp i2cmGetByte_ ;3
i2cmGetBytes jmp i2cmGetBytes_ ;3
i2cmWaitNotBusy jmp i2cmWaitNotBusy_ ;3
i2cmInit jmp i2cmInit_ ;3
;*****************************************************************************************
; I2C Master Subroutines
;*****************************************************************************************
;*********************************************************************************
; VP: I2C Master
;
; Function: i2cmStartRead
;
; This state simply loads the contents of the i2cmAddress register into the
; i2cmByte register and sets up the i2cmWrite state to output the address
; of the slave to read.
;*********************************************************************************
i2cmStartRead
mov w,i2cmAddress ; // write how many cycles each instruction take
mov i2cmByte,w
inc i2cmIndex ;
mov w,#8
mov i2cmBitCount,w
inc i2cmState
retp
;*********************************************************************************
; VP: I2C Master
;
; Function: i2cmReadData
;
; This function prepares the I2CM read routine so it can read from the slave
; device. It initializes the bit count, etc.
;*********************************************************************************
i2cmReadData
mov w,#8
mov i2cmBitCount,w
inc i2cmState
;*************************************************************************
; Read 8 bits of data from the slave device.
;*************************************************************************
i2cmRead mov w,i2cmSubState
add pc,w
jmp :state1
jmp :state2
jmp :state3
:state1 setb i2cmPortBuf.i2cmScl
inc i2cmSubState
retp
:state2 sb i2cmSclPin
retp
setb c
sb i2cmSdaPin
clrb c
rl i2cmByte
inc i2cmSubState
retp
:state3 clrb i2cmPortBuf.i2cmScl
clr i2cmSubState
dec i2cmBitCount
snb z
inc i2cmState
retp
;*********************************************************************************
; VP: I2C Master
;
; Function: i2cmStoreByte
;
; This function stores the byte just read into the buffer.
;*********************************************************************************
i2cmStoreByte
mov w,i2cmByte
mov isrTemp0,w
inc i2cmIndex ;
mov w,i2cmIndex
add fsr,w
mov w,isrTemp0
mov indf,w
inc i2cmState
setb i2cmRxFlag
retp
;*********************************************************************************
; VP: I2C Master
;
; Function: i2cmSendAck
;
; This function sends an ack if there is data left to write, and a NACK if
; there is no data left to write.
;*********************************************************************************
i2cmSendAck
mov w,i2cmSubState
add pc,w
jmp :state1
jmp :state2
jmp :state3
jmp :state4
:state1 clrb i2cmPortBuf.i2cmSda
mov w,i2cmIndex
xor w,i2cmNumBytes
snb z
setb i2cmPortBuf.i2cmSda
inc i2cmSubState
retp
:state2 setb i2cmPortBuf.i2cmScl ;Pulse the clock
inc i2cmSubState
retp
:state3 sb i2cmSclPin
retp
clrb i2cmPortBuf.i2cmScl
inc i2cmSubState
retp
:state4 setb i2cmPortBuf.i2cmSda
clr i2cmSubState
mov w,i2cmIndex
xor w,i2cmNumBytes
snb z
jmp :done
mov w,#(i2cmReadRptLoc - i2cmIdleLoc) ;back to read
mov i2cmState,w
retp
:done inc i2cmState ;If this was the last byte to be read, send a stop
retp
;*****************************************************************************************
; I2C Master Subroutines: Mainline Access routines (call from the mainline)
;*****************************************************************************************
;*********************************************************************************
; VP: I2C Master
;
; Function: i2cmSendByte
;
; This routine sets up the I2CM state machine to write the byte of data in
; the i2cmDataBuf register. Before entering this routine, make sure that
; the I2CM state machine is in its idle state (use the i2cmWaitNotBusy
; subroutine) and that the i2cmAddress is loaded with the address of the
; slave that this byte is going to, and that i2cmDataBuf is loaded with the
; data to send.
;*********************************************************************************
i2cmSendByte_
mov w,#1 ;There is one byte of data in the buffer
mov i2cmNumBytes,w
;*********************************************************************************
; VP: I2C Master
;
; Function: i2cmSendBytes
;
; This routine sets up the I2CM state machine to write the bytes of data in
; the i2cmDataBuf - i2cmData_n registers. Before entering this routine,
; make sure that the I2CM state machine is in its idle state (use the
; i2cmWaitNotBusy subroutine) and that i2cmAddress is loaded with the
; address of the slave that this byte is going to, that i2cmDataBuf - i2cmData_n
; are loaded loaded with the data to send, and that the i2cmNumBytes register
; is loaded with the number of data bytes to send.
;*********************************************************************************
i2cmSendBytes_
mov w,#%11111110
and i2cmAddress,w
clrb i2cmNack
mov w,#(i2cmWriteLoc-i2cmIdleLoc)
mov i2cmState,w
retp
;*********************************************************************************
; VP: I2C Master
;
; Function: i2cmWaitNotBusy
;
; This routine polls the i2cmState register until it is not busy. It returns
; when the I2C master state machine becomes idle. It returns a (0) in the w
; register if the transfer appeared successful (ie. The slave returned an ACK
; when addressed), and a (1) in the w register if the slave did not return
; an ack when addressed/written.
;*********************************************************************************
i2cmWaitNotBusy_
:wait_loop test i2cmState
sb z ; wait until I2C is idle
jmp :wait_loop
mov w,#1 ; return (1) if we are idle because a NACK was
snb i2cmNack ; received. Return (0) if we are idle because
retp ; this was successful
clr w
retp
;*********************************************************************************
; VP: I2C Master
;
; Function: i2cmGetByte
;
; This routine gets one byte of data from the slave at address i2cmAddress.
; before calling this routine, ensure that the I2C Master State Machine is
; in its idle state (use the i2cmWaitNotBusy subroutine) and that the
; i2cmAddress register is loaded with a valid address. The routine returns
; with the byte received in the w register and in the i2cmDataBuf register.
;*********************************************************************************
i2cmGetByte_
:nack mov w,#%00000001
or i2cmAddress,w
mov w,#1
mov i2cmNumBytes,w
clrb i2cmNack
mov w,#(i2cmReadLoc-i2cmIdleLoc)
mov i2cmState,w
call i2cmWaitNotBusy
and w,#$ff
sb z
jmp :nack
clrb i2cmRxFlag
mov w,i2cmDataBuf
retp
;*********************************************************************************
; VP: I2C Master
;
; Function: i2cmGetBytes
;
; This routine gets i2cmNumBytes of data from the slave at address
; i2cmAddress. Before calling this routine, ensure that the I2CM state
; machine is idle by using the i2cmWaitNotBusy subroutine, that i2cmAddress
; register contains the address of the slave to be read from, and that the
; i2cmNumBytes register is loaded with the number of bytes of data to receive.
; The received bytes will be contained in registers i2cmDataBuf to i2cmData_n.
;*********************************************************************************
i2cmGetBytes_
mov w,#%00000001
or i2cmAddress,w
clrb i2cmNack
clrb i2cmRxFlag
mov w,#(i2cmReadLoc-i2cmIdleLoc)
mov i2cmState,w
retp
;*********************************************************************************
; VP: I2C Master
;
; Function: i2cmInit
;
; This subroutine should be called on startup. It initializes the registers
; which are critical to the operation of the I2CM state machine.
;*********************************************************************************
i2cmInit_
mov w,#%10111111 ;Set RB in/out directions
mov i2cmPortBuf,w
mov w,#$ff
mov i2cmNumBytes,w
mov w,#$ff
mov i2cmIndex,w
retp
;*****************************************************************************************
org STRINGS_ORG ; This label defines where strings are kept in program space.
;*****************************************************************************************
;------------------------------------------------------------------------------
; Put String Data Here
;------------------------------------------------------------------------------
; Example:
;_hello dw 13,10,'UART Demo',0
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; - Routines that use location-dependant data, such as in example below, should
; use a LABEL rather than a literal value as their input. Example:
; instead of
; mov m,#3 ; move upper nybble of address of strings into m
; use
; mov m,#STRINGS_ORG>>8; move upper nybble of address of strings into m
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;*****************************************************************************************
org PAGE3_ORG
;*****************************************************************************************
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; To ensure that several Virtual Peripherals, when pasted together, do not cross
; a page boundary without the integrator's knowledge, put an ORG statement and one
; instruction at every page boundary. This will generate an error if a pasted
; subroutine moves another subroutine to a page boundary.
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
jmp $ ; This instruction will cause an assembler error if the source code before
; the org statement inadvertantly crosses a page boundary.
org $500 ;
jmp $ ; This instruction will cause an assembler error if the source code before
; the org statement inadvertantly crosses a page boundary.
;*****************************************************************************************
org MAIN_PROGRAM_ORG
;*****************************************************************************************
;------------------------------------------------------------------------------
; RESET VECTOR
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
; Program execution begins here on power-up or after a reset
;------------------------------------------------------------------------------
_resetEntry
;------------------------------------------------------------------------------
; Initialize all port configuration
;------------------------------------------------------------------------------
_mode ST_W ;point MODE to write ST register
mov w,#RB_ST ;Setup RB Schmitt Trigger, 0 = enabled, 1 = disabled
mov !rb,w
mov w,#RC_ST ;Setup RC Schmitt Trigger, 0 = enabled, 1 = disabled
mov !rc,w
IFDEF SX48_52
mov w,#RD_ST ;Setup RD Schmitt Trigger, 0 = enabled, 1 = disabled
mov !rd,w
mov w,#RE_ST ;Setup RE Schmitt Trigger, 0 = enabled, 1 = disabled
mov !re,w
ENDIF
_mode LVL_W ;point MODE to write LVL register
mov w,#RA_LVL ;Setup RA CMOS or TTL levels, 0 = TTL, 1 = CMOS
mov !ra,w
mov w,#RB_LVL ;Setup RB CMOS or TTL levels, 0 = TTL, 1 = CMOS
mov !rb,w
mov w,#RC_LVL ;Setup RC CMOS or TTL levels, 0 = TTL, 1 = CMOS
mov !rc,w
IFDEF SX48_52
mov w,#RD_LVL ;Setup RD CMOS or TTL levels, 0 = TTL, 1 = CMOS
mov !rd,w
mov w,#RE_LVL ;Setup RE CMOS or TTL levels, 0 = TTL, 1 = CMOS
mov !re,w
ENDIF
_mode PLP_W ;point MODE to write PLP register
mov w,#RA_PLP ;Setup RA Weak Pull-up, 0 = enabled, 1 = disabled
mov !ra,w
mov w,#RB_PLP ;Setup RB Weak Pull-up, 0 = enabled, 1 = disabled
mov !rb,w
mov w,#RC_PLP ;Setup RC Weak Pull-up, 0 = enabled, 1 = disabled
mov !rc,w
IFDEF SX48_52
mov w,#RD_PLP ;Setup RD Weak Pull-up, 0 = enabled, 1 = disabled
mov !rd,w
mov w,#RE_PLP ;Setup RE Weak Pull-up, 0 = enabled, 1 = disabled
mov !re,w
ENDIF
_mode DDIR_W ;point MODE to write DDIR register
mov w,#RA_DDIR ;Setup RA Direction register, 0 = output, 1 = input
mov !ra,w
mov w,#RB_DDIR ;Setup RB Direction register, 0 = output, 1 = input
mov !rb,w
mov w,#RC_DDIR ;Setup RC Direction register, 0 = output, 1 = input
mov !rc,w
IFDEF SX48_52
mov w,#RD_DDIR ;Setup RD Direction register, 0 = output, 1 = input
mov !rd,w
mov w,#RE_DDIR ;Setup RE Direction register, 0 = output, 1 = input
mov !re,w
ENDIF
mov w,#RA_latch ;Initialize RA data latch
mov ra,w
mov w,#RB_latch ;Initialize RB data latch
mov rb,w
mov w,#RC_latch ;Initialize RC data latch
mov rc,w
IFDEF SX48_52
mov w,#RD_latch ;Initialize RD data latch
mov rd,w
mov w,#RE_latch ;Initialize RE data latch
mov re,w
ENDIF
;------------------------------------------------------------------------------
; Clear all Data RAM locations
;------------------------------------------------------------------------------
zeroRam
IFDEF SX48_52 ;SX48/52 RAM clear routine
mov w,#$0a ;reset all ram starting at $0A
mov fsr,w
:zeroRam clr ind ;clear using indirect addressing
incsz fsr ;repeat until done
jmp :zeroRam
_bank bank0 ;clear bank 0 registers
clr $10
clr $11
clr $12
clr $13
clr $14
clr $15
clr $16
clr $17
clr $18
clr $19
clr $1a
clr $1b
clr $1c
clr $1d
clr $1e
clr $1f
ELSE ;SX18/20/28 RAM clear routine
clr fsr ;reset all ram banks
:zeroRam sb fsr.4 ;are we on low half of bank?
setb fsr.3 ;If so, don't touch regs 0-7
clr ind ;clear using indirect addressing
incsz fsr ;repeat until done
jmp :zeroRam
ENDIF
;------------------------------------------------------------------------------
; Initialize program/VP registers
;------------------------------------------------------------------------------
; VP: I2C Master
_bank i2cmBank ;
call @i2cmInit
;------------------------------------------------------------------------------
; Setup and enable RTCC interrupt, WREG register, RTCC/WDT prescaler
;------------------------------------------------------------------------------
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
;
; The suggested default values for the option register are:
; - Bit 7 set to 0: location $01 addresses the W register (WREG)
; - Bit 3 set to 1: Prescaler assigned to WatchDog Timer
;
; If a routine must change the value of the option register (for example, to access
; the RTCC register directly), then it should restore the default value for the
; option register before exiting.
;
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
RTCC_ON = %10000000 ;Enables RTCC at address $01 (RTW hi)
;*WREG at address $01 (RTW lo) by default
RTCC_ID = %01000000 ;Disables RTCC edge interrupt (RTE_IE hi)
;*RTCC edge interrupt (RTE_IE lo) enabled by default
RTCC_INC_EXT = %00100000 ;Sets RTCC increment on RTCC pin transition (RTS hi)
;*RTCC increment on internal instruction (RTS lo) is default
RTCC_FE = %00010000 ;Sets RTCC to increment on falling edge (RTE_ES hi)
;*RTCC to increment on rising edge (RTE_ES lo) is default
RTCC_PS_ON = %00000000 ;Assigns prescaler to RTCC (PSA lo)
RTCC_PS_OFF = %00001000 ;Assigns prescaler to WDT (PSA hi)
PS_000 = %00000000 ;RTCC = 1:2, WDT = 1:1
PS_001 = %00000001 ;RTCC = 1:4, WDT = 1:2
PS_010 = %00000010 ;RTCC = 1:8, WDT = 1:4
PS_011 = %00000011 ;RTCC = 1:16, WDT = 1:8
PS_100 = %00000100 ;RTCC = 1:32, WDT = 1:16
PS_101 = %00000101 ;RTCC = 1:64, WDT = 1:32
PS_110 = %00000110 ;RTCC = 1:128, WDT = 1:64
PS_111 = %00000111 ;RTCC = 1:256, WDT = 1:128
OPTIONSETUP equ RTCC_ON | RTCC_PS_OFF ; the default option setup for this program.
mov w,#OPTIONSETUP ; setup option register for RTCC interrupts enabled
mov !option,w ; and prescaler assigned to WDT.
;*****************************************************************************************
; Delay Before Starting I2C Transmission. (approx. 200us)
; This is to give the slaves time to initialize when power is applied to the system.
;*****************************************************************************************
_bank i2cmTimer ; switch to timer bank
inc i2cmTimerHigh ; ensure high byte of 16-bit timer is not 0
strtDelay test i2cmTimerHigh ; check if is 0
sb z
jmp strtDelay ; keep checking until is 0
;------------------------------------------------------------------------------
; MAIN PROGRAM CODE
;------------------------------------------------------------------------------
; This can be changed as you like to suit your own application.
; At present data is read from an EEPROM or SX Slave device and the data stored into
; RAM bank 7.
main
;VP_begin I2C Master
_bank i2cmBank
mov w,#i2cmSlaveAddress ; set address of slave to read.
mov i2cmAddress,w
_bank i2cmVars ; clear read and write pointers
clr i2cmReadAdr
clr i2cmWritePtr
i2cRead _bank i2cmVars
mov w,i2cmReadAdr
_bank i2cmBank
mov i2cmDataBuf,w ; load send register with data address
call @i2cmWaitNotBusy ; wait for I2CM state machine to enter idle state
call @i2cmSendByte ; send 'want to read' and bank addess to i2c slave
call @i2cmWaitNotBusy ; wait for I2CM state machine to enter idle state
call @i2cmGetByte ; Gets the byte from slave and returns it in w
snb i2cmNack
jmp got_NACK ; indicate no NACK by entering never ending loop
and w,#$FF ; check for null character read
snb z ; null char, end of string...restart
jmp main ; null char, end of string...restart
mov i2cRecvChar,w ; save received character
storeChar _bank i2cmVars ;
mov w,#i2cmRecvString ; set to store data received into bank 7
add w,i2cmWritePtr
mov fsr,w
mov w,i2cRecvChar ; save revcd data to address pointed to by i2cmWritePtr
mov indf,w
_bank i2cmVars
inc i2cmReadAdr ; set to read next address
inc i2cmWritePtr ; increment offset of where to save data
snb z ; finished reading entire bank...restart
jmp main
jmp i2cRead ; read another character from eeprom
:errors
got_NACK jmp $ ; PC here if did not get an ACK from slave
bad_data jmp $ ; PC here if data did not match an 'S'
;VP_end
;*****************************************************************************************
END ;End of program code
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************
file: /Techref/scenix/lib/io/osi2/i2c/i2c_master.src, 72KB, , updated: 2001/10/18 10:39, local time: 2024/12/25 21:53,
|
| ©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://linistepper.com/techref/scenix/lib/io/osi2/i2c/i2c_master.src"> scenix lib io osi2 i2c i2c_master</A> |
Did you find what you needed?
|