Plexus/source/I2C_slave_Venatrix.spin

246 lines
16 KiB
Plaintext

{{┌──────────────────────────────────────────┐
│ I2C slave object │
│ Author: Chris Gadd │
│ Copyright (c) 2013 Chris Gadd │
│ See end of file for terms of use. │
└──────────────────────────────────────────┘
This object creates an I2C slave device with 32 byte-sized registers on the Propeller, which an I2C master device can read from and write to using standard protocol:
7-bit device ID + read/write bit, 8-bit register address, 8 bits of data
Supports single, repeated, and page reads and writes
Does NOT check for overruns - Writing 5 bytes starting at register 31 may produce unexpected results
To use:
slave.start(28,29,$42) Start the slave object using p28 for clock, p29 for data, with device ID $42
slave.check Returns the index (31-0) of the highest byte in the register that was written to by a master
Subsequent calls to slave.check return the index of the next highest byte with new data
slave.check_reg(5) Returns the contents of register 5 only if the new-data flag for that register is set, returns -1 otherwise
slave.get(10) Returns the value of register 10
slave.put(11,#2) Stores the value 2 in register 11
slave.flush Clears all 32 registers to 0
slave.address Returns the base address of the slave registers - useful for directly operating on the registers
by higher-level objects
Tested up to 769Kbps (max transmit speed of the master object)
}}
VAR
long flags ' Used to determine if a register has new data from the master
byte _slave_address
byte SCL_pin
byte SDA_pin
byte register[32]
byte cog
PUB start(clk_pin, data_pin, slave_address) : okay
stop
_slave_address := slave_address
SCL_pin := clk_pin
SDA_pin := data_pin
okay := cog := cognew(@entry, @flags) + 1
PUB stop
if cog
cogstop(cog~ - 1)
PUB address
return @register
PUB check : index
{{
Returns the number of the highest byte that was written to:
If an I2C master wrote to addresses 3 and 7 of the slave's buffer, #7 is returned
The flag for the highest byte is then cleared, so a subsequent check would return #3
Returns -1 if all updated byte addresses have been returned (no new data)
}}
index := (>| flags) - 1
flags := flags & !(|< index) ' Clear the highest set bit
PUB check_reg(index)
{{
Returns the value of the indexed register if that register has new data
Returns -1 otherwise
}}
if |< index & flags
flags := flags & !(|< index)
return register[index]
return -1
PUB get(index)
return register[index]
PUB put(index,data)
register[index] := data
PUB flush | i
flags~
repeat i from 0 to 31
register[i]~
DAT org
entry
mov t1,par
mov flags_address,t1 ' Retrieve all of the addresses and
add t1,#4 ' pin assignments from the VAR block,
rdbyte device_address,t1 ' and create bit masks
shl device_address,#1
add t1,#1
rdbyte t2,t1
mov SCL_mask,#1
shl SCL_mask,t2
add t1,#1
rdbyte t2,t1
mov SDA_mask,#1
shl SDA_mask,t2
add t1,#1
mov register_address,t1
mov idle_mask,SCL_mask
or idle_mask,SDA_mask
'----------------------------------------------------------------------------------------------------------------------
main
call #wait_for_start
start_detected
call #receive
mov t1,I2C_byte
and t1,#%1111_1110 ' clear the read/write flag and compare received
cmp t1,device_address wz ' device address with assigned address
if_ne jmp #main
call #ack
test I2C_byte,#%0000_0001 wc ' test read(1) or write(0) bit of device address
if_nc jmp #write
read '(from_master)
call #respond ' The master sends an ACK or NAK in response to
add data_address,#1 ' every byte sent back from the slave
if_nc jmp #read ' Send another byte if ACK (c=0)
jmp #main ' Stop if NAK (c=1)
write '(from_master)
rdlong t1,flags_address ' Use t1 to hold all flags
mov t2,#1 ' Use t2 to hold the flag of the current register
mov data_address,register_address ' Prepare the to store new data
call #receive ' First byte received is a register address
add data_address,I2C_byte
shl t2,I2C_byte ' Shift the flag to the appropriate register
call #ack
:loop
call #receive ' Receive a data byte
wrbyte I2C_byte,data_address ' Store in the addressed register
add data_address,#1 ' Address the next register
or t1,t2 ' Update the flags
wrlong t1,flags_address
shl t2,#1 ' Shift the flag to the next register
call #ack
jmp #:loop
'======================================================================================================================
wait_for_start ' SCL 
waitpeq idle_mask,idle_mask ' SDA 
:loop
waitpne SDA_mask,SDA_mask
test SCL_mask,ina wc
if_nc jmp #wait_for_start
wait_for_start_ret ret
'----------------------------------------------------------------------------------------------------------------------
receive ' (Read)
mov loop_counter,#8 ' 
mov I2C_byte,#0 ' SCL 
:loop ' SDA ───────
waitpne SCL_mask,SCL_mask
waitpeq SCL_mask,SCL_mask
test SDA_mask,ina wc
rcl I2C_byte,#1
call #detect_st_or_sp ' Check to see if the received bit is a stop or restart
djnz loop_counter,#:loop
receive_ret ret
'----------------------------------------------------------------------------------------------------------------------
respond ' (Write) (Read ACK or NAK)
mov loop_counter,#8 '  
rdbyte I2C_byte,data_address ' SCL  
shl I2C_byte,#32-8 ' SDA  ───────
:loop
waitpne SCL_mask,SCL_mask
shl I2C_byte,#1 wc
muxnc dira,SDA_mask
waitpeq SCL_mask,SCL_mask
waitpne SCL_mask,SCL_mask
djnz loop_counter,#:loop
andn dira,SDA_mask
'receive_ack_or_nak
waitpne SCL_mask,SCL_mask
waitpeq SCL_mask,SCL_mask
test SDA_mask,ina wc ' C is set if NAK
call #detect_st_or_sp
respond_ret ret
'----------------------------------------------------------------------------------------------------------------------
ack ' SCL 
waitpne SCL_mask,SCL_mask ' SDA 
or dira,SDA_mask
waitpeq SCL_mask,SCL_mask
waitpne SCL_mask,SCL_mask
andn dira,SDA_mask
ack_ret ret
'----------------------------------------------------------------------------------------------------------------------
nak ' SCL 
waitpne SCL_mask,SCL_mask ' SDA 
waitpeq SCL_mask,SCL_mask
call #detect_st_or_sp
waitpne SCL_mask,SCL_mask
nak_ret ret
'----------------------------------------------------------------------------------------------------------------------
detect_st_or_sp
' test SDA_mask,ina wc ' routine is called with C already set to SDA
if_c jmp #:detect_restart
:detect_stop ' SCL 
test SCL_mask,ina wz ' SDA 
if_z jmp detect_st_or_sp_ret
test SDA_mask,ina wz
if_nz jmp #main
jmp #:detect_stop
:detect_restart ' SCL 
test SCL_mask,ina wz ' SDA 
if_z jmp detect_st_or_sp_ret
test SDA_mask,ina wz
if_z jmp #start_detected
jmp #:detect_restart
detect_st_or_sp_ret ret
'----------------------------------------------------------------------------------------------------------------------
SCL_mask res 1
SDA_mask res 1
idle_mask res 1
device_address res 1
register_address res 1
data_address res 1
flags_address res 1
I2C_byte res 1
loop_counter res 1
t1 res 1
t2 res 1
fit
DAT
{{
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ TERMS OF USE: MIT License │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │
│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │
│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│
│is furnished to do so, subject to the following conditions: │
│ │
│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│
│ │
│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │
│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │
│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
}}