## Problem with a 16x16 led matrix and its luminosity

4 posts / 0 new
Author
Message

I'm a college student and I'm attempting to make the "snake game" on a 16x16 led matrix.

I'm using the Atmel Studio to do the coding and a usbasp to programme the microcontroller (atmega88).

Rigth now im just trying to understand the matrix that uses 2 decoders  74HC138 (for lines) and 2 shift registors  shift register (for the columms).

Here is the link for the matrix: https://www.elabpeers.com/led-matrix-display.html

The problem is that the first column of the matrix is brigther than the others ones and I dont know why.

This is the code that im using: (adapted from here https://www.hackster.io/Treebug842/16x16-led-matrix-display-012b1e)

#define F_CPU 1000000UL        // 1MHZ (default fuses)
#define D PINC0
#define C PINC1
#define B PINC2
#define A PINC3
#define G PINC4
#define DI PINC5
#define CLK PIND6
#define LAT PIND7
#define MATRIZ_PORTC	PORTC
#define MATRIZ_DDRC 	DDRC
#define MATRIZ_PORTD	PORTD
#define MATRIZ_DDRD		DDRD

#include <avr/io.h>
#include <stdio.h>
#include <stdlib.h>
#include <util/delay.h>
#include <avr/interrupt.h>

void MatrizToWord(); //Matrix to word
void Scan_Line(unsigned char m); //wich line
void Send(unsigned char dat); //serial data to matrix
void Clear_screen();
void inic();

unsigned char Display_Buffer[2];

unsigned int Word1[32];

//here you'll draw the matrix in your screen
unsigned char  Matriz[16][16] =
{
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
};

void inic(void)
{
MATRIZ_DDRC = 0xBF;
MATRIZ_PORTC |= (1<<G);							// G=1
MATRIZ_DDRD |= (1<<CLK) | (1<<LAT);

}
void MatrizToWord()
{
int i, k; //key = 0; dunno what this key variable was there for
unsigned int value;
for(i = 0; i < 16; i++)
{
for(k = 0; k < 16; k++)
{
if(i < 8)
{
value = Matriz[i][k] << (7 - i);
Word1[15 - k] += value;
}
else
{
value = Matriz[i][k] << (15 - i);
Word1[31 - k] += value;
}
}
}
}

void Display(unsigned int dat[])
{
unsigned char i;

for( i = 0 ; i < 16 ; i++ )
{
MATRIZ_PORTC |=  (1<<G);		// G = HIGH

Display_Buffer[0] = dat[i];	// store first 8 bits
Display_Buffer[1] = dat[i+16];	//store last 8 bits

Send(Display_Buffer[1]);		// screen upper part
Send(Display_Buffer[0]);		// screen lower part

MATRIZ_PORTD |=  (1<<LAT);      // LATCH = HIGH
_delay_us(1);

MATRIZ_PORTD &= ~(1<<LAT);      // LATCH = LOW
_delay_us(1);

Scan_Line(i);					// call this to change lines

MATRIZ_PORTC &= ~(1<<G);        // G = LOW

_delay_us(100);
}

}

void Scan_Line(unsigned char m)
{
switch(m)
{
case 0:
MATRIZ_PORTC &= ~((1<<D) | (1<<C) |(1<<B)|(1<<A));
//digitalWrite(LEDARRAY_D, LOW);digitalWrite(LEDARRAY_C, LOW);digitalWrite(LEDARRAY_B, LOW);digitalWrite(LEDARRAY_A, LOW);
break;
case 1:
MATRIZ_PORTC &= ~((1<<D) | (1<<C) |(1<<B));
MATRIZ_PORTC|=(1<<A);
//digitalWrite(LEDARRAY_D, LOW);digitalWrite(LEDARRAY_C, LOW);digitalWrite(LEDARRAY_B, LOW);digitalWrite(LEDARRAY_A, HIGH);
break;
case 2:
MATRIZ_PORTC &= ~((1<<D) | (1<<C) |(1<<A));
MATRIZ_PORTC |=(1<<B);
//digitalWrite(LEDARRAY_D, LOW);digitalWrite(LEDARRAY_C, LOW);digitalWrite(LEDARRAY_B, HIGH);digitalWrite(LEDARRAY_A, LOW);
break;
case 3:
MATRIZ_PORTC &= ~((1<<D) | (1<<C));
MATRIZ_PORTC |=(1<<B)|(1<<A);
//digitalWrite(LEDARRAY_D, LOW);digitalWrite(LEDARRAY_C, LOW);digitalWrite(LEDARRAY_B, HIGH);digitalWrite(LEDARRAY_A, HIGH);
break;
case 4:
MATRIZ_PORTC &= ~((1<<D)  |(1<<B)|(1<<A));
MATRIZ_PORTC |=(1<<C);
//digitalWrite(LEDARRAY_D, LOW);digitalWrite(LEDARRAY_C, HIGH);digitalWrite(LEDARRAY_B, LOW);digitalWrite(LEDARRAY_A, LOW);
break;
case 5:
MATRIZ_PORTC &= ~((1<<D) |(1<<B));
MATRIZ_PORTC |=(1<<C)|(1<<A);
//digitalWrite(LEDARRAY_D, LOW);digitalWrite(LEDARRAY_C, HIGH);digitalWrite(LEDARRAY_B, LOW);digitalWrite(LEDARRAY_A, HIGH);
break;
case 6:
MATRIZ_PORTC &= ~((1<<D)|(1<<A));
MATRIZ_PORTC |=(1<<C) |(1<<B);
//digitalWrite(LEDARRAY_D, LOW);digitalWrite(LEDARRAY_C, HIGH);digitalWrite(LEDARRAY_B, HIGH);digitalWrite(LEDARRAY_A, LOW);
break;
case 7:
MATRIZ_PORTC &= ~((1<<D));
MATRIZ_PORTC |=(1<<C) |(1<<B)|(1<<A);
//digitalWrite(LEDARRAY_D, LOW);digitalWrite(LEDARRAY_C, HIGH);digitalWrite(LEDARRAY_B, HIGH);digitalWrite(LEDARRAY_A, HIGH);
break;
case 8:
MATRIZ_PORTC &= ~((1<<C) |(1<<B)|(1<<A));
MATRIZ_PORTC |=(1<<D);
//digitalWrite(LEDARRAY_D, HIGH);digitalWrite(LEDARRAY_C, LOW);digitalWrite(LEDARRAY_B, LOW);digitalWrite(LEDARRAY_A, LOW);
break;
case 9:
MATRIZ_PORTC &= ~((1<<C) |(1<<B));
MATRIZ_PORTC |=(1<<D)|(1<<A);
//digitalWrite(LEDARRAY_D, HIGH);digitalWrite(LEDARRAY_C, LOW);digitalWrite(LEDARRAY_B, LOW);digitalWrite(LEDARRAY_A, HIGH);
break;
case 10:
MATRIZ_PORTC &= ~( (1<<C)|(1<<A));
MATRIZ_PORTC |=(1<<D)|(1<<B);
//digitalWrite(LEDARRAY_D, HIGH);digitalWrite(LEDARRAY_C, LOW);digitalWrite(LEDARRAY_B, HIGH);digitalWrite(LEDARRAY_A, LOW);
break;
case 11:
MATRIZ_PORTC &= ~((1<<C));
MATRIZ_PORTC |=(1<<D)|(1<<B)|(1<<A);
//digitalWrite(LEDARRAY_D, HIGH);digitalWrite(LEDARRAY_C, LOW);digitalWrite(LEDARRAY_B, HIGH);digitalWrite(LEDARRAY_A, HIGH);
break;
case 12:
MATRIZ_PORTC &= ~((1<<B)|(1<<A));
MATRIZ_PORTC |=(1<<D) | (1<<C);
//digitalWrite(LEDARRAY_D, HIGH);digitalWrite(LEDARRAY_C, HIGH);digitalWrite(LEDARRAY_B, LOW);digitalWrite(LEDARRAY_A, LOW);
break;
case 13:
MATRIZ_PORTC &= ~((1<<B));
MATRIZ_PORTC |=(1<<D) | (1<<C)|(1<<A);
//digitalWrite(LEDARRAY_D, HIGH);digitalWrite(LEDARRAY_C, HIGH);digitalWrite(LEDARRAY_B, LOW);digitalWrite(LEDARRAY_A, HIGH);
break;
case 14:
MATRIZ_PORTC &= ~((1<<A));
MATRIZ_PORTC |=(1<<D) | (1<<C) |(1<<B);
//digitalWrite(LEDARRAY_D, HIGH);digitalWrite(LEDARRAY_C, HIGH);digitalWrite(LEDARRAY_B, HIGH);digitalWrite(LEDARRAY_A, LOW);
break;
case 15:
MATRIZ_PORTC |=(1<<D) | (1<<C) |(1<<B)|(1<<A);
//digitalWrite(LEDARRAY_D, HIGH);digitalWrite(LEDARRAY_C, HIGH);digitalWrite(LEDARRAY_B, HIGH);digitalWrite(LEDARRAY_A, HIGH);
break;
default : break;
}
}

//
void Send(unsigned char dat)
{
unsigned char i;
MATRIZ_PORTD &= ~(1<<CLK);		   // CLK a 0;
_delay_us(1);
MATRIZ_PORTD &= ~(1<<LAT);   // LATCH = LOW (A latch low alows data sending)
_delay_us(1);

for( i = 0 ; i < 8 ; i++ )
{
if( dat & 0b00000001 )
{
MATRIZ_PORTC |= (1<<DI);   //  DI (data input) a 1;
}
else
{
MATRIZ_PORTC &= ~(1<<DI);  // DI (data input) a 0;
}

_delay_us(1);
MATRIZ_PORTD |= (1<<CLK);      // CLK  1;
_delay_us(1);
MATRIZ_PORTD &= ~(1<<CLK);     //  CLK  0;
_delay_us(1);
dat	>>= 1;

}
}
void Clear_screen(){
for(unsigned char i = 0; i < 32; i++)
Word1[i] = 0;
}

int main(void)
{
inic();
while (1)
{
Clear_screen();
MatrizToWord();
Display(Word1);
}
}

Could it be because of the variable key=0 from the MatrizToWord function??

HI Pauolo,

Welcome to the forum, concgrats with your first post :)

Difference in brightness of columns / rows in a multiplexed display is almost always because of timing issues.

Make sure each row / column is shown the same amount of time.

In the funcion:

Paulo Silva wrote:
void Display(unsigned int dat[])

You seem to write the whole display, with delays of 100us for each row/column, and when you exit (probably) the last row/column remains on the display untill the display is updated again.

Using all those _delay() functions everywere is an sort of easy approach, but will not give optimal results.

Most are pretty small ( 1us) (Or at least they should be 1us, if your timing is right, have you checked that?) and those small delays are pretty harmless and sometimes even neccasarry when latching data into external chips, I tend to remove them completely (after debugging) or replace them with a single or a few nop's.

A delay of 16x 100us in a loop is a significant amount of time however and this programming style will get you into trouble once your programs get more complex.

An often used approach for displays is to have a regular timer tick interrupt, and have that interrupt read one row or column from a buffer and output it to the hardware.

To be absolutely perfect the delay between the start of the interrupt and the output of the data to the hardware should be constant.

The easiest way to accomplish that is to have a pointer into your data buffer.

The ISR then starts with reading data from wherever that pointer points to, and output it to the display. Then the "critical" part is over, and the ISR can spend a variable amount of timing to pre-calculate the pointer value for thenext iteration.

Working reliably with interrupts in C is a bit of a challenge for beginners. You have to use "volatile" in the right places (shared data between ISR and main code) and data which is used in the ISR only but must be persistent between calls (such as the display pointer) must be declared "static" in the ISR.

Also:

In your scanline switch in most cases you are setting and clearing bits, except in cases 0 and 15. This lack of symmetry is ... a point of concern, but I have not looked too deep into the code.

That switch is also always writing to the same port. It seems more logical to read the data that has to be written to that port from an array instead of using a switch here. This makes your code easier to read and it also ensures that each line is handled in the same way.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

Hi Paul!

I'll try to do as you suggested and use a regular timer tick interrupt for the data output to the display.