This is Spin1, so you'll have to translate it, but if you've got a handle on I2C you'll see that it's quite simple. I haven't used this code in ages, but when I did it was on a client project (commercial HVAC system).
This is Spin1, so you'll have to translate it, but if you've got a handle on I2C you'll see that it's quite simple. I haven't used this code in ages, but when I did it was on a client project (commercial HVAC system).
Well... I tried to share my Spin1 driver for the chip that has been used in commercial products, but the forums tells me I am not allowed to upload files here. If you'd like that code you can send me a PM.
Here's the source (needs an I2C driver).
'' =================================================================================================
''
'' File....... jm_mcp23017.spin
'' Author..... Jon "JonnyMac" McPhalen
'' Copyright (c) 2011-2021 Jon McPhalen
'' -- see below for terms of use
'' E-mail..... jon.mcphalen@gmail.com
'' Started....
'' Updated.... 04 AUG 2013
'' -- modified to allow all addreses from single object
'' -- streamlined with wr_reg() and rd_reg()
''
'' =================================================================================================
con
MCP23017 = %0100_000_0 ' I2C device ID for MCP23017
' command bytes for standard POR settings
' -- IOCON.BANK = 0
IODIRA = $00 ' command bytes
IODIRB = $01
IPOLA = $02
IPOLB = $03
GPINTENA = $04
GPINTENB = $05
DEFVALA = $06
DEFVALB = $07
INTCONA = $08
INTCONB = $09
IOCON = $0A
GPPUA = $0C
GPPUB = $0D
INTFA = $0E
INTFB = $0F
INTCAPA = $10
INTCAPB = $11
GPIOA = $12
GPIOB = $13
OLATA = $14
OLATB = $15
obj
i2c : "jm_i2c"
pub null
'' This is not a top-level object
pub start(sclpin, sdapin)
'' Start MCP23017 device
'' -- creates slave ID for this object
'' -- sclpin and sdapin are connections to I2C buss
i2c.setupx(sclpin, sdapin) ' connect to i2C
pub wr_reg(reg, value, addr)
'' Write value to register in device at addr
'' -- see constants section for valid registers
'' -- see MCP23017 docs for details on register value
'' -- addr is device address, %000 to %111
i2c.start ' start transaction
i2c.write(MCP23017 | (addr << 1)) ' write id
i2c.write(reg) ' write reg #
i2c.write(value) ' write reg value
i2c.stop ' end transaction
pub rd_reg(reg, addr) | id, value
'' Read value from register in device at addr
'' -- see constants section for valid registers
'' -- see MCP23017 docs for details on register value
'' -- addr is device address, %000 to %111
id := MCP23017 | (addr << 1) ' id for write
i2c.start ' start transaction
i2c.write(id) ' write id
i2c.write(reg) ' write reg #
i2c.start ' re-start
i2c.write(id | %1) ' id for read
value := i2c.read(i2c#NAK) ' read value from reg
i2c.stop ' end transaction
return value ' return value to caller
con
{ ------------------------------- }
{ Named methods for easy access }
{ ------------------------------- }
pub cfg_dira(dirbits, addr)
'' Configure GPIOA IO pins
'' -- 0 bit = output, 1 bit = input
'' -- addr is device address, %000 to %111
wr_reg(IODIRA, dirbits, addr)
pub cfg_pola(polbits, addr)
'' Configure GP0 input polarity
'' -- 0 bit = normal, 1 bit = inverted
'' -- addr is device address, %000 to %111
wr_reg(IPOLA, polbits, addr)
pub cfg_iocon(iocbits, addr)
'' Configure IOCON bits
'' -- see documentation for details on iocon bits
'' -- addr is device address, %000 to %111
wr_reg(IOCON, iocbits, addr)
pub cfg_pua(pubits, addr)
'' Configure GPIOA pull-ups
'' -- 1 bit = pull-up pin via 100K
'' -- addr is device address, %000 to %111
wr_reg(GPPUA, pubits, addr)
pub wr_gpioa(outbits, addr)
'' Write outbits to GPIOA pins
'' -- only pins set as outputs affected
'' -- addr is device address, %000 to %111
wr_reg(GPIOA, outbits, addr)
pub rd_gpioa(addr) | devid, inbits
'' Read GPIOA bits
'' -- addr is device address, %000 to %111
return rd_reg(GPIOA, addr)
pub cfg_dirb(dirbits, addr)
'' Configure GPIOB IO pins
'' -- 0 bit = output, 1 bit = input
'' -- addr is device address, %000 to %111
wr_reg(IODIRB, dirbits, addr)
pub cfg_polb(polbits, addr)
'' Configure GPIOB input polarity
'' -- 0 bit = normal, 1 bit = inverted
'' -- addr is device address, %000 to %111
wr_reg(IPOLB, polbits, addr)
pub cfg_pub(pubits, addr)
'' Configure GPIOB pull-ups
'' -- 1 bit = pull-up pin via 100K
'' -- addr is device address, %000 to %111
wr_reg(GPPUB, pubits, addr)
pub wr_gpiob(outbits, addr)
'' Write value to GPIOB
'' -- addr is device address, %000 to %111
wr_reg(GPIOB, outbits, addr)
pub rd_gpiob(addr) | devid, inbits
'' Read GPIOB bits
'' -- addr is device address, %000 to %111
return rd_reg(GPIOB, addr)
con
{{
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 NON-INFRINGEMENT. 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.
}}
My Spin1 I2C driver
'' =================================================================================================
''
'' File....... jm_i2c.spin
'' Purpose.... Low-level I2C routines (requires pull-ups on SCL and SDA)
'' Author..... Jon "JonnyMac" McPhalen
'' Copyright (c) 2009-2021 Jon McPhalen
'' -- see below for terms of use
'' -- elements inspired by code from Mike Green & Chris Gadd
'' E-mail..... jon.mcphalen@gmail.com
'' Started.... 28 JUL 2009
'' Updated.... 01 MAR 2021
''
'' {$P1}
''
'' =================================================================================================
' IMPORTANT Note: This code requires pull-ups on the SDA _and_ SCL lines -- it does not drive
' the SCL line high.
'
' Restored clock stretch feature
con { fixed io pins }
RX1 = 31 ' programming / terminal
TX1 = 30
SDA1 = 29 ' eeprom / i2c
SCL1 = 28
con
#0, ACK, NAK
var
long sclpin ' bus pins
long sdapin
pub null
'' This is not a top-level object
pub setup
'' Setup I2C using Propeller EEPROM pins
setupx(SCL1, SDA1)
pub setupx(_scl, _sda)
'' Define I2C SCL (clock) and SDA (data) pins
longmove(@sclpin, @_scl, 2) ' copy pins
dira[sclpin] := 0 ' float to pull-up
outa[sclpin] := 0 ' write 0 to output reg
dira[sdapin] := 0
outa[sdapin] := 0
repeat 9
if (ina[sdapin])
quit ' reset device
dira[sclpin] := 1
dira[sclpin] := 0
pub present(slaveid)
'' Pings device, returns true if ACK
start
return (write(slaveid) == ACK)
pub wait(slaveid) | ackbit
'' Waits for I2C device to be ready for new command
repeat
start
ackbit := write(slaveid)
until (ackbit == ACK)
pub start
'' Create I2C start sequence
'' -- will wait if I2C bus SCL pin is held low
dira[sdapin] := 0 ' float SDA (1)
dira[sclpin] := 0 ' float SCL (1)
dira[sdapin] := 1 ' SDA low (0)
dira[sclpin] := 1 ' SCL low (0)
pub write(i2cbyte) | ackbit
'' Write byte to I2C bus
'' -- leaves SCL low
i2cbyte := !i2cbyte << 24 ' move msb (bit7) to bit31
repeat 8 ' output eight bits
dira[sdapin] := i2cbyte <-= 1 ' send msb first
dira[sclpin] := 0 ' SCL high (float to p/u)
dira[sclpin] := 1 ' SCL low
dira[sdapin] := 0 ' relase SDA to read ack bit
dira[sclpin] := 0 ' SCL high (float to p/u)
repeat while (ina[sclpin] == 0) ' hold for clock stretch
ackbit := ina[sdapin] ' read ack bit
dira[sclpin] := 1 ' SCL low
return ackbit
pub read(ackbit) : i2cbyte
'' Read byte from I2C bus
dira[sdapin] := 0 ' make sdapin input
repeat 8
dira[sclpin] := 0 ' SCL high (float to p/u)
i2cbyte := (i2cbyte << 1) | ina[sdapin] ' read the bit
dira[sclpin] := 1 ' SCL low
dira[sdapin] := !ackbit ' output ack bit
dira[sclpin] := 0 ' clock it
repeat while (ina[sclpin] == 0) ' hold for clock stretch
dira[sclpin] := 1
return i2cbyte
pub stop
'' Create I2C stop sequence
dira[sdapin] := 1 ' SDA low
dira[sclpin] := 0 ' float SCL
dira[sdapin] := 0 ' float SDA
con { license }
{{
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.
}}
@JonnyMac said:
Well... I tried to share my Spin1 driver for the chip that has been used in commercial products, but the forums tells me I am not allowed to upload files here.
Thanks for the alert Jon.
Uploads are allowed now on all the new categories.
I have only just gotten back to this and quite surprised to find that the I2C driver appears to be bit-banging and not a special smartpin function, correct?
@JonnyMac said:
Here's my P2 I2C driver. There is no smart pin I2C mode, but the pin configuration bits allow for optional pull-ups which the code can use.
@JonnyMac said:
Here's my P2 I2C driver. There is no smart pin I2C mode, but the pin configuration bits allow for optional pull-ups which the code can use.
Not to steal the thread, but... With flexprop, I'm not able to compile code that uses this version of jm_i2c.spin2 (the one in this thread as well as the one from the Spin 2 Beginner Series Part 12 download).
flexprop doesn't like the line (the addition of '#0,", within the enum):
"~/flexspin" -2 -l --tabs=4 -D_BAUD=230400 -O1 --charset=utf8 -I "~/flexprop//include" "~/Documents/P2ESCode/jm_p2_code/code-12_nextion-2/06_nextion_pixel_controller/jm_i2c.spin2"
Propeller Spin/PASM Compiler 'FlexSpin' (c) 2011-2021 Total Spectrum Software Inc.
Version 5.9.6 Compiled on: Nov 26 2021
~/Documents/P2ESCode/jm_p2_code/code-12_nextion-2/06_nextion_pixel_controller/jm_i2c.spin2:61: error: Duplicate case value
~/Documents/P2ESCode/jm_p2_code/code-12_nextion-2/06_nextion_pixel_controller/jm_i2c.spin2:62: error: Duplicate case value
~/Documents/P2ESCode/jm_p2_code/code-12_nextion-2/06_nextion_pixel_controller/jm_i2c.spin2:63: error: Duplicate case value
jm_i2c.spin2
child process exited abnormally
flexprop doesn't like the line (the addition of '#0,", within the enum):
That's a feature of Spin going back to Spin1. Hopefully, @ersmith can adjust FlexProp to handle that. In the meantime, I suggest modify your local copy like this.
flexprop doesn't like the line (the addition of '#0,", within the enum):
That's a feature of Spin going back to Spin1. Hopefully, @ersmith can adjust FlexProp to handle that. In the meantime, I suggest modify your local copy like this.
@dgately and @JonnyMac : I think flexprop's error in this case is legit, and I'm surprised PNut didn't give at least a warning. The case statement starting at line 57 of jm_i2c.spin2 reads:
case pullup
PU_NONE : pullup := P_HIGH_FLOAT ' use external pull-up
PU_EXT : pullup := P_HIGH_FLOAT
PU_1K5 : pullup := P_HIGH_1K5 ' 1.5k
PU_3K3 : pullup := P_HIGH_1MA ' acts like ~3.3k
other : pullup := P_HIGH_15K ' 15K
but PU_NONE and PU_EXT are both equal to 0, so there are indeed two case statements with the same label. Is that legal in Spin? At the very least it seems like it should be a warning.
In this case it does not, in fact, matter, since both cases do the exact same thing (set pullup to P_HIGH_FLOAT) but one can certainly imagine other cases where it could create unintended consequences.
My mistake -- I thought Dennis was suggesting the enumerated constants definition was the problem. Propeller Tool doesn't complain about the redundant value in case, but maybe it should. Since PU_NONE and PU_EXT has the same value, I will put the case structure back to the way it was.
case pullup
PU_NONE : pullup := P_HIGH_FLOAT ' use external pull-up
PU_1K5 : pullup := P_HIGH_1K5 ' 1.5k
PU_3K3 : pullup := P_HIGH_1MA ' acts like ~3.3k
other : pullup := P_HIGH_15K ' 15K
Comments
This is Spin1, so you'll have to translate it, but if you've got a handle on I2C you'll see that it's quite simple. I haven't used this code in ages, but when I did it was on a client project (commercial HVAC system).
This is Spin1, so you'll have to translate it, but if you've got a handle on I2C you'll see that it's quite simple. I haven't used this code in ages, but when I did it was on a client project (commercial HVAC system).
Well... I tried to share my Spin1 driver for the chip that has been used in commercial products, but the forums tells me I am not allowed to upload files here. If you'd like that code you can send me a PM.
Here's the source (needs an I2C driver).
My Spin1 I2C driver
Now that we can attach files... here they are.
Thanks for the alert Jon.
Uploads are allowed now on all the new categories.
Testing Jon's files.
@JonnyMac
Many thanks, Jon
Very nice coding, as usual
I have only just gotten back to this and quite surprised to find that the I2C driver appears to be bit-banging and not a special smartpin function, correct?
Edit: Ah, it's P1 I see.
Here's my P2 I2C driver. There is no smart pin I2C mode, but the pin configuration bits allow for optional pull-ups which the code can use.
Cheers! I had already grabbed it from github
The one I attached is the latest. Added a constant called PU_EXT the other day.
Not sure if it was the P2 but I remember reading somewhere that external pull-ups were recommended(?)
Not to steal the thread, but... With flexprop, I'm not able to compile code that uses this version of jm_i2c.spin2 (the one in this thread as well as the one from the Spin 2 Beginner Series Part 12 download).
flexprop doesn't like the line (the addition of '#0,", within the enum):
compile example:
To compile, I had to modify the line:
A difference in how flexprop handle enumerators, I suppose.
dgately
That's a feature of Spin going back to Spin1. Hopefully, @ersmith can adjust FlexProp to handle that. In the meantime, I suggest modify your local copy like this.
Yep! Thanks!
@dgately and @JonnyMac : I think flexprop's error in this case is legit, and I'm surprised PNut didn't give at least a warning. The case statement starting at line 57 of jm_i2c.spin2 reads:
but PU_NONE and PU_EXT are both equal to 0, so there are indeed two case statements with the same label. Is that legal in Spin? At the very least it seems like it should be a warning.
In this case it does not, in fact, matter, since both cases do the exact same thing (set pullup to P_HIGH_FLOAT) but one can certainly imagine other cases where it could create unintended consequences.
My mistake -- I thought Dennis was suggesting the enumerated constants definition was the problem. Propeller Tool doesn't complain about the redundant value in case, but maybe it should. Since PU_NONE and PU_EXT has the same value, I will put the case structure back to the way it was.