Shop OBEX P1 Docs P2 Docs Learn Events
Help with Parallel to Serial Input — Parallax Forums

Help with Parallel to Serial Input

mynet43mynet43 Posts: 644
edited 2011-06-07 08:04 in Propeller 1
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

Comments

  • LeonLeon Posts: 7,620
    edited 2011-06-06 05:54
    Try the 74HC165.
  • mynet43mynet43 Posts: 644
    edited 2011-06-06 07:15
    Thanks Leon,

    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
  • LeonLeon Posts: 7,620
    edited 2011-06-06 07:32
    There should be some code in the Obex for it.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2011-06-06 07:40
    There are a couple of objects for using a 74HC165 chip. Microcontrolled wrote one and the other one is for using both the 74HC165 and the 74HC595 at the same time.

    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
  • mynet43mynet43 Posts: 644
    edited 2011-06-06 08:24
    Thanks for the info Duane. I'll check it out.

    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
  • Duane DegnDuane Degn Posts: 10,588
    edited 2011-06-06 08:51
    Jim, Depending on how fast you need to output with the 595 chips, you could interweave the 165 commands and share the same clock signal. Both the 595 and 165 chips are eight bits so you might be able to use the same loop counter. I'm not sure if this would work but if it did it should be faster than switching from one to the other inside one cog.
  • SarielSariel Posts: 182
    edited 2011-06-06 08:57
    I have recently worked with this issue, and was able to get 4 74HC165 IC's cascaded together to get 32 I/O's with this:
     
    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 cycle
    
     
    

    Hope that helps.
  • kwinnkwinn Posts: 8,697
    edited 2011-06-06 08:58
    You might want to take a look at the '597 as well. It seems to be the input equivalent of the '595. I have used the 595 and 597 together to shift data in on one pin and out on another using the same clock. Works like a charm.

    PS - also works with a '165.
  • mynet43mynet43 Posts: 644
    edited 2011-06-06 10:24
    Thanks for all the good info. I was kind-of thinking to combine the two drivers.

    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
  • SarielSariel Posts: 182
    edited 2011-06-06 12:24

    1. Did you tie all four of the latch pins together when you were getting 32
    bits?

    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.

    2. Is the waitcnt command necessary? It seems like spin is so slow that this
    wouldn't be an issue.

    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.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2011-06-06 13:14
    Jim, I don't have any code for the 165 or 597 chips but I've written a driver that uses 16 595s. I'm using the shift registers in this project. It's in PASM. The 595s have all their clock and latch lines tied together. I broke the data lines into four groups of four. All four groups have their data shifted out together.

    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
  • mynet43mynet43 Posts: 644
    edited 2011-06-06 13:21
    Hi 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
  • Duane DegnDuane Degn Posts: 10,588
    edited 2011-06-07 00:43
    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       #newCycle
     
    

    I'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
  • mynet43mynet43 Posts: 644
    edited 2011-06-07 05:43
    Hi Duane,

    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
  • Duane DegnDuane Degn Posts: 10,588
    edited 2011-06-07 08:04
    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.
    mynet43 wrote: »
    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

    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
Sign In or Register to comment.