Three Max7219 connected in daisy-chain to output ADC data

Hello there

I am trying to show values of ADC0, ADC1, and ADC2 on 3 different 3-digit displays. I am using 3 Max7219 connected in daisy-chain configurations, but I am struggling and could really use some help to do that.

I was able to show 1 on display 1, 2 on display 2, 3 on display 3 through the following code:

#include <avr/io.h>
#include <util/delay.h>

// SPI initialization
void SPI_Init() {
    // Set MOSI (PB5) and SCK (PB7) as output
    DDRB |= (1<<PB5) | (1<<PB7);
    // Set SS (PB4) as output
    DDRB |= (1<<PB4);
    // Enable SPI, Master mode, set clock rate fck/16
    SPCR = (1<<SPE) | (1<<MSTR) | (1<<SPR0);
}

// Send a byte via SPI
void SPI_SendByte(uint8_t data) {
    SPDR = data;
    while (!(SPSR & (1<<SPIF)));  // Wait for transmission complete
}

// Send a command to all MAX7219s
void MAX7219_Send(uint8_t address, uint8_t data) {
    PORTB &= ~(1<<PB4);  // SS low
    SPI_SendByte(address);  // Address
    SPI_SendByte(data);     // Data
    PORTB |= (1<<PB4);   // SS high
}

// Initialize MAX7219
void MAX7219_Init() {
    MAX7219_Send(0x09, 0x00);  // Decode mode: No decode for all digits
    MAX7219_Send(0x0B, 0x02);  // Scan limit: Display digits 0-2 (3 digits)
    MAX7219_Send(0x0C, 0x01);  // Shutdown: Normal operation
    MAX7219_Send(0x0F, 0x00);  // Display test: Off
}

// Display values on each of the 3-digit displays
void Display_Values() {
    // Display "1" on the first display (digit 0)
    MAX7219_Send(1, 0x30);  // "1" on digit 0
    MAX7219_Send(2, 0x00);  // Clear digit 1
    MAX7219_Send(3, 0x00);  // Clear digit 2

    // Select the second display (CS line for second MAX7219)
    PORTB &= ~(1<<PB4);  // SS low for the second MAX7219
    SPI_SendByte(1);    // Address digit 0
    SPI_SendByte(0x6D); // "2" on digit 0
    PORTB |= (1<<PB4);  // SS high

    // Select the third display (CS line for third MAX7219)
    PORTB &= ~(1<<PB4);  // SS low for the third MAX7219
    SPI_SendByte(1);    // Address digit 0
    SPI_SendByte(0x79); // "3" on digit 0
    PORTB |= (1<<PB4);  // SS high
}

int main() {
    SPI_Init();              // Initialize SPI
    MAX7219_Init();          // Initialize all MAX7219s

    while (1) {
        Display_Values();    // Display "1", "2", "3" on each of the 3 displays
        _delay_ms(2000);     // Delay to observe
    }
}

image

can you show picture?

1 Like
0b01111110, // 0
0b00110000, // 1
0b01101101, // 2
0b01111001, // 3
0b00110011, // 4
0b01011011, // 5
0b01011111, // 6
0b01110000, // 7
0b01111111, // 8
0b01111011  // 9

If the schematic shown truly reflects what you've built, add a .1uF ceramic decoupling capacitor to each IC (Vcc to GND).

1 Like

Thanks for the tip , I will do that.

#include <avr/io.h>
#include <util/delay.h>
const static byte numTable [] PROGMEM  = {
  0b01111110, // 0
  0b00110000, // 1
  0b01101101, // 2
  0b01111001, // 3
  0b00110011, // 4
  0b01011011, // 5
  0b01011111, // 6
  0b01110000, // 7
  0b01111111, // 8
  0b01111011  // 9
};

// SPI initialization
void SPI_Init() {
  // Set MOSI (PB5) and SCK (PB7) as output
  DDRB |= (1 << PB5) | (1 << PB7);
  // Set SS (PB4) as output
  DDRB |= (1 << PB4);
  // Enable SPI, Master mode, set clock rate fck/16
  SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0);
}

// Send a byte via SPI
void SPI_SendByte(uint8_t data) {
  SPDR = data;
  while (!(SPSR & (1 << SPIF))); // Wait for transmission complete
}

// Send a command to all MAX7219s
void MAX7219_Send(uint8_t address, int data) {
  PORTB &= ~(1 << PB4); // SS low
  SPI_SendByte(address);
  SPI_SendByte(data);
  PORTB |= (1 << PB4); // SS high
}

// Initialize MAX7219
void MAX7219_Init() {
  MAX7219_Send(0x09, 0x00);  // Decode mode: No decode for all digits
  MAX7219_Send(0x0B, 0x02);  // Scan limit: Display digits 0-2 (3 digits)
  MAX7219_Send(0x0C, 0x01);  // Shutdown: Normal operation
  MAX7219_Send(0x0F, 0x00);  // Display test: Off
}

// Display values on each of the 3-digit displays
void Display_Values(int number) {

  MAX7219_Send(1, 0x30);  // "1" on digit 0

  byte data = number > 99 ? pgm_read_word_near(numTable + (number / 100) % 10) : 0;
  MAX7219_Send(1, data);

  data = number > 9 ? pgm_read_word_near(numTable + (number / 10) % 10) : 0;
  MAX7219_Send(2, data);

  MAX7219_Send(3, pgm_read_word_near(numTable + number % 10));
}
int main() {
  SPI_Init();              // Initialize SPI
  MAX7219_Init();          // Initialize all MAX7219s

  while (1) {
    Display_Values(123);    // Display "123", "456", "789" on the 3 displays
    Display_Values(456);
    Display_Values(789);
    _delay_ms(2000);     // Delay to observe
  }
}
1 Like

1. Which Arduino Board (UNO/NANO/MEGA) and which ADC Channels are you using?

2. Cascade operation of MAX7219 requires a bit good undestanding of the role of "No-op Register" of the MAX7219.

3. I would suggest that you first make three individual display units with three mAX7219. After that you cascade two, make them operational, and then cascade all three.

4. Working with MAX7219 will be very much enjoyable if you can use the follwing MAX7219-driven display units (Fig-1).
image
Figure-1:

1 Like

Output:
display 1: 756
display 2: 700
display 3: 789

Thank you.

I am using custom Atmega32 board..
The idea definitely turned out way more complicated than I expected!

So, you are not using standard Arduino!!

Is the above a count from an ADC channel? What is the corresponding input DC voltage? Do you want to show that voltage on the display unit? How many digits do you want after the decimal point?

1 Like
#include <avr/io.h>
#include <util/delay.h>
const static byte numTable [] PROGMEM  = {
  0b01111110, // 0
  0b00110000, // 1
  0b01101101, // 2
  0b01111001, // 3
  0b00110011, // 4
  0b01011011, // 5
  0b01011111, // 6
  0b01110000, // 7
  0b01111111, // 8
  0b01111011  // 9
};

// SPI initialization
void SPI_Init() {
  // Set MOSI (PB5) and SCK (PB7) as output
  DDRB |= (1 << PB5) | (1 << PB7);
  // Set SS (PB4) as output
  DDRB |= (1 << PB4);
  // Enable SPI, Master mode, set clock rate fck/16
  SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0);
}

// Send a byte via SPI
void SPI_SendByte(uint8_t data) {
  SPDR = data;
  while (!(SPSR & (1 << SPIF))); // Wait for transmission complete
}

// Send a command to all MAX7219s
void MAX7219_Send(uint8_t address, int data) {
  SPI_SendByte(address);
  SPI_SendByte(data);
}

// Initialize MAX7219
void MAX7219_Init() {
  PORTB &= ~(1 << PB4); // SS low
  MAX7219_Send(0x09, 0x00);  // Decode mode: No decode for all digits
  MAX7219_Send(0x09, 0x00);  // Decode mode: No decode for all digits
  MAX7219_Send(0x09, 0x00);  // Decode mode: No decode for all digits
  MAX7219_Send(0x0B, 0x02);  // Scan limit: Display digits 0-2 (3 digits)
  MAX7219_Send(0x0B, 0x02);  // Scan limit: Display digits 0-2 (3 digits)
  MAX7219_Send(0x0B, 0x02);  // Scan limit: Display digits 0-2 (3 digits)
  MAX7219_Send(0x0C, 0x01);  // Shutdown: Normal operation
  MAX7219_Send(0x0C, 0x01);  // Shutdown: Normal operation
  MAX7219_Send(0x0C, 0x01);  // Shutdown: Normal operation
  MAX7219_Send(0x0F, 0x00);  // Display test: Off
  MAX7219_Send(0x0F, 0x00);  // Display test: Off
  MAX7219_Send(0x0F, 0x00);  // Display test: Off
  PORTB |= (1 << PB4); // SS high
}

// Display values on each of the 3-digit displays
void Display_Values(int number1, int number2, int number3) {
  PORTB &= ~(1 << PB4); // SS low
  byte data;

  data = number > 99 ? pgm_read_word_near(numTable + (number1 / 100) % 10) : 0;
  MAX7219_Send(1, data);  // "1" on digit 0
  data = number > 99 ? pgm_read_word_near(numTable + (number2 / 100) % 10) : 0;
  MAX7219_Send(1, data);
  data = number > 99 ? pgm_read_word_near(numTable + (number3 / 100) % 10) : 0;
  MAX7219_Send(1, data);

  data = number > 9 ? pgm_read_word_near(numTable + (number1 / 10) % 10) : 0;
  MAX7219_Send(2, data);
  data = number > 9 ? pgm_read_word_near(numTable + (number2 / 10) % 10) : 0;
  MAX7219_Send(2, data);
  data = number > 9 ? pgm_read_word_near(numTable + (number3 / 10) % 10) : 0;
  MAX7219_Send(2, data);

  MAX7219_Send(3, pgm_read_word_near(numTable + number1 % 10));
  MAX7219_Send(3, pgm_read_word_near(numTable + number2 % 10));
  MAX7219_Send(3, pgm_read_word_near(numTable + number3 % 10));

  PORTB |= (1 << PB4); // SS high
}
int main() {
  SPI_Init();              // Initialize SPI
  MAX7219_Init();          // Initialize all MAX7219s

  while (1) {
    Display_Values(123, 456, 789); // Display "123", "456", "789" on the 3 displays
    _delay_ms(2000);     // Delay to observe
  }
}
1 Like

So, you are not using standard Arduino!!

Unfortunately not.

Is the above a count from an ADC channel?

This is a static value from the provided code by kolaha.

Do you want to show that voltage on the display unit?

Yes

How many digits do you want after the decimal point?

Only one.

It's good that you are playing with ATmega32 which was my favorite one before I have switched overt to ATmega328P. Anyway, being a Arduino Forum Member, you may own Arduino UNO/NANO/MEGA.

How are you programming the TAmega32? Are you using Arduino as ISP Programmer?

1 Like

It's good that you are playing with ATmega32 which was my favorite one before I have switched overt to ATmega328P. Anyway, being a Arduino Forum Member, you may own Arduino UNO/NANO/MEGA.

How are you programming the TAmega32? Are you using Arduino as ISP Programmer?

1 Like

display 1: 75E
display 2: 700 with one missing segment on last 0
display 3: 789 the 9 is inverted

I definitely have some Arduinos around yes..
I am programming it through USB with ft232rl on board..

1 Like
#include <avr/io.h>
#include <util/delay.h>
const static byte numTable [] PROGMEM  = {
  0b01111110, // 0
  0b00110000, // 1
  0b01101101, // 2
  0b01111001, // 3
  0b00110011, // 4
  0b01011011, // 5
  0b01011111, // 6
  0b01110000, // 7
  0b01111111, // 8
  0b01111011  // 9
};

// SPI initialization
void SPI_Init() {
  // Set MOSI (PB5) and SCK (PB7) as output
  DDRB |= (1 << PB5) | (1 << PB7);
  // Set SS (PB4) as output
  DDRB |= (1 << PB4);
  // Enable SPI, Master mode, set clock rate fck/16
  SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0);
}

// Send a byte via SPI
void SPI_SendByte(uint8_t data) {
  SPDR = data;
  while (!(SPSR & (1 << SPIF))); // Wait for transmission complete
}

// Send a command to all MAX7219s
void MAX7219_Send(uint8_t address, int data) {
  SPI_SendByte(address);
  SPI_SendByte(data);
}

// Initialize MAX7219
void MAX7219_Init() {
  PORTB &= ~(1 << PB4); // SS low
  MAX7219_Send(0x09, 0x00);  // Decode mode: No decode for all digits
  MAX7219_Send(0x09, 0x00);  // Decode mode: No decode for all digits
  MAX7219_Send(0x09, 0x00);  // Decode mode: No decode for all digits
  PORTB |= (1 << PB4); // SS high

  PORTB &= ~(1 << PB4); // SS low
  MAX7219_Send(0x0B, 0x02);  // Scan limit: Display digits 0-2 (3 digits)
  MAX7219_Send(0x0B, 0x02);  // Scan limit: Display digits 0-2 (3 digits)
  MAX7219_Send(0x0B, 0x02);  // Scan limit: Display digits 0-2 (3 digits)
  PORTB |= (1 << PB4); // SS high

  PORTB &= ~(1 << PB4); // SS low
  MAX7219_Send(0x0C, 0x01);  // Shutdown: Normal operation
  MAX7219_Send(0x0C, 0x01);  // Shutdown: Normal operation
  MAX7219_Send(0x0C, 0x01);  // Shutdown: Normal operation
  PORTB |= (1 << PB4); // SS high

  PORTB &= ~(1 << PB4); // SS low
  MAX7219_Send(0x0F, 0x00);  // Display test: Off
  MAX7219_Send(0x0F, 0x00);  // Display test: Off
  MAX7219_Send(0x0F, 0x00);  // Display test: Off
  PORTB |= (1 << PB4); // SS high
}

// Display values on each of the 3-digit displays
void Display_Values(int number1, int number2, int number3) {
 static byte data=0;
 
  PORTB &= ~(1 << PB4); // SS low
  data = number > 99 ? pgm_read_word_near(numTable + (number1 / 100) % 10) : 0;
  MAX7219_Send(1, data);  // "1" on digit 0
  data = number > 99 ? pgm_read_word_near(numTable + (number2 / 100) % 10) : 0;
  MAX7219_Send(1, data);
  data = number > 99 ? pgm_read_word_near(numTable + (number3 / 100) % 10) : 0;
  MAX7219_Send(1, data);
  PORTB |= (1 << PB4); // SS high

  PORTB &= ~(1 << PB4); // SS low
  data = number > 9 ? pgm_read_word_near(numTable + (number1 / 10) % 10) : 0;
  MAX7219_Send(2, data);
  data = number > 9 ? pgm_read_word_near(numTable + (number2 / 10) % 10) : 0;
  MAX7219_Send(2, data);
  data = number > 9 ? pgm_read_word_near(numTable + (number3 / 10) % 10) : 0;
  MAX7219_Send(2, data);
  PORTB |= (1 << PB4); // SS high

  PORTB &= ~(1 << PB4); // SS low
  MAX7219_Send(3, pgm_read_word_near(numTable + number1 % 10));
  MAX7219_Send(3, pgm_read_word_near(numTable + number2 % 10));
  MAX7219_Send(3, pgm_read_word_near(numTable + number3 % 10));
  PORTB |= (1 << PB4); // SS high
}

int main() {
  SPI_Init();              // Initialize SPI
  MAX7219_Init();          // Initialize all MAX7219s

  while (1) {
    Display_Values(888, 888, 888); // Display "123", "456", "789" on the 3 displays
    _delay_ms(2000);     // Delay to observe
    Display_Values(111, 222, 333); 
    _delay_ms(2000);    
  }
}
1 Like

Which Core have you installed? Have yo loaded Bootloader into your ATmega32?

Are you programming it using Arduino IDE? If yes, then why are you not using setup(), loop() functions, and the SPI.h Libray?

I appreciate your help,
Output:
111
222
333

Then change to:
888
888
888

I am using Mightycore and yes the bootloader is burnt on the uC.

Yes, I am using Arduino IDE, and the Atmega32 is not supported by Arduino core libraries, so can't use these functions.