|
|
View previous topic :: View next topic |
Author |
Message |
evaradharaj
Joined: 15 Jan 2009 Posts: 60
|
Re: Multiplexing 7 segment displays |
Posted: Tue Nov 30, 2010 10:44 am |
|
|
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
|
|
Posted: Tue Nov 30, 2010 12:59 pm |
|
|
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
|
Re: code |
Posted: Tue Nov 30, 2010 10:25 pm |
|
|
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
|
|
Posted: Wed Dec 01, 2010 3:11 am |
|
|
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: 19537
|
|
Posted: Wed Dec 01, 2010 3:36 am |
|
|
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
|
Re: code |
Posted: Wed Dec 01, 2010 4:44 am |
|
|
Thanks for your reply Telmah.
I will do the changes in my code. _________________ embedding innovation in engineers |
|
|
tayoboy
Joined: 19 Mar 2010 Posts: 4
|
pls help me wit d code |
Posted: Thu Jan 12, 2012 3:12 am |
|
|
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
|
Flicker |
Posted: Fri Jan 13, 2012 5:05 am |
|
|
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
|
|
Posted: Fri Jan 13, 2012 7:19 am |
|
|
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
|
LED drivers |
Posted: Fri Jan 13, 2012 8:17 am |
|
|
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
|
|
Posted: Fri Jan 13, 2012 10:45 am |
|
|
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 |
|
|
|
|
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
|