Multiplexing 7-segmentdisplay

Go To Last Post
18 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

I have a little problem with my program.

I want to show 1 to 4 on four separate 7 segment displays.

If I run the program on my uc and you turn the display upside down you will see the letter e.

I did not declared an e so what did i wrong with the coding?

Her is my program:

#include <avr/io.h>
 #include <avr/interrupt.h>

void display0(uint8_t nr)  // Display function
{
/*
       A          PB0
     F   B    PD3     PD7
       G          PD5
     E   C    PD4     PD6
       D          PD2
*/
	if (nr==1)
	{
		PORTD=PORTD|(1<<7)|(1<<6);  //segment B & C
	}
	if (nr==2)
	{
		PORTB=PORTB|(1<<0);                       //segment A
		PORTD=PORTD|(1<<7)|(1<<5)|(1<<4)|(1<<2);  //segment B, G, E en D
	}
	if (nr==3)
	{
		PORTB=PORTB|(1<<0);                       //segment A
		PORTD=PORTD|(1<<7)|(1<<6)|(1<<5)|(1<<2);  //segment B, G, E en D
	}
	if (nr==4)
	{
		PORTD=PORTD|(1<<7)|(1<<6)|(1<<5)|(1<<3);  //segment B, C, G en F
	}
}

void timer2_count() // Initialize counter timer 2
 {

 TCCR2A |= (1<<WGM21); // use Timer/Counter2 in CTC mode

 TCCR2B |= (1<<CS22)|(1<<CS21)|(1<<CS20);  // Prescaler 1024

 TCNT2 = 0; // Timer counter start at 0

 OCR2A = 124;  //Count to 124

 TIMSK2 |= (1<<OCIE2A);

 }

 ISR(TIMER2_COMPA_vect)
 {
	static int8_t X;

	for (X=0; X<=4; X++)
	{
		PORTC |= 0b00001111;  // initialize portc 0, 1, 2 and 3
		if (X == 1)
		{
			PORTC |= 0b00000001; // show portc0

		}
		if (X == 2)
		{
			PORTC |= 0b00000010;  // show portc1

		}
		if (X == 3)
		{
			PORTC |= 0b00000100;  // show portc2

		}
		if (X == 4)
		{
			PORTC |= 0b00001000;  // show portc3

		}

		if (X == 4)  // Make X zero again
		{
			X = 0;
		}
		display0(X); // show number defined in void display0
	}
 }

 int main(void)
 {
	DDRB=0b00001111;
	DDRC=0b00001111;
	DDRD=0b11111100;

	timer2_count();

	sei(); // globale interrupt

	while(1)
	{
		//do nothing!
	}
 }

Can somebody help me with a little hint?

 

This topic has a solution.
Last Edited: Sat. Oct 21, 2017 - 10:50 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'd say the problem is that every time you call display0() you or together the previous value of the output port with the new value and are leaving the segments on from the previous number(s).

 

Also, for readability, consider doing the following:

 

#DEFINE SEG_A   PB0
#DEFINE SEG_F   PD3
#DEFINE SEG_B   PD7
#DEFINE SEG_G   PD5
#DEFINE SEG_E   PD4
#DEFINE SEG_C   PD6
#DEFINE SEG_D   PD2

/*
         SEG_A            PB0
     SEG_F   SEG_B    PD3     PD7
         SEG_G            PD5
     SEG_E   SEG_C    PD4     PD6
         SEG_D            PD2
*/
    if (nr==1)
    {
        // Previously was
        // PORTD=PORTD|(1<<SEG_B)|(1<<SEG_C);  //segment B & C

        PORTD=(1<<SEG_B)|(1<<SEG_C);  //segment B & C
    }
    if (nr==2)
    {
        //Previously was
        //PORTB=PORTB|(1<<SEG_A);                                 //segment A
        //PORTD=PORTD|(1<<SEG_B)|(1<<SEG_G)|(1<<SEG_E)|(1<<SEG_D);//segment B, G, E en D

        PORTB=(1<<SEG_A);                                 //segment A
        PORTD=(1<<SEG_B)|(1<<SEG_G)|(1<<SEG_E)|(1<<SEG_D);//segment B, G, E en D
    }

    //... etc
    

 

Also - Consider this:

 

volatile uint8_t number_bit_mask_port_b[10];
volatile uint8_t number_bit_mask_port_d[10];

uint8_t number_bit_mask_port_b[0]=(1<<SEG_A);
uint8_t number_bit_mask_port_d[0]=(1<<SEG_F)|(1<<SEG_B)|(1<<SEG_E)|(1<<SEG_C)|(1<<SEG_D);

uint8_t number_bit_mask_port_b[1]=0;
uint8_t number_bit_mask_port_d[1]=(1<<SEG_B)|(1<<SEG_C);

uint8_t number_bit_mask_port_b[2]=(1<<SEG_A);
uint8_t number_bit_mask_port_d[2]=(1<<SEG_B)|(1<<SEG_G)|(1<<SEG_E)|(1<<SEG_D);

//etc

// Then display0() simplifies to nothing more than the following
void display0(uint8_t nr)  // Display function
{
    // Consider adding a check here to ensure nr < 10
    PORTB=number_bit_mask_port_b[nr];
    PORTD=number_bit_mask_port_d[nr];
}

 

Last Edited: Sun. Oct 15, 2017 - 12:45 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

First you'll have to verify that the hardware is ok. Maybe one of the led's is shorted, open, or the leds are connected in the wrong order, there are too many different pin layouts for 7 segment displays.

Then toy around a bit with the software. Write some routines to just blink one of the segments at a time. Write an animation to make a running light running around the outside of your display.

And then after some time you have the knowledge to debug your own code.

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

S_Tukker wrote:
If I run the program on my uc and you turn the display upside down

Why are you turning it upside down?!

 

you will see the letter e.

You mean this:

 -----
|     |
|     |
|     |
 -----
|
|
|
 -----

 

 

I did not declared an e

Presumably, your code is not intended to run "upside down";  so you wouldn't have declared it anyhow - would you?

 

Did you never play the game at school where you enter a number on your calculator, turn it upside down, and it says "hello"?

 

Of course, the calculator manufacturers never designed it to say "hello" - it's just a coincidence that 0.1134 on an upside down 7-segment display looks like "hEllO"

 

 

Anyhow, turning the above upside down gives

 -----
      |
      |
      |
 -----
|     |
|     |
|     |
 -----

Maybe it's supposed to be a 6, but you've got segments F & B swapped?

 

As the other s have said, the first thing to do is to just write some code to light each segment - to make sure that they all work, and that you have the wiring correct.

 

Then write code to test each "character" you want to print.

 

You could use the debugger (if your chip has one) or simulator to step through the code and see how the segments are getting lit ...

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:
Did you never play the game at school where you enter a number on your calculator, turn it upside down, and it says "hello"?
 

Yes i have done that and even all the the dutch words.

awneil wrote:
As the others have said, the first thing to do is to just write some code to light each segment - to make sure that they all work, and that you have the wiring correct.

The code for the numbers is correct because i copied it from a working program i already have tested counting the numbers from 0 to 9 on all the four displays.

Paulvdh wrote:
First you'll have to verify that the hardware is ok.

The hardware is ok, tested that with the counter program mentioned above.

 

I think the code in my interrupt is wrong but i do not know what.

If somebody can give me a hint that would be great.

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Did you read post #2?

SolarFreak wrote:
every time you call display0() you or together the previous value of the output port with the new value

and:

    if (nr==1)
    {
        // Previously was
        // PORTD=PORTD|(1<<SEG_B)|(1<<SEG_C);  //segment B & C

        PORTD=(1<<SEG_B)|(1<<SEG_C);  //segment B & C
    }
    if (nr==2)
    {
        //Previously was
        //PORTB=PORTB|(1<<SEG_A);                                 //segment A
        //PORTD=PORTD|(1<<SEG_B)|(1<<SEG_G)|(1<<SEG_E)|(1<<SEG_D);//segment B, G, E en D

        PORTB=(1<<SEG_A);                                 //segment A
        PORTD=(1<<SEG_B)|(1<<SEG_G)|(1<<SEG_E)|(1<<SEG_D);//segment B, G, E en D
    }

 

David (aka frog_jr)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

frog_jr wrote:
every time you call display0() you or together the previous value of the output port with the new value

I need to clear the previous befor calling the next one. 

 

Mis understood it the first time.

 

Thanks i will try that.

Last Edited: Wed. Oct 18, 2017 - 07:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

 

Finally it works.

I have added a reset function and that helped.

Now i have 1 to 4 in the display.

 

So thanks for all the help.

 

S

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

S_Tukker wrote:
Finally it works.

Great.

 

laugh

 

So please mark the solution: http://www.avrfreaks.net/comment...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

SolarFreak wrote:
I'd say the problem is that every time you call display0() you or together the previous value of the output port with the new value and are leaving the segments on from the previous number(s).

 

 

 #include <avr/io.h>
 #include <avr/interrupt.h>

void display0(uint8_t nr)  // Display function
{
/*
	   A          PB0
     F   B    PD3     PD7
       G          PD5
     E   C    PD4     PD6
       D          PD2
*/
	if (nr==1)
	{
		PORTD=PORTD|(1<<7)|(1<<6);  //segment B & C
	}
	if (nr==2)
	{
		PORTB=PORTB|(1<<0);                       //segment A
		PORTD=PORTD|(1<<7)|(1<<5)|(1<<4)|(1<<2);  //segment B, G, E en D
	}
	if (nr==3)
	{
		PORTB=PORTB|(1<<0);                       //segment A
		PORTD=PORTD|(1<<7)|(1<<6)|(1<<5)|(1<<2);  //segment B, G, E en D
	}
	if (nr==4)
	{
		PORTD=PORTD|(1<<7)|(1<<6)|(1<<5)|(1<<3);  //segment B, C, G en F
	}
}
 void Delay(uint16_t ms)
 {
	 uint16_t i;
	 uint16_t total;

	 for (total=0; total < ms ;++total)
	 {
		 for (i=0;i<840;++i)
		 {
		 }
	 }
 }
void timer2_count() // Initialize counter timer 2 
 {

	TCCR2A |= (1<<WGM21); // use Timer/Counter2 in CTC mode
 
	TCCR2B |= (1<<CS22)|(1<<CS21)|(1<<CS20);  // Prescaler 1024
 
	TCNT2 = 0; // Timer counter starts at 0
 
	OCR2A = 50;  //Count to 75
 
	TIMSK2 |= (1<<OCIE2A);
 
 }
void reset(uint16_t R)
{
	if (R==1)
	{	
		PORTB &= ~ (1<<0);
		PORTD= 0b00000000;
		PORTC &=~ (1<<0);
		PORTC &=~ (1<<1);
		PORTC &=~ (1<<2);
		PORTC &=~ (1<<3);
	}
}
int counter;
ISR(TIMER2_COMPA_vect)
{
	
	
	static int segment;
	static int number;
	counter++;
	if (counter==1)
	{
		number=1;
		
		for (segment=0;segment<4;segment++)
		{
			display0(number);
			PORTC=PORTC|(1<<segment);
			Delay(5);
			reset(1);
			number++;
			if (number>4)
			{
				number=0;
			}
		}
		counter=0;
		
	}
}

 int main(void)
 {
	DDRB=0b00001111;
	DDRC=0b00001111;
	DDRD=0b11111100;
 
	timer2_count();
 
	sei(); // globale interrupt
 
	while(1)
	{
		//do nothing!
	}
 }

For completion here is the code that works.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

The general idea is that you display only one digit for each timer tick. You display all digits each timer tick and you use a delay - the main reason to use a timer is to avoid using delays! Changing your code to display one digit per tick will make the code simpler and faster and should result in your digits being brighter.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
The general idea is that you display only one digit for each timer tick. You display all digits each timer tick and you use a delay - the main reason to use a timer is to avoid using delays! Changing your code to display one digit per tick will make the code simpler and faster and should result in your digits being brighter.

I deleted the delay and changed the ISR code into this;

ISR(TIMER2_COMPA_vect)
{

	static uint8_t msec4_count = 0;
	if (msec4_count++ > 50)
	{
		msec8_count = 0;

			display0(1);
			reset(1);

	}
}

result is 1234.

 

Is this what you meant Kartman?

Last Edited: Sun. Oct 22, 2017 - 09:58 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Not sure - i can't make sense of your code. Why would you count 50 ticks then update the digits? One tick, one digit.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
Not sure - i can't make sense of your code. Why would you count 50 ticks then update the digits? One tick, one digit.

 

There was something wrong and i fixed it

 

int count
ISR(TIMER2_COMPA_vect)
{

	static int segment;
	static int number;
	count++;
	
	if (count==5)
	{
		
		display0(cijfer+1);
		PORTC=PORTC|(1<<segment);
		Delay(1);
		reset(1);
		number++;
		segment++;
		
		
		if (number>4)
		{
			number=0;
		}
		if (segment>4)
		{
			segment=0;
		}
		
		count=0;
		
	}
}

now it is working fine

Thanks for your help kartman.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Err no. Why the Delay(1)?
You’ve got things reversed - disable the previous digit, output the new segment data then select the new digit. The timer provides the delay.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok i have it.

The delay and the count are out here the latest version off the code:

ISR(TIMER2_COMPA_vect)


{
	static int segment=0;
	static int number=1;
	
	reset();
	display0(number);
	PORTC=PORTC|(1<<segment);
		
	number++;
	segment++;
		
		
	if (number>4)
	{
		number=1;
	}
	if (segment>3)
	{
		segment=0;
	}
}

Thanks for your help.

S

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Almost too simple now! With the AVR being an 8bit micro, use the appropriate variable sizes for speed and use of memory. In your case, segment and number should be uint8_t rather than int (which is 16 bit) as those variables wont hold a value > 255.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
 segment and number should be uint8_t rather than int (which is 16 bit) as those variables wont hold a value > 255.

Also, int is signed - but you will never need a negative segment number!