Thursday 12 July 2012

How a shift register works

This was a quick-and-dirty project just to understand how our shift registers from Farnell work. The block diagram on the datasheet makes things pretty simple, but we still need to make sure we're toggling the right lines to "flush" the data into the device:


From this block diagram, we can see that any data clocked into the shift register is held in a buffer, and only transferred onto the output pins when the RCK line is toggled (in this case, according to the "truth table" from low to high)


So we built a quick usb device (for getting data quickly from our PC to the end device) using a PIC 18F255 microcontroller and used it to clock data in/out of the shift register. The PIC was connected to our SMT LED breakout board as follows:

PORTB.5 -> serial clock in (SCLK)
PORTB.4 -> register clock (RCLK)
PORTB.3 -> serial data in (SI)



The layout/PDF for the breakout board is here:
SOIC-to-DIP breakout With Leds (for shift register)


(don't forget that this is for SMT components, not through-hole, so the final etched board will be mirrored. That's why PIN1 is marked on the top right, not top left, and Vcc- pin16 - is top left. You'll need to use your imagination to "flip" this and get the pins in the right place!)

A shift register is quite easy to work with, but until you actually get your hands on one and make it work, it can be difficult to fully appreciate what's needed. A shift register can be used in a variety of ways, you can tie the clock lines together (to save pins) but that only adds to the complexity. You can daisy-chain the registers, by enabling the output line and have one SR feed the next in a chain and so on.

We're going to concentrate on simply getting serial data into the device, and displaying it on a sequence of LEDs. This project will form the basis for future projects by reducing the pin-count needed to individually control lots of separate LEDs. Having learned from the word clock in November, we're sticking to surface mount LEDs this time - no way are we going to be spending hours and hours drilling hundreds of holes again!

(this board was made up with solder paste and a cheap nasty "fire-starter" soldering iron from Farnell. Hence the scorched traces and missing pin header where the connect-to-ground jumper burned away and had to be fixed with a bit of spare wire!)

With the shift register hooked up to the PIC microcontroller, we dropped some simple code onto the PIC and wrote a quick VB app to send data to the PIC. The PIC simply takes a single byte value and sends it to the shift register in the following sequence:


Here's the Oshonsoft Basic code for our PIC:
Define CLOCK_FREQUENCY = 20
Define CONFIG1L = 0x24
Define CONFIG1H = 0x0c
Define CONFIG2L = 0x38
Define CONFIG2H = 0x00
Define CONFIG3L = 0x00
Define CONFIG3H = 0x03
Define CONFIG4L = 0x80
Define CONFIG4H = 0x00
Define CONFIG5L = 0x0f
Define CONFIG5H = 0xc0
Define CONFIG6L = 0x0f
Define CONFIG6H = 0xe0
Define CONFIG7L = 0x0f
Define CONFIG7H = 0x40

UsbSetVendorId 0x1234
UsbSetProductId 0x1234
UsbSetVersionNumber 0x1122
UsbSetManufacturerString "Nerd Club"
UsbSetProductString "Generic USB HID Device"
UsbSetSerialNumberString "1111111111"
UsbOnIoInGosub input_report_before_sending
UsbOnIoOutGosub output_report_received
UsbOnFtInGosub feature_report_before_sending
UsbOnFtOutGosub feature_report_received

AllDigital
Dim i As Byte
Dim pattern As Byte

Symbol rclk = PORTB.4
Symbol sclk = PORTB.5
Symbol si = PORTB.3

UsbStart
i = 0
pattern = 255

loop:
     UsbService

     If i = 0 Then
          Gosub senddata
          i = 1
     Endif
Goto loop

End



senddata:
     'prepare the shift register "flush" pin
     Low rclk

     'send data to the shift register
     If pattern.0 = 1 Then High si Else Low si
     Gosub toggleserialclock

     If pattern.1 = 1 Then High si Else Low si
     Gosub toggleserialclock

     If pattern.2 = 1 Then High si Else Low si
     Gosub toggleserialclock

     If pattern.3 = 1 Then High si Else Low si
     Gosub toggleserialclock

     If pattern.4 = 1 Then High si Else Low si
     Gosub toggleserialclock

     If pattern.5 = 1 Then High si Else Low si
     Gosub toggleserialclock

     If pattern.6 = 1 Then High si Else Low si
     Gosub toggleserialclock

     If pattern.7 = 1 Then High si Else Low si
     Gosub toggleserialclock
          
     'flush the shift data onto the output pins
     High rclk

Return



toggleserialclock:
     Low sclk
     WaitUs 50
     High sclk
Return



feature_report_received:
Return



feature_report_before_sending:
Return



output_report_received:
     pattern = UsbIoBuffer(0)
     Gosub senddata
Return



input_report_before_sending:
Return

The idea is that we'll send a byte value to our PIC controller board (via usb) which it will then clock into the shift register. It's important to note that we've only used six of the possible eight output pins on the shift register. So the first and last bits (mapped to pins 7 and 15 on the shift register) aren't actually connected to an LED. Any data value sent will be displayed as X 1 1 1 1 1 1 X

To see this in action, we can send the byte value 47 to the PIC.
In binary, this is represented by 0 0 1 0 1 1 1 1
Since our board is ignoring the first and last bits (why didn't we just connect them up to make explaining this a bit easier?!) this gives the LED lighting sequence X 0 1 0 1 1 1 X

Instead of just typing random numbers, converting to binary and comparing the results, we built a simple VB app to generate a byte value and send it to the PIC microcontroller:



Although focus isn't brilliant you should be able to see that by clicking the onscreen LEDs, the VB app is generating a byte value (from a binary representation of the LED states). This value is sent to the PIC, which turns the byte value back into a series of bits, which are then transmitted to the shift register, one bit at a time.

We set the value of the serial input line to match the value of the first "bit" in our byte value (either on/high or off/low) then toggle the serial clock line. This pushes the first bit into the shift register. When then set the serial line to match the second bit (either on/high or off/low) and toggle the serial clock line again. This pushes the first bit along Ishifts it) and pushes the second bit into the register.
After doing this eight times, our shift register contains the byte value we sent over a single serial data line. To get the value to display by lighting our LEDs, we need a low-to-high transistion on the RCLK pin. Until this pin goes high, the data sent to the shift register sits in the "top part" and doesn't actually affect the output pins. When the RCLK pin goes high, the value in the shift part of the register is "flushed" into the latching register which controls the output pins QA-QH (and the sequence of LEDs changes)


Btw - it turns out that we did indeed mount the shift registers the correct way around on our little breakout board, despite not finding a pin1 marker on the casing. If anyone else is using these 74HC595 SOIC shift registers, they should be mounted, rotated 90 degrees to the right and pin1 is the top-left-most pin as per the datasheet!


1 comment: