CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Re: Multiplexing 7 segment displays

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
evaradharaj



Joined: 15 Jan 2009
Posts: 60

View user's profile Send private message Visit poster's website

Re: Multiplexing 7 segment displays
PostPosted: Tue Nov 30, 2010 10:44 am     Reply with quote

Hi,

I am developing a counter which will count till 9999. I am using 16f877a and 4 7 segment displays for this. I am multiplexing the 7 segment displays. Here I have given the code which is having the flickering problem. Please go through the code and give the suggestions to remove the flickering problem.
Code:

#include <16F877A.h>
#device adc=8
#use delay(clock=4000000)
#fuses NOWDT,XT, NOPUT, NOPROTECT, NODEBUG, BROWNOUT, LVP, NOCPD, NOWRT
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=9)

unsigned char value[10] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
int millisecs = 0,mpx = 0;
unsigned char shift = 0x00;
int count = 0;

unsigned char x,y,z,v,u,f,g;

#int_TIMER0
void timer0()
{
   set_timer0(190);    // the value 193 comes by crystal freq/4*16(div by 16)

   if(millisecs++ == 1000)
   {
      millisecs = 0;
      count++;
      if(count == 9999)
         count = 0;
   }
}


#int_TIMER1
void timer1()
{
   set_timer1(55000);    // the value 193 comes by crystal freq/4*16(div by 16)

   z = count / 1000;
   x = count % 10;
   v = count / 100;
   u = v % 10;
   f = count % 100;
   g = f / 10;
   mpx = mpx + 1;

   if(mpx == 1)
   {
         output_B(0x00);
         shift = 0x01;
         output_B(shift);
         output_D(value[z]);

   }
   else if(mpx == 2)
   {
         output_B(0x00);
         shift = 0x02;
         output_B(shift);
         output_D(value[u]);


   }
   else if(mpx == 3)
   {
         output_B(0x00);
         shift = 0x04;
         output_B(shift);
         output_D(value[g]);
         mpx =0;
   }

}


void main()
{
   int i;

   setup_counters(RTCC_INTERNAL,RTCC_DIV_16);
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_4);
   //set_tris_D(0x00);
   //port_b_pullups(TRUE);
   //set_tris_B(0x00);
   enable_interrupts(INT_TIMER1);
   enable_interrupts(INT_TIMER0);
   enable_interrupts(global);

   while (1)
   {

    }

}

Please see the attached circuit diagram in following address,

http://img686.imageshack.us/img686/4601/evrac.jpg

Thanks in advance,
Regards,
Varadharaj E
_________________
embedding innovation in engineers
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Nov 30, 2010 12:59 pm     Reply with quote

Quote:
#fuses NOWDT,XT, NOPUT, NOPROTECT, NODEBUG, BROWNOUT, LVP, NOCPD, NOWRT

Are you using a Low Voltage Programmer ? Maybe only one PIC user
in 500 does this. The ICD2, ICD3, Pickit2, Pickit3, CCS ICD-U40, etc.,
all are High voltage programmers. You should use NOLVP as the
fuse for these programmers.

Quote:

int count = 0;

z = count / 1000;
if(count == 9999)
count = 0;

In CCS (for the PCM compiler), an 'int' is 8 bits. It can only go from
0 to 255. Your code shown above won't work correctly with count
declared as an 'int'. To help you remember the size of the data types,
CCS has special names that you can use, such as int1, int8, int16, and
int32. It's a good idea to use those. Then you won't make mistakes like
the ones shown above.
evaradharaj



Joined: 15 Jan 2009
Posts: 60

View user's profile Send private message Visit poster's website

Re: code
PostPosted: Tue Nov 30, 2010 10:25 pm     Reply with quote

Thanks PCM programmer for your suggestions. I am using my own JDM type of programmer.

Is there any problem with my code functional wise?

regards,
Varadharaj E
_________________
embedding innovation in engineers
Wayne_



Joined: 10 Oct 2007
Posts: 681

View user's profile Send private message

PostPosted: Wed Dec 01, 2010 3:11 am     Reply with quote

The flickering is most likely down to the refresh rate (how many times a second your timer1 interrupts). I assume you are NOT latching the drives for the displays!

This is either because it is not interuppting fast enough (change its settings) but most likely and this will be more of a problem when you try to speed it up is that it is doing too much work. I know you only have the 2 interrupts and no code in main but if timer1 is not fast enough then your display will flicker.

Why have you implimented a state machine to only update 1 display at a time in your timer1 int?

A better method to do this would be to have 4 vars (an array works well) to hold the values for your displays, they are the values required to display the correct number not the count value, unless you have some how built it this way.

Your timer1 routine would JUST update the 4 displays by outputting these values. You can them trim the timing of timer1 to remove flicker.

All the other stuff for generating the correct values for the display vars would be done in your main loop.
Ttelmah



Joined: 11 Mar 2010
Posts: 19458

View user's profile Send private message

PostPosted: Wed Dec 01, 2010 3:36 am     Reply with quote

Potentially time....

Seriously, division by 10, with an int16, will take about 355uSec. You do this 3 times in the interrupt. Then the % operator, itself performs another division, used another 3 times. You are talking probably something like 3 to 4mSec for the arithmetic. Add the array accesses, and other bits and pieces, and this code could easily be taking something near to 5mSec to execute Now you are calling the interrupt about every 10000 instruction times (it is counting in instructions - OSC/4), and you are calling it potentially every 65536-55000 = 10536 instructions). So, if you want to do anything else in the main code, you could have a problem. Also, because you are using arithmetic in the interrupt, if you have any similar arithmetic in the main code, interrupts will be disabled during this...

Now, 'speed things up'. Think for the processor, and minimise the _time_ in the interrupt.
Why have two timers?.
Also, be aware that it takes time to get into an interrupt, so setting a timer 'to' a value, _will_ result in timing errors, as it will have already counted a bit. Your timer0 code, is trying to execute every 264 instructions. It takes typically something like sixty instruction times to get into and out of an interrupt. When timer1 triggers, the counter will be completely screwed. The flicker, is probably caused by a beat effect between the two interrupts.
On your chip, you have timer2, which can be set to trigger an interrupt at a nicer division. So, something like:
Code:

#include <16F877A.h>
#device adc=8
#use delay(clock=4000000)
#fuses NOWDT,XT, NOPUT, NOPROTECT, NODEBUG, BROWNOUT, LVP, NOCPD, NOWRT
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=9)

unsigned char value[10] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};

struct {
   int8 tenmillisec;
   int8 second;
   int8 sec10;
   int8 sec100;
   int8 sec1000;
} clock = {0,0,0,0,0};

#INT_TIMER1
void tick(void) {
   static int8 mpx=0; //display multiplexor
   clock.tenmillisec+=1;
   if (clock.tenmillisec>=100) {
      clock.tenmillisec=0;
      clock.second++;
      if (clock.second>=10) {
          clock.second=0;
          clock.sec10++;
          if (clock.sec10>=10) {
              clock.sec100++;
              clock.sec10=0;
              if (clock.sec100>=10) {
                  clock.sec1000++;
                  clock.sec100=0;
                  if (clock.sec1000>=10) clock.sec1000=0;
              }
          }
      }
   }
   switch (mpx) {
   case 0:
         mpx=1;
         output_B(0x00);
         shift = 0x01;
         output_B(shift);
         output_D(value[clock.sec1000]);
         break;
   case 1:
         mpx=2;
         output_B(0x00);
         shift = 0x02;
         output_B(shift);
         output_D(value[clock.sec100]);
         break;
   case 2:
         mpx=0;
         output_B(0x00);
         shift = 0x04;
         output_B(shift);
         output_D(value[clock.sec10]);
         break;
   }
}

void main() {

   setup_timer_2(T2_DIV_BY_4,250,10); //tick 100*/sec
   enable_interrupts(INT_TIMER2);
   enable_interrupts(global);

   while (1) {

   }
}


Now, you only show three digits being output, so I have done the same.

Key difference is that the arithmetic develops the digits. Each digit is a separate int8, while counts from 0 to 9, involving just a single increment, and then a test for it overflowing. If it overflows _and only if_, the next digit is incremented, and it too tested. For 9 out of ten interrupts, the single increment will use just a couple of uSec. On the tenth, this will probably about triple, then on the hundredth you will be up to perhaps 20uSec, and on the thousandth perhaps 30. The display code is in the same interrupt, and simply puts one of the already prepared digits out to the display.
You might think 'oh I could use an array to do this'. Yes, you could, but it'd take significantly longer. Pulling a digit from an array, takes something like 20uSec while just accessing a 'named' variable directly like this takes just a couple...
Key here is that even the worst case through this, is probably something like 5* faster than handling just a single division.....

Best Wishes
evaradharaj



Joined: 15 Jan 2009
Posts: 60

View user's profile Send private message Visit poster's website

Re: code
PostPosted: Wed Dec 01, 2010 4:44 am     Reply with quote

Thanks for your reply Telmah.

I will do the changes in my code.
_________________
embedding innovation in engineers
tayoboy



Joined: 19 Mar 2010
Posts: 4

View user's profile Send private message Send e-mail

pls help me wit d code
PostPosted: Thu Jan 12, 2012 3:12 am     Reply with quote

Hi, I commend Ttelmah and Wayne's contibution to this code, it has helped my understanding in multiplexing 7 segment display. But I have been working on the modified code for the past two weeks, the flickering problem persists. Please, I need suggestions and contributions.
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

Flicker
PostPosted: Fri Jan 13, 2012 5:05 am     Reply with quote

Quote:

Hi, I commend Ttelmah and Wayne's contibution to this code, it has helped my understanding in multiplexing 7 segment display. But I have been working on the modified code for the past two weeks, the flickering problem persists. Please, I need suggestions and contributions.


To achieve flicker free display you need to ensure:-

1) The refresh rate is high enough.
2) All digits are turned OFF as cathode drives are changed.
3) There is minimal variation in timing for each digit.
4) You have large enough decoupling capacitors on your supply rails.

1) Complete refreshing at above 50Hz (20ms) is usually sufficient. This means each digit is on for 5ms in turn. With a 4 digit display Ttelmah's code is going to refresh at 25Hz (3 digits at 33.3Hz). This is probably just on the slow side, try doubling the refresh rate, i.e. use 5ms interrupt rather than 10ms.

Code:

struct {
   int8 tenmillisec;
.
#INT_TIMER1
.
   clock.tenmillisec+=1;
   if (clock.tenmillisec>=100) {
      clock.tenmillisec=0;
.
void main() {
.
   setup_timer_2(T2_DIV_BY_4,250,10); //tick 100*/sec
.


Becomes

Code:

struct {
   int8 fivemillisec;
.
#INT_TIMER2     // NOT #INT_TIMER1
.
   clock.fivemillisec+=1;
   if (clock.fivemillisec>=200) {
      clock.fivemillisec=0;
.
void main() {
.
   setup_timer_2(T2_DIV_BY_4,250,5); //tick 200*/sec
.



2) As shown in the schematic the LEDs are common anode. Suppose you are driving 6 on one digit and 1 on the next. If you are not careful you get a faint version of the 6 showing on the 1. The way round is to turn off ALL the digit anode drives, change the the cathode drives, then turn on the next digit anode drive.

Code:


   switch (mpx) {
   case 0:
.
         output_B(shift);
         output_D(value[clock.sec1000]);
.
   case 1:
         mpx=2;
.
         output_B(shift);
         output_D(value[clock.sec100]);
.
   case 2:
         mpx=0;
.
         output_B(shift);
         output_D(value[clock.sec10]);
.



Becomes

Code:


   switch (mpx) {
   case 0:
.
         output_D(value[clock.sec1000]);
         output_B(shift);
.
   case 1:
         mpx=2;
.
         output_D(value[clock.sec100]);
         output_B(shift);
.
   case 2:
         mpx=0;
.
         output_D(value[clock.sec10]);
         output_B(shift);
.




3) You can achieve more stable timing by making the display refresh happen at the same time during the interrupt routine. The delay in getting into the interrupt routine is not important, provided it is the same lon each occurance. I find the simplest way is, do the refresh first, then other activities later. Suppose you were using this display as say a general purpose display, (eg. a voltmeter, or basic text messages) you may have other interrupts operating. You want the display refresh period to be rock solid. In this case you make the display refresh the only high priority interrupt and assign low priority to the others (if you're using a chip with high and low level interrupts, otherwise restrict yourself to the one interrupt and use it to set flags for the other routines to handle in main). Then, even if another interrupt is being serviced, the display is refreshed at the proper time.

4) Check for ripple with an oscilloscope.

Mike Walne
tayoboy



Joined: 19 Mar 2010
Posts: 4

View user's profile Send private message Send e-mail

PostPosted: Fri Jan 13, 2012 7:19 am     Reply with quote

Thanks mike for your help, there is improvement. Seems the flicker is at the verge of stopping. But right now there is another problem with the Proteus ide displaying some errors complaining of time imprecision, with abrupt interruption in the simulation.
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

LED drivers
PostPosted: Fri Jan 13, 2012 8:17 am     Reply with quote

Quote:

Thanks mike for your help, there is improvement. Seems the flicker is at the verge of stopping. But right now there is another problem with the Proteus ide displaying some errors complaining of time imprecision, with abrupt interruption in the simulation.


I don't have Proteus, and don't intend to get it, so can't help you.

Listen to the guys on this forum. Those who know what they're doing won't touch it with a barge pole. I use MPLAB SIM for initial software testing (MPLAB SIM does have minor issues with timings). The real testing is done on hardware with a trusty 'scope.

As a matter of preference I don't like driving multiplexed LEDs directly from a PIC. You simply can't get enough oomph! Using emitter followers results in brightness variation as the display digit changes (each segment of a 1 is usually brighter than say an 8). The way round this is to use common source drivers for both anodes and cathodes (or equivalent with FETs). For a slowly changing clock the effect may not be apparent, but it will be for other, more demanding, applications.

Mike
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Fri Jan 13, 2012 10:45 am     Reply with quote

Quote:
I don't like driving multiplexed LEDs

PERIOD in my case

i worked on ( at customer insistence) an LED mux display for a small specialized RF receiver -and the interference generated by the muxing
( through a 12 inch cable) was a really big bummer. ever since LCD's have been my best friend
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group