6522 VIA Shift Register & 7 Segment Display

Shifting out data using the 6522

Objectives

  1. Gain an appreciation of how the VIA 6522 shift register works
  2. Use timer 1 and interrupts in a practical application
  3. Extend my familiarity with the 6522 features, writing assembly code and debugging hardware and software
The goal for the project was to count up from zero in hexadecimal and display the count on a 4 digit 7 segment display.  The count should increment roughly each second

Approach

I had a 4 digit 7 segment display (common cathode) that needs 12 lines to drive it - one for each of the 7 segments, one for a decimal point and 4 to select the digit (the segments of each digit are connected together).  These displays work by multiplexing the segments across the four digits - each digit needs to have its segments turned on about every 5mS to be flicker free.

It would be possible to use both port A and B to provide the 12 lines but that seems extravagant and anyway, I wanted to try the shift register.

My idea was to shift out the data to a pair of daisy-chained 74HC595 shift register ICs.  In addition to the serial data these need a clock and a 'latch' line.  Data is clocked in bit by bit but is not loaded into the output registers until a positive pulse is received in the latch pin.

The 6522 uses CB1 for a clock signal and CB2 for data.  I chose to use one of the PortB pins to supply the latch signal because it is straightforward to toggle this in software to create a pulse of the right duration (positive for this case but could be negative).  Another option would be to use CA1 but I didn't investigate this as it seemed to offer less software control.

This is the plan:

The 74HC595 pins can source or sink up to 35mA.  Each of the pins driving the segments is supplying current to one segment at a time and that is 8mA for my display.  On the other hand, the 4 pins switching the digits in turn would need to sink current from 7 segments (for the number 8) i.e. 7 * 8mA = 56mA which is more than the 595 can handle.  This is why those 4 pins are used to switch an NPN transistor (using base resistors to limit current).  I used 2N3904 but most NPN transistors
should work fine (BC107, 2N2222 etc).

Although this approach works it is not optimal for a long term application in a 'real' system:
  • the interrupts needs to refresh each digit about 50 times a second; 200 per second in total or 5ms between interrupts.  The 6502 can cope with this and timing is not critical (some jitter would not be visible) but it is hardly the best use of the 6502
  • if interrupts get disabled the display would freeze
  • another option for serious use is the MAX7219 driver IC.  Modules can be purchased for around £5 in the uk complete with 8 x 7 segment digits and the MAX7219 driver.  These require only three lines so would replace all of my circuit

VIA Configuration
  • DDRB set all pins to output (only PB0 is used): 000001
  • ACR - timer 1 to free run - bit 7 & 6; timer 2 off - bit 5; shift register - shift out under PHI2 control bit 4, 3 & 2; latch not used - bit 1 & 0 (%01 0 110 00)
  • Enable T1 interrups - IER %11000000 (1 in bit 7 means all 1s in bit 6 - 0 enable an interrupt)
  • Disable other interrupts - IER 111111 (0 in bit 7 means all 1 in bit 6 - 0 disables an interrupt)
  • Timer 1 - T1CL loaded with zero and T1CH loaded with $17 (chosen to give a fast enough refresh rate to avoid flicker)
The 6522 shift register can output serial data using either PHI2 (the system clock) or Timer 2.  I chose to use PHI2 because it seemed simpler than setting up timer 2 and my system clock is 1MHz which is well inside the specification for the 74HC595.

Assembler Code

These are the main functions of the code must perform:
  1. set up PortB
  2. set up timer 1
  3. set up interrupts
  4. set up the shift register
  5. load timer 1 with a value and start it
  6. Set up and maintain a hexadecimal count starting from zero and incrementing about every second
  7. Handle the timer 1 interrupts including:
    1. resetting the interrupt flag
    2. tracking which of the 4 digits needs to be refreshed next
    3. sending the required segment pattern and digit number to the 6522 shift register
    4. pulsing the latch line to load the new data into the 595 output registers

The assembler code is in the Gist: https://gist.github.com/robinharris/73ea10166a630d080733bf8f71171e5b

Comments