Help with Parallel to Serial Input
mynet43
Posts: 644
I'm designing a circuit board that requires more I/O pins than are available from the Propeller.
I have no problem adding output ports. I added 32 outputs using 74HC595 shift registers and 5 GPIO lines.
The problem is adding input ports. I'd like to use some kind of 8 or 16 bit parallel to serial chip to allow more input signals, but I'm not sure which chip to use or the best control interface. I'd like to be able to monitor these input lines in a way similar to the Propeller input lines.
Any help you can provide will be really appreciated.
Thank you,
Jim
I have no problem adding output ports. I added 32 outputs using 74HC595 shift registers and 5 GPIO lines.
The problem is adding input ports. I'd like to use some kind of 8 or 16 bit parallel to serial chip to allow more input signals, but I'm not sure which chip to use or the best control interface. I'd like to be able to monitor these input lines in a way similar to the Propeller input lines.
Any help you can provide will be really appreciated.
Thank you,
Jim

Comments
Do you have any experience with a driver or how best to make this look like propeller I/O inputs? I.e monitor them for change of state?
Jim
There might be more objects for using the 165. I just entered 74HC165 in the search window of the OBEX. The search might not have found them all.
I haven't used either of these objects myself.
Duane
The HC595 driver has to be very high speed, since I'm emulating I/O ports. I already have an assembly language driver I wrote for that.
By the way, I'll probably use the HCT165's, because I'm using them at 5V and controlling them with the 3.3V from the Prop. I've run into problems before trying to do this with the HC's.
It's beginning to look like I may have to dedicate a cog to monitoring the 165 chips. Does anyone have a better suggestion for this? I hate to use a cog for nothing but this.
Thank you for the help and support.
Jim
PUB ShiftIn ( LD165, SDat165 , CLK165 , NoOfBits) :vByte | vBit dira[LD165] ~~ '' Set load pin to out outa[LD165] := 0 '' Load pin off waitcnt(1000 + cnt) '' Wait for data to latch outa[LD165] := 1 '' Load pin on dira[SDat165] ~ '' Set data pin to out dira[CLK165] ~~ '' Set the clock Pin to out outa[CLK165] := 0 '' Clock Pin off repeat NoOfBits '' Repeat for number of bits vBit:=ina[SDat165] '' Read data vByte := (vByte << 1) + vBit '' Shift Left and store next bit outa[CLK165] := 1 '' Clock pin on outa[CLK165] := 0 '' Clock pin off waitcnt(1000 + cnt) '' Wait for data to be latched, then cycleHope that helps.
PS - also works with a '165.
I like the idea of using the same clock. The only problem with this is that the output (595's) is called only when needed, and I'm cascading 4 of the 595's to get 32 bits out. The 165 needs to be running continuously.
Sariel. Thanks for the code. It looks very straightforward. A couple of questions.
1. Did you tie all four of the latch pins together when you were getting 32 bits?
2. Is the waitcnt command necessary? It seems like spin is so slow that this wouldn't be an issue.
This is great code, it looks like it will be easy to put it in assembly.
It looks like I will probably interleave the 595 and 165 routines. I'll have the 165 running continuously, but interrupt it when a 595 access command is issued.
If anyone has some other suggestions or sample code. Let me know.
Thank you all for the help.
Jim
Yes. The latch pins were tied and the clock pins were all tied to the same I/O's. Ins and outs of each chip are connected accordingly (in of one to out of other, main output connected to the I/O of your choice on the prop.
The data sheet I have has a really crappy timing diagram, but after looking at it, I suppose those could be removed since there is a delay of like 20ns from load to clock pulse. Might be something I should look into when I go to update this project. I am not an assembly guru by any means, so I don't know how that would affect it if you were to port it over to PASM
Good luck, and glad to help out.
I use the code to control the brightness of 120 LEDs. I don't think there is anything in the code that isn't common sense but I'll post it if you'd like me to.
It sounds like you already of the 595 stuff figured out.
Duane
I like your idea of shifting out the data lines together. My code works fine, but it would obviously be faster if I shifted all 4 data lines out in parallel. Of course this would use three more prop pins.
I would like to look at your code if it's convenient.
Thank you for the help.
Jim
This code is pretty rough. The parent object is very large and cumbersome. I don't have a demo for this object. The parent object uses special hardware including an eight bit parallel ADC to capture video.
I use the attached object to display a gray-scale image that the video capture cog places in a 120 byte array.
The brightness of each LED is determined by the effective duty cycle of the object. I don't have a frequency parameter with the PWM. The period is just however long it takes to read the 120 bytes from the array 254 times(I plan to change this so the cycle loops 255 time instead of 254).
As the driver is currently written the count down (periodCount) never reaches zero since the djnz causes the loop to exit without using the zero value within the loop. This makes LEDs with brightness values of zero and one both turn on for the same amount of time(1/254 of total time). I plan to change the driver so the LEDs with brightness values of zero stay off the entire cycle.
I realized after I finished writing the driver, I probably had plenty of time to use only one data line for all 16 shift register chips. I haven't computed the frequency of the PWM produced by this driver.
I can change the location of the 120 byte buffer on the fly. This is useful to switch from displaying a grey-scale image of the video capture to displaying scrolling text or graphics.
Here's the guts of the PASM code:
DAT org enter or dira, dataMask or dira, latchMask or dira, clockMask checkCommand rdlong cogCommand,par add cogCommand,#:jmpTable jmp cogCommand :jmpTable jmp #newCycle jmp #changeBuffer jmp #changeBright ' jmp #changeContrast changeBuffer rdlong address0,ptrAddress 'mov address0, par mov address1, address0 add address1, blockSize mov address2, address1 add address2, blockSize mov address3, address2 add address3, blockSize ' move pointer to end of each block add address0, blockEnd add address1, blockEnd add address2, blockEnd add address3, blockEnd wrlong zero, par newCycle mov periodCount, #$FF add periodCount, extraLoops bigLoop andn outa, latchMask mov count, #30 mov tempAddr0, address0 mov tempAddr1, address1 mov tempAddr2, address2 mov tempAddr3, address3 smallerLoop andn outa, clockMask ' start with clock low andn outa, dataMask rdbyte value0, tempAddr0 cmp periodCount, value0 wc muxc outa, data0Mask rdbyte value1, tempAddr1 cmp periodCount, value1 wc muxc outa, data1Mask rdbyte value2, tempAddr2 cmp periodCount, value2 wc muxc outa, data2Mask rdbyte value3, tempAddr3 cmp periodCount, value3 wc muxc outa, data3Mask sub tempAddr0, #1 sub tempAddr1, #1 or outa, clockMask sub tempAddr2, #1 sub tempAddr3, #1 djnz count, #smallerLoop or outa, latchMask djnz periodCount, #bigLoop jmp #checkCommand 'newCycle changeBright rdlong extraLoops, extraPtr wrlong zero, par jmp #newCycleI'd be very surprised if there isn't a faster way of doing this but the driver, as it is now, is plenty fast for my current needs.
There should also be a way of using a rdlong instead of rdbyte to speed things up but I'm sure using a rdlong would make the code more complicated.
The name of the file was a result of my seeing the cool patterns scroll across the LED array (with a date code postfix).
Duane
LedCoolness110519a.spin
Thanks for sharing the code. I really like the way it's so compact, by using the wc and the muxc instructions. I need more practice with this.
You're right, I think you could speed it up if you needed to, by doing a rdlong of the 4 value bytes, then making an inner loop that uses local variables. This would eliminate three of the rdbytes, which should speed it up quite a bit.
Also, an easy way to get to the zero you're looking for would be to use a value of one less than periodCount within the loop, and just use periodCount for the djnz. This would make the upper limit 254, but that's probably OK.
mov periodCount, #$FF
mov valueCount, periodCount
sub valueCount, #1 ' use this as the cmp value in the loop and decrement it again just before the djnz
I'll copy the code and stare at it some more. I think it will help.
Thanks again!
Jim
You are welcome.
If I were to use a rdlong, I'd still want to read from four separate places within the buffer.
It would be much too hard to try to arrange the four groups of bytes in the array into an array of longs.
I have other methods that scroll text and graphics into and out of the array area.
There is probably some nice alogrithm to do just what I want. I'm just can't think of it myself. As I mentioned before, the code is already plenty fast for my needs. It's just hard not to think about ways of making it faster.
Yes, that's what I'll need to do. I can just chang "#$FF" to "#$100" to have it loop 255 times. I'll probably add a way of dynamicly changing the number of times to loop as a way of dimming the display. If I increased the times to loop to $200, it should cut the brightness in half.
I think I've been off on my hex to decimal conversions. The loop had been executing 255 times. I want it to execute 256 times. Don't you love the way you have to start counting at zero with computers?
Duane