Working with STM32 and displays: MAX7219 LED Matrix

In this guide, we shall interface the MAX7219 LED Matrix with STM32 and display characters, numbers and custom shapes.

In this guide, we shall cover the following:

  • MAX7219.
  • Connection with STM32
  • SPI configuration.
  • Sending data.
  • MAX7219 Initialization.
  • Functions to display character, number and custom shape.
  • Results.

1.1 MAX7219:

Unlike the 74HC595 shift register, we don’t need to control each digit of 7-segment display individually by adding delay. We just send data 7-segments and seven-segment control data. It has a built-in BCD to 7-segment decoder. Most importantly, it has on-chip 8×8 RAM which we can use to store display codes of digits that we want to display.

One of the major advantages of this display driver is that we can update individual bits without updating the entire display that isn’t possible with 74HC595 shift register. Hence, we can save microcontroller execution time and energy.

Moreover, it has a built-in brightness control circuit and by using a scan limit register we can configure the number of digits display we want to use.

1.2 Pinout Diagram:

As you can depict from this pinout diagram that it is a 24 pin IC. Eight pins provide data to segments that A-G pins. DIG0 to DIG7 pins are used to connect with common pins of 8 common-cathode type seven-segment displays. The pin configuration is the same for both MAX7219 and MAX7212.

This table shows the function of each pin. Out of 14 pins, 16 pins are used to connect 8-digit LED displays.

MAX7219 pin details

According to the above table, MAX7219/MAX7212 receives and transmits data through SPI communication. Pin1 (DIN) is a serial data input pin. We connect DIN of MAX7219 with a DOUT pin of PIC16F877A microcontroller SPI pin. Serial data transfer to an internal 16-bit register on every positive edge clock on pin13(Clk). We provide clock signals from Pic microcontroller SPI communication clock pin (SCK).

1.3 MAX7219/MAX7212 Registers:

This block diagram shows the configuration registers of MAX7219 LED driver IC. It has drivers for segements and digits.

MAX7219 MAX7221configuration Registers

Following are the main registers that used to configure and control display.

1.4 Registers Map:

The following table shows the registers within the IC.

1.5 Serial Data Format:

The data shall be sent in serial format with most significant bit (MSB) first to be transmit:

2. Connection with STM32:

The connection as following:

MAX7219STM32F411 Nucleo
Vcc5V
GNDGND
DinMOSI (PA7)
SCKSCK (PA5)
CSPA0

3. SPI Configuration:

Since the module uses SPI to communicate with the MCU, we shall configure SPI1 of the STM32F4 to work as half-duplex master mode.

Before we start, please refer to this guide for how to setup a new project in STM32CubeIDE.

We start off by creating new source and header file with name of MAX7219.c and MAX7219.h respectively.

Within the header file, use header guard as following:

#ifndef MAX7219_H_
#define MAX7219_H_


#endif /* MAX7219_H_ */

Within the header file, include stdint.h as following:

#include "stdint.h"

Include the following function:

void MAX7219_SPI_Init(void);

void MAX7219_Init(void);

void MAX7219_Char_Display(char ch);

void MAX7219_Num_Display(uint8_t Num);

void MAX7219_Custom_Shap(uint8_t shape[]);

Thats all for the header file.

For the source file, include the MAX7219 header file as following:

#include "max7219.h"

Include STM32F4 main header file:

#include "stm32f4xx.h"

Include ctypes (needed later):

#include <ctype.h>

Declare the following two dimensional array to hold the map for numbers and characters:

static uint8_t Num_Char[38][8]={
{0x3C,0x42,0x42,0x42,0x42,0x42,0x42,0x3C},//0
{0x10,0x30,0x50,0x10,0x10,0x10,0x10,0x7c},//1
{0x7E,0x2,0x2,0x7E,0x40,0x40,0x40,0x7E},//2
{0x3E,0x2,0x2,0x3E,0x2,0x2,0x3E,0x0},//3
{0x8,0x18,0x28,0x48,0xFE,0x8,0x8,0x8},//4
{0x3C,0x20,0x20,0x3C,0x4,0x4,0x3C,0x0},//5
{0x3C,0x20,0x20,0x3C,0x24,0x24,0x3C,0x0},//6
{0x3E,0x22,0x4,0x8,0x8,0x8,0x8,0x8},//7
{0x0,0x3E,0x22,0x22,0x3E,0x22,0x22,0x3E},//8
{0x3E,0x22,0x22,0x3E,0x2,0x2,0x2,0x3E},//9
{0x18,0x24,0x42,0x42,0x7E,0x42,0x42,0x42},//A
{0x3C,0x22,0x22,0x3c,0x22,0x22,0x3C,0x0},//B
{0x3C,0x40,0x40,0x40,0x40,0x40,0x40,0x3C},//C
{0x7C,0x22,0x22,0x22,0x22,0x22,0x22,0x7C},//D
{0x7C,0x40,0x40,0x7C,0x40,0x40,0x40,0x7C},//E
{0x7C,0x40,0x40,0x7C,0x40,0x40,0x40,0x40},//F
{0x3C,0x40,0x40,0x40,0x4c,0x44,0x44,0x3C},//G
{0x44,0x44,0x44,0x7C,0x44,0x44,0x44,0x44},//H
{0x7C,0x10,0x10,0x10,0x10,0x10,0x10,0x7C},//I
{0x3C,0x8,0x8,0x8,0x8,0x8,0x48,0x30},//J
{0x0,0x24,0x28,0x30,0x20,0x30,0x28,0x24},//K
{0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7C},//L
{0x81,0xC3,0xA5,0x99,0x81,0x81,0x81,0x81},//M
{0x0,0x42,0x62,0x52,0x4A,0x46,0x42,0x0},//N
{0x3C,0x42,0x42,0x42,0x42,0x42,0x42,0x3C},//O
{0x3C,0x22,0x22,0x22,0x3C,0x20,0x20,0x20},//P
{0x1C,0x22,0x22,0x22,0x22,0x26,0x22,0x1D},//Q
{0x3C,0x22,0x22,0x22,0x3C,0x24,0x22,0x21},//R
{0x0,0x1E,0x20,0x20,0x3E,0x2,0x2,0x3C},//S
{0x0,0x3E,0x8,0x8,0x8,0x8,0x8,0x8},//T
{0x42,0x42,0x42,0x42,0x42,0x42,0x22,0x1C},//U
{0x42,0x42,0x42,0x42,0x42,0x42,0x24,0x18},//V
{0x0,0x49,0x49,0x49,0x49,0x2A,0x1C,0x0},//W
{0x0,0x41,0x22,0x14,0x8,0x14,0x22,0x41},//X
{0x41,0x22,0x14,0x8,0x8,0x8,0x8,0x8},//Y
{0x0,0x7F,0x2,0x4,0x8,0x10,0x20,0x7F},//Z
};

For the SPI configuration:

Declare the following function:

void MAX7219_SPI_Init(void)

Within the function.

Enable clock access to GPIOA:

	/*Enable clock access to GPIOA*/
	RCC->AHB1ENR|=RCC_AHB1ENR_GPIOAEN;

Set PA5 and PA7 to alternate function and SPI alternate for SPI:

	/*Set PA5 and PA7 to alternate mode*/
	GPIOA->MODER|=GPIO_MODER_MODE5_1|GPIO_MODER_MODE7_1;
	GPIOA->MODER&=~(GPIO_MODER_MODE5_0|GPIO_MODER_MODE7_0);

	/*Set PA5 and PA7 alternate function*/
	#define AF05	0x05
	GPIOA->AFR[0]|=(AF05<<GPIO_AFRL_AFSEL5_Pos)|(AF05<<GPIO_AFRL_AFSEL7_Pos);

Set PA0 as output to act as CS line:

	/*Set PA0 as out for CS line*/
	GPIOA->MODER|=GPIO_MODER_MODE0_0;
	GPIOA->MODER&=~GPIO_MODER_MODE0_1;

	/*Set PA0 to high*/
	GPIOA->BSRR=GPIO_BSRR_BS0;

For the SPI configuration:

  • Set to half-duplex.
  • Enable output for half-duplex mode.
  • Set the data frame formate to 16-bit.
  • Software slave management.
  • Set to master mode.
  • Enable SPI peripheral.
	/*Enable clock access to SPI1*/

	RCC->APB2ENR|=RCC_APB2ENR_SPI1EN;

	/*Enable half-duplex mode*/
	SPI1->CR1|=SPI_CR1_BIDIMODE;

	/*Enable output for half-duplex (act as master)*/
	SPI1->CR1|=SPI_CR1_BIDIOE;

	/*Set data format to 16-bit*/

	SPI1->CR1|=SPI_CR1_DFF;

	/*Enable software slave management*/

	SPI1->CR1|=SPI_CR1_SSI|SPI_CR1_SSM;

	/*Set to master mode*/
	SPI1->CR1|=SPI_CR1_MSTR;

	/*Enable SPI Peripheral*/

	SPI1->CR1|=SPI_CR1_SPE;

Note: Make sure that the baud rate is not more than 8MHz. (By default STM32F4 running at 16MHz and baud rate of SPI is 8MHz by default)

4. Sending Data:

In order to send the data, we need to declare a function to transmit data over SPI bus as following:

static void SPI_Transmit(uint16_t data)
{
	SPI1->DR = data;
	/*Wait until TXE is set*/
	while(!(SPI1->SR & (SPI_SR_TXE))){}

	/*Wait for BUSY flag to reset*/
	while((SPI1->SR & (SPI_SR_BSY))){}

	/*Clear OVR flag*/
	(void)SPI1->DR;
	(void)SPI1->SR;
}

The function shall take uint16_t variable and returns nothing.

First, put the data into the DR register of the SPI.

Wait until the Transmit buffer is empty.

Wait until the bus is free.

Clear over run flag by reading the DR register and SR register.

For sending the data MAX719 chip, we shall use the following function:

static void MAX7219_Write(uint8_t address, uint8_t data)
{
	/*set the address and data as single variable*/
	uint16_t temp_data;
	temp_data=(address<<8)|data;

	/*Set CS to low*/
	GPIOA->BSRR=GPIO_BSRR_BR0;

	/*Transmit the data*/
	SPI_Transmit(temp_data);

	/*Set CS to high*/
	GPIOA->BSRR=GPIO_BSRR_BS0;

}

Since the chip requires address followed by the data, hence the function shall take two parameters:

  • Address.
  • Data.

Then we shall store both the address and data into single uint16_t variable with the upper 8-bit for the address and lower 8-bit for the data.

Set PA0 (CS) to low.

Send the data over SPI bus.

Set PA0 (CS) to high.

5. MAX7219 Initialization:

In order to Initialize the MAX7219, we need the following steps:

  • No decode for the incoming data.
  • Set the brightness.
  • Scan limit to 8 digit (8-lines).
  • Power up the mode.
  • No test.
void MAX7219_Init(void)
{
	MAX7219_Write(0x09, 0x00);       //  no decoding
	MAX7219_Write(0x0a, 0x03);       //  brightness intensity
	MAX7219_Write(0x0b, 0x07);       //  scan limit = 8 LEDs
	MAX7219_Write(0x0c, 0x01);       //  power down =0,normal mode = 1
	MAX7219_Write(0x0f, 0x00);       //  no test display
}

6. Functions to display character, number and custom shape:

To display a character:

void MAX7219_Char_Display(char ch)
{
	ch=toupper(ch);
	for(int i=1;i<9;i++)
	{
		MAX7219_Write (i,Num_Char[(ch - 55)][i-1]);
	}
}

First, convert the character from small case to upper case.

Send the character data to the MAX7219 from the lookup matrix.

For displaying the numbers:

void MAX7219_Num_Display(uint8_t Num)
{
	if(Num>9)return;

	for(int i=1;i<9;i++)
	{
		MAX7219_Write (i,Num_Char[(Num)][i-1]);
	}
}

For displaying custom shapes:

void MAX7219_Custom_Shap(uint8_t shape[])
{
	for(int i=1;i<9;i++)
	{
		MAX7219_Write (i,shape[i-1]);
	}
}

7. Results:

In main.c:

#include "delay.h"
#include "max7219.h"


uint8_t he[8]= {0B00000000, 0B01100110, 0B11111111, 0B11111111, 0B11111111, 0B00111100, 0B00011000, 0B00000000};


int main(void)
{

	delay_init(16000000);
	MAX7219_SPI_Init();
	MAX7219_Init();
    MAX7219_Char_Display('E');
	

	while(1)
	{

	}

}

When you run the code, you should get this:

Happy coding 😉

Add Comment

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