PDA

View Full Version : Port alias in Port Expanders (MCP23S17)



ecoli-557
- 1st December 2015, 20:21
Hello to All-
I am trying to do something and I just can't break through the fog to see how this may be accomplished;
Assign an alias to a port pin on a port expander.
I know how to do it with a port/pin on the uProc but I just can't figure it out.
The port expander is using SPI and each expander has its own chip select.
How can you alias GPB.2 on chip2 from GPB.2 on chip3?
What am I missing? Can it be done? Should it be done?
I get that aliasing is somewhat lazy but it makes for clear code!

Anyone done this?
-Stumped......

HenrikOlsson
- 1st December 2015, 21:04
Hi,
Since it's on a bus external to the PIC itself you are NOT going to do things like GPB.2 = 1 to "get at" the output directly since the actual data needs to be transfered to the external chip using SHIFTOUT or the MSSP module or whatever. I suspect you know that but wanted to get it squared away just in case.

Since the port expander is on the SPI bus you're sending it one or more bytes. I'm guessing that one (or more) of those bytes reflects the desired state of the outputs on the expander. (Had you said which chip you're using I could've been more specific).

So if you have a byte called ChipOne then you can alias each bit as usual ChipOne.0 = 1 but then you obviously need to transfer ChipOne to chip one. If you had a timer interrupt sending the byte to the expander x timer per second then doing ChipOne.4 = 1 might be fast/transparent enough - I don't know.

/Henrik.

ecoli-557
- 2nd December 2015, 00:25
Thanks, yes I did say which chip in the topic.... <grin>.
And yes again, that is what I had come up with and I had used that in the past - using
outputs var byte[4]
and then indexing by just what you said -> outputs[0].4 = 1 or whatever.
Just thought there may be other solutions....... more minds the better!
Thanks for the input, I appreciate it.

HenrikOlsson
- 2nd December 2015, 06:25
Ooops, I missed the topic, sorry about that!

I guess I don't see the problem then....sorry about that as well :-)

/Henrik.

ecoli-557
- 2nd January 2016, 18:13
Henrik-
I may have been too hasty in my description ( I am back on my home project) as I am stumped. I had used the I2C version of this part a while back and have it working. Now I am trying to use the SPI version (MCP23S17) for its speed and I can't seem to figure out how to get from a byte indicating which bit to turn on for the MCP23S17 part.
Lets say I receive a var and it's value is $E. This var needs to tell the MCP23S17 that the 15th output should turn on. Now that is GPB-7 on the MCP23S17.
I have a routine that converts HEX to DECIMAL (thanks to this forum years ago) and that works fine, how to I get a single decimal to go to the correct port on the MCP23S17?
My code works fine to send a binary string (easier to visualize for me) to the MCP23S17 but I can't seem to get the finesse of going from 1 hex or 1 dec char to the right bit output.
I have looked through the forum and I have seen yours and others pointing to OUT.0[var] which gets to the VAR-position of the OUT variable but as I have 2 eight bit outputs I am stumped......

From the MCP23S17 datasheet it looks like it may be addressed as one 16 bit output, is that accurate? Any suggestions?
Some code below:


OutA var byte

OUTtest:'------------ TEST
OutA=$1 'Simulate number from serial comm, example '1'
K=OutA 'Sets up the conversion from hex to decimal
gosub H2D 'Performs the conversion
debug "After H2D K=",dec1 K,13 'Double-check what the sub did
k=k-8 'try this for a pointer
OutA_DataOut=OutA.0[k] 'Try to set the correct bit out of 8 to turn on and pass to OutA_DataOut which is passed to the MCB23S17
OutA_MCPReg = OLATA 'Sets up PORTA output
' OutA_DataOut=%11111110 'This works by setting individual bits, but not good for what I want to do
gosub SEND_OutA_2317 'Send to the expander
pause 10 'wait a bit
OutA_MCPReg = OLATB 'Sets up PORTB output
OutA_DataOut=%01111111
gosub SEND_OutA_2317

ecoli-557
- 2nd January 2016, 18:15
Henrik-
Also forgot to mention that in the older I2C design I had 8 bits of inputs and 8 bits of outputs on one 2317.
This new design is all outputs - I thought it would be easier!
Not so much......
Regards,
Steve

HenrikOlsson
- 2nd January 2016, 18:44
Sounds like what you're looking for is the DCD operator:

DCD returns the decoded value of a bit number. It changes a bit number (0 - 31) into a binary number with only that bit set to 1. All other bits are set to 0.
B0 = DCD 2 ' Sets B0 to %00000100
So, perhaps something:


OutBits VAR WORD
GPA VAR OutBits.BYTE0
GPB VAR OutBits.BYTE1

Outbits = DCD $E


Henrik.

ecoli-557
- 2nd January 2016, 19:56
Thanks I will try that!

I have made some progress but I still need a way to get from 1 byte which will tell me which of the 16 outputs to turn on.

Current test code:


OutA=$FFFE 'Initial value for word var
OutA_hi=OutA.highbyte 'Splits into hibyte
OutA_lo=OutA.lowbyte 'And into low byte of the word var
OutA_lo.3=0 'Bit mod
OutA_hi.5=0 'Another bit mod
OutA_MCPReg = OLATA 'Sets up PORTA output
OutA_DataOut=OutA_lo 'Sets up data to be pushed to the MCP23S17 as the low byte
gosub SEND_OutA_2317 'Send it
pause 10 'Wait a bit
OutA_MCPReg = OLATB 'Sets up PORTB output
OutA_DataOut=OutA_hi 'Sets up data to be pushed to the MCP23S17 as the hi byte
gosub SEND_OutA_2317 'Send it

This gives me what I want - EXCEPT - for that I have to resolve 1 byte into a word and figure out how to get the pointer right........
starting with a byte var which has a value from 0-15 to a specific bit on the MCP23S17 still evades me I am afraid.

ecoli-557
- 2nd January 2016, 20:05
Henrik-
Thanks for the assist, but I don't think I can use that idea. There are some outputs which will be already active which another serial command comes in and I will need to add to the already ON LEDs with whatever new ones are called for.
There is a serial comm line coming into the project and on that serial comm is a byte which will let me know which output(s) to turn on. Some may or may not be on.
So, I will have to logically AND them to get the correct outputs on when they need to be.

ecoli-557
- 2nd January 2016, 20:17
Why does this not work?
OutA.12=0 doesn't gen an error but it nor the other (OutA.15=0) turns ON an LED?????



OutA=$FFFE 'Initial value for word var
OutA_hi=OutA.highbyte 'Splits into hibyte
OutA_lo=OutA.lowbyte 'And into low byte of the word var
' OutA_lo.3=0 'Bit mod - WORKS
' OutA_hi.5=0 'Another bit mod - WORKS
OutA.12=0 'Why doesn't this work?
OutA.15=0 'Or this?
OutA_MCPReg = OLATA 'Sets up PORTA output
OutA_DataOut=OutA_lo 'Sets up data to be pushed to the MCP23S17 as the low byte
gosub SEND_OutA_2317 'Send it
pause 10 'Wait a bit
OutA_MCPReg = OLATB 'Sets up PORTB output
OutA_DataOut=OutA_hi 'Sets up data to be pushed to the MCP23S17 as the hi byte
gosub SEND_OutA_2317 'Send it

richard
- 2nd January 2016, 22:10
OutA_hi=OutA.highbyte 'Splits into hibyte
OutA_lo=OutA.lowbyte 'And into low byte of the word var

is a simple assignment operation not an alias



OutA=$FFFE 'Initial value for word var
OutA.12=0
OutA.15=0
OutA_hi=OutA.highbyte 'Splits into hibyte
OutA_lo=OutA.lowbyte 'And into low byte of the word var

would fix it (get the order correct)

ecoli-557
- 2nd January 2016, 22:23
Thanks!
I tried your suggestion:


OutA=$FFFF 'Initial value for word var
OutA.0=0 'Bit mod - WORKS
OutA.2=0 'Another bit mod - WORKS OutA.0=0 'Bit mod - WORKS
OutA.8=0 'Another bit mod - WORKS
OutA.10=0 'Bit mod - WORKS
OutA.12=0 'Another bit mod - WORKS
OutA_hi=OutA.highbyte 'Splits into hibyte
OutA_lo=OutA.lowbyte 'And into low byte of the word var
OutA_MCPReg = OLATA 'Sets up PORTA output
OutA_DataOut=OutA_lo 'Sets up data to be pushed to the MCP23S17 as the low byte
gosub SEND_OutA_2317 'Send it
pause 10 'Wait a bit
OutA_MCPReg = OLATB 'Sets up PORTB output
OutA_DataOut=OutA_hi 'Sets up data to be pushed to the MCP23S17 as the hi byte
gosub SEND_OutA_2317 'Send it

And it works like a CHAMP!
However (you knew it might come) how do I resolve a single byte variable which tells me which bit to turn on/off into a word var?
My need for this home project, is to read a byte var and based on its value turn on/off a bit on the expander?
Example: Var=$C, so I need to turn on the 12th bit on the expander.
Regards,
Steve

ecoli-557
- 2nd January 2016, 22:33
Richard-
I tried the following:


OutA=$FFFF 'Initial value for word var
PortDevice=$D 'Var from comm link
K=PortDevice 'Copes var to var for hex to dec converter
gosub H2D 'Perform sub
OutA.K=0 'PBP3 barks with 'Bad variable modifier
OutA.0=0 'Bit mod - WORKS
OutA.2=0 'Another bit mod - WORKS OutA.0=0 'Bit mod - WORKS
OutA.8=0 'Another bit mod - WORKS
OutA.10=0 'Bit mod - WORKS
OutA.12=0 'Another bit mod - WORKS
OutA_hi=OutA.highbyte 'Splits into hibyte
OutA_lo=OutA.lowbyte 'And into low byte of the word var
OutA_MCPReg = OLATA 'Sets up PORTA output
OutA_DataOut=OutA_lo 'Sets up data to be pushed to the MCP23S17 as the low byte
gosub SEND_OutA_2317 'Send it
pause 10 'Wait a bit
OutA_MCPReg = OLATB 'Sets up PORTB output
OutA_DataOut=OutA_hi 'Sets up data to be pushed to the MCP23S17 as the hi byte
gosub SEND_OutA_2317 'Send it

In an effort to get from the single byte to the pointer, but PBP3 barks with the bad variable modifier.......
There a ought to be a way to do this.......

ecoli-557
- 2nd January 2016, 22:55
OK, it is working!
Thanks to all who helped and provided some food for thought.....
Code below for any others who need to do this:


OutA=$FFFF 'Initial value for word var - everything is OFF
PortDevice=$A 'Var from comm link
OutA.0[PortDevice]=0 'This works!
' OutA.2=0 'Manual bit mod - works though
' OutA.12=0 'Manual bit mod - works though
OutA_hi=OutA.highbyte 'Splits into hibyte
OutA_lo=OutA.lowbyte 'And into low byte of the word var
OutA_MCPReg = OLATA 'Sets up PORTA output
OutA_DataOut=OutA_lo 'Sets up data to be pushed to the MCP23S17 as the low byte
gosub SEND_OutA_2317 'Send it
pause 10 'Wait a bit
OutA_MCPReg = OLATB 'Sets up PORTB output
OutA_DataOut=OutA_hi 'Sets up data to be pushed to the MCP23S17 as the hi byte
gosub SEND_OutA_2317 'Send it

Happy New Year!
-Steve

richard
- 2nd January 2016, 23:26
OutA var word


asm

OutA_hi=OutA
OutA_lo=OutA+1

endasm

OutA_hi var byte EXT
OutA_lo var byte EXT




the easy way

ps I hope I got the endianness correct if not swap the hi byte low byte definitions

richard
- 2nd January 2016, 23:53
yep I did get it wrong (choice of two I will pick the wrong one every time)

found my endianness note

MyVar =$AABB would be stored little-endian. I.E. if the address
of MyVar starts at 30h, then $BB will be stored at 30h followed by $AA at 31h


so



asm
OutA_hi=OutA+1
OutA_lo=OutA
endasm

ecoli-557
- 3rd January 2016, 21:49
Thanks, I will give that a try.
Now if I could write both bytes at the same SPI communication, that would be an even bigger win!

tumbleweed
- 4th January 2016, 10:32
If you have the MCP23S17 IOCON register set to it's default setting of $00 that sets the chip to 16-bit mode (BANK=0) with auto address increment enabled (SEQOP=0).

In that mode you can send register address byte = $14 (OLATA) and then write the two data bytes and they'll go to OLATA and OLATB.

ecoli-557
- 5th January 2016, 21:02
That is exactly what I was looking for!
I thought I had done this before but could have been mistaken.
Will try later this evening.
Thanks!
-Steve