FPGA for Fun #1 (Part 1) – Driving the MAX 7219 LED Display Module

FPGA for Fun #1 (Part 1) - Driving the MAX 7219 LED Display
Module

I recently ordered a few LED display modules based on the MAX7219 chip. When
they arrived, I immediately began putting together projects to use them. The
first project was to wire one up to my Raspberry Pi and use C to bitbang some
numbers to display. I wrote about this in a [previous
post](https://cerkit.com/2017/11/25/driving-the-max-7219-7-segment-display-
module-from-asp-net-core-on-the-raspberry-pi/).

The Lucid HDL “code” can be found on GitHub – FPGA for Fun on
GitHub

Blog posts in this series:

This time, I’d like to talk about how I wired the MAX7219 LED display module
up to my Mojo FPGA.

The Module

FPGA for Fun #1 (Part 1) - Driving the MAX 7219 LED Display
Module

Here is a module on eBay similar to the ones that I purchased:

[Red MAX7219 8-Digit LED Display SPI Control Module Digital Tube For
Arduino](https://www.ebay.com/itm/Red-MAX7219-8-Digit-LED-Display-SPI-Control-
Module-Digital-Tube-For-
Arduino/232597763398?epid=2163908124&hash=item3627e83546:g:6OQAAOSw6hNZiVAF)

If this link is dead (or the sale has ended), search for the above text on
eBay and you should find one.

Wiring the MAX7219 to the Mojo

First, I started by wiring the Max 7219 to the Mojo.
FPGA for Fun #1 (Part 1) - Driving the MAX 7219 LED Display
Module

I wired RAW from the Mojo to VCC, GND to GND, Pin 50 to Data In (DIN), Pin 51
to CLK, and Pin 40 to CS (Load).

MAX7219 Datasheet
(PDF)

Timing Considerations

The timing diagram for the MAX7219 module is fairly straight forward. It only
covers four of the interface pins. We won’t be using the data out pin (DOUT),
so we can ignore that one.

We’re interested in the behavior of the Load, Clock, and the data in pins and
how they interact.

FPGA for Fun #1 (Part 1) - Driving the MAX 7219 LED Display
Module

Since we are working with an FPGA clock that runs too fast for our display
module, we’ll need to use an SPI component to manage our communication and
synchronize with that clock instead of the FPGA. In addition, we’ll use a
d-flip-flop to manage the state of the load pin between clock cycles.

After creating a new Mojo project, I added the SPI Master component to it
(it’s one of the available components in the Mojo IDE).

The load pin must be low to clock data in, so that’s our first step when
sending data. Once the load pin is low, we start our clock pulses to the CLK
pin. As you can see in the timing diagram, we need to send one bit of data at
roughly the same time as our clock pulse. The Max 7219 uses a 16-bit word for
each message that it processes, so we need to keep up with how many clock
cycles have passed since we started (we’ll use a d-flip-flop to do this in the
implementation).

Once we get to 16 clock pulses on the CLK pin, the data is then latched into
either the digit or control registers by pulsing LOAD/CS.

LOAD/CS must go high concurrently with or after the 16th rising clock edge,
but before the next rising clock edge or data will be lost. This is very
important to remember and was the source of a great deal of trouble for me
when first building this project. I spent a few hours debugging this (I don’t
yet have the wave capture utility setup in my Mojo IDE and the logic probe
that I ordered hasn’t arrived from Hong Kong, yet).

Finite State Machines

I’ve found that planning my logic out in terms of finite state machines really
helps me get my head around the problem. The Max7219 component is driven by
three main states: Idle, Transfer Address, and Transfer Data. It requires a
little setup to synchronize the clocks and manage the state of the load pin.

MAX7219 Component FSM

FPGA for Fun #1 (Part 1) - Driving the MAX 7219 LED Display
Module

We need to make an assumption about the overall state of the component; there
is an SPI Clock counter with its clock input set to the SCK output of the SPI
component.

IDLE
  • Set Load (CS) to 1
  • If Start == 1
    • Save incoming address and data to memory
    • Reset the SPI clock counter (helps manage two clock domains)
    • Set Load to 0 (tells the MAX7219 to start listening)
    • Transition to next state
TRANSFER_ADDR
  • Start the SPI component
  • Send the address we have in memory to the SPI component’s input
  • Set the data out to the value of the SPI.MOSI
  • If the SPI clock counter == 8
    • Transfer to the next state.
TRANSFER_DATA
  • Start the SPI component
  • Send the data we have in memory to the SPI component’s input
  • Set the data out to the value of the SPI.MOSI
  • If the SPI clock counter == 16
    • Latch the data by pulsing the load pin high
    • Reset the SPI clock counter
    • Transition back to the IDLE state to await more input

Register Map

There are 13 registers on the Max7219 module including a no-op.

FPGA for Fun #1 (Part 1) - Driving the MAX 7219 LED Display
Module

For example, if I wanted to turn on all of the segments on the display (test
mode), I would send 0x0F as the first 8 bits of the message (the address), and
then 0x01 as the next 8 bits (the data). I would then pulse the Load pin to
latch the message into the Max7219.

Let’s go over the order of operations that I’ll use in my test component:

  • Shutdown = 0
  • Shutdown = 1
  • Decode Mode = 0 (display segments instead of BCD data)
  • Scan Limit = 7 (use all 8 digits)
  • Send 8 characters
  • Halt (send all zeros – no-op)

Main Component (Mojo Top) FSM

The main component is a little more complicated because it is the one that
drives the MAX7219 component. It keeps up with the characters that are being
sent, as well as the states required to initialize the MAX7219 module.

This particular component was written as an initial test to see if I could get
data to display. It does not handle any sort of number conversion or true
display driver functionality. It merely outputs “DEADBEEF” to the display. The
segment index is simply a d-flip-flop that I use to keep up with which segment
we’re sending the data for. Since there is more than one segment, we have to
send them one at a time. We use the index to determine which one to send.

FPGA for Fun #1 (Part 1) - Driving the MAX 7219 LED Display
Module

IDLE
  • Reset segment index to 0
  • Transition to next state
SEND_SHUTDOWN
  • Start the Max7219 component
  • Set the max7219 address to 0C (Shutdown)
  • Set the max7219 data to 00
  • When max is complete, transition to next state

Remember that all actions shown are simultaneous and happen for each clock
cycle. That’s why we have to wait until the Max7219 component has completed
sending its message and is no longer busy before moving on to the next state.
Otherwise, we’d overload the module and nothing would be displayed. It’s a
careful balancing act of clock, data, and load.

SEND_RESET
  • Start the Max7219 component
  • Set the max7219 address to 0C (Shutdown)
  • Set the max7219 data to 01 (Normal operation)
  • When max is complete, transition to next state
SENDNODECODE
  • Start the Max7219 component
  • Set the max7219 address to 09 (Decode Mode)
  • Set the max7219 data to 00
  • When max is complete, transition to next state
SENDALLDIGITS
  • Start the Max7219 component
  • Set the max7219 address to 0B (Scan Limit)
  • Set the max7219 data to 07 (use all 8 digits)
  • When max is complete, transition to next state
SEND_WORD
  • Start the Max7219 component
  • If segment index < 8
    • Set the max7219 address to the segment index +1 (to account for a zero-based index and a 1 based segment count)
    • Set the max7219 data to segment array with the index of segment index (segments.q[segment_index.q])
    • When max is complete, increment the segment index by 1
  • If segment index = 8
    • Reset segment index to 0
    • Transition to the next state (HALT)
HALT
  • Set the max7219 address to 0
  • Set the max7219 data to 0

Segment Lines

Programming the 7-segments is done by sending an 8-bit number representing the
state of the segments in order from A-G with the decimal point being the most
significant bit.

FPGA for Fun #1 (Part 1) - Driving the MAX 7219 LED Display
Module

I coded the 16 hexadecimal digits and added a few characters so that the word
“Error” could be displayed, as well as a minus sign and a blank space.

0 = 01111110
1 = 00110000
2 = 01101101
3 = 01111001
4 = 00110011
5 = 01011011
6 = 01011111
7 = 01110000
8 = 01111111
9 = 01111011
A = 01110111
B = 00011111
C = 01001110
D = 00111101
E = 01001111
F = 01000111
O = 00011101
R = 00000101
MINUS = 01000000
BLANK = 00000000

That’s the basic state workflow for the module on the FPGA. I had a great time
figuring this out and I hope this has helped you understand how you might wire
a MAX7219 module to your FPGA.

In the [next post](https://cerkit.com/2017/12/27/fpga-for-
fun-1-part-2-driving-the-max-7219-led-display-module/), we’ll take a look at
the implementation in the Lucid Hardware Description Language. Lucid is the
HDL used by the Mojo. It is a simpler HDL than Verilog or VHDL, but
transpiles to Verilog for
synthesis onto the hardware.

Video:

Read [Part 2](https://cerkit.com/2017/12/27/fpga-for-fun-1-part-2-driving-the-
max-7219-led-display-module/)

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top