Accessing Shift Registers

Date:2017-08-18

Intro

Driving LEDs or 7-Segment Registers can be done in several ways. This document explains, how to interface them with shift registers. A shift register will receive 8 bit of data on a connection with 2 signals: data and clock. After the complete byte has been transfered, a third signal, latch, can be used to assert the newly received byte on the corresponding 8 output pins. A shift register is, in a way, a serial to parallel converter. Interestingly, shift registers can be chained.

For example, one can transfer 4 Bytes through a chain of 4 shift register chips. After the transfer asserting the latch signal will make the transfered bytes appear on all the output pins simultaneously.

I strongly prefer shift registers and thus continuous signals on LEDs over the multiplexing (and thus flickering) methods.

Code Details

We need to define 3 pins corresponding to the acutal hardware layout:

\ abakus display
PORTD 2 portpin: sr_latch
PORTD 3 portpin: sr_clock
PORTD 4 portpin: sr_data

Then +sr will set the pins up correctly (never assume that pins are setup in a specific way before you start using them):

: +sr
  sr_latch pin_output  sr_latch high
  sr_clock pin_output  sr_clock low
  sr_data  pin_output  sr_data  high
;

bit>sr will clock out just one bit and nothing else:

: bit>sr ( bit -- )
  if sr_data high else sr_data low then
  sr_clock high  sr_clock low
;

The shift registers I use expect the most significant bit first. This could be otherwise. So byte>sr clock out one byte. The loop can be written differently, of course.

: get.bit ( byte pos -- bit )
  1 swap lshift    \ -- byte bitmask
  and              \ -- bit
;

\ clock one byte out, MSB first!
: byte>sr ( byte -- )
  0 7 do
    dup i get.bit   \ 7 6 5 ... 0: MSB first!
    bit>sr
  -1 +loop
  drop
;

So far data is transfered, but not asserted onto the output pins. So >sr adds asserting a low pulse on pin latch:

: >sr
  byte>sr
  sr_latch low sr_latch high
;

Putting it all together

The main program will know, how many shift registers are chained (if any), thus a function like n>sr will be needed: clock out a known number of bytes, then assert the latch signal.

: n>sr  ( c1 .. cn n -- )
  0 ?do
    byte>sr
  loop
  sr_latch low sr_latch high
;

The Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
\ 2017-05-10 shiftregister.fs
\
\ Written in 2017 by Erich Wälde <erich.waelde@forth-ev.de>
\
\ To the extent possible under law, the author(s) have dedicated
\ all copyright and related and neighboring rights to this software
\ to the public domain worldwide. This software is distributed
\ without any warranty.
\
\ You should have received a copy of the CC0 Public Domain
\ Dedication along with this software. If not, see
\ <http://creativecommons.org/publicdomain/zero/1.0/>.
\
\
\ needs pin definitions
\     PORTD 2 portpin: sr_latch
\     PORTD 3 portpin: sr_clock
\     PORTD 4 portpin: sr_data
\ words:
\     +sr ( -- )     \ enable shift register
\     emit.sr ( n -- )     \ transfer 1 Byte
\     type.sr ( xn-1 .. x0 n -- )  \ n Bytes

: +sr
  sr_latch pin_output  sr_latch high
  sr_clock pin_output  sr_clock low
  sr_data  pin_output  sr_data  high
;

: bit>sr ( bit -- )
  if sr_data high else sr_data low then
  sr_clock high  sr_clock low
;

: get.bit ( byte pos -- bit )
  1 swap lshift    \ -- byte bitmask
  and              \ -- bit
;

\ clock one byte out, MSB first!
: byte>sr ( byte -- )
  0 7 do
    dup i get.bit
    bit>sr
  -1 +loop
  drop
;