Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Multiple digits 7 segments display

Status
Not open for further replies.

Joel Rainville

New Member
I am working on my first somewhat "elaborate" PIC project, a 7 segment display library on a PIC16F877A.

The display I am using is a 99¢ 9 digits 7 segments LED display (with small clear bubbles over the LEDs wich presumably act as lenses to magnify the tiny LEDs?)

The digits share a common anode, so that I need to select them one-by-one by connecting their cathode to ground, and applying a small +5V current to each segment needed to display digits.

9 digits, so that makes 9 pins for selecting digits, and 8 pins for the 7 segments and the dot. 17 pins total. Still following?

In order to avoid using 17 pins on the PIC, I use a 16 pin 4028 BCD decoder, so that I can choose digits using only 4 pins on the PIC. The BCD takes the decimal value from the 4 pins and outputs the binary value on 10 different pins. Got it?

I need to sink current (remember, to select a digit, I need to connect its cathode to ground), and while the PIC can do that, the 4028 cannot, so I take the 4028's output and feed it to 9 different 2SC2458 transistors, which in turn let current flow through ground when they get the signal from the 4028.

The segments on the other hand simply get their current from the PIC's PORTD pins (all 8 of them, 1 for each segment).

My code does the following :

1- turn all segments off
2- select active digit
3- turn segments on
4- loop for all digits

Now, my problem is : something in the circuit is not acting fast enough. If I let my code run at full speed, I get all 8s (with dots) on the display. I have to insert a sgnificant delay between steps 3 and 4 to get a proper display.

I think the 4028 is at fault here.

The PIC's oscillator is 8MHz, so it's running at 2MHz in "instruction cycles".
The 2SC2458 can switch at a rate of 80MHz.
The 4028, at 5V, 25 degrees Celsius has a "Max Propagation Delay Time" of 480ns and "Max Transition Time" of 350ns, which if I read this correctly translates to a total switching rate (off-on-off) of 1.2MHz.

So my theory is that the 4028, when the PIC's pin goes low, is still feeding the transistor for a few ns, which translates into 2 digits being lit at the same time for a few ns.

When I insert a delay time long enough to let the 4028 go low, everything's fine.

What do you think? Am I right or mistaken? Is there something else that could explain my "problem"?

Thanks for bearing with me, that's a seriously long *ss post!
 
Update : :roll: :lol:

I do not necessarily get all segments lit (all 8s) when there's no delay. As I suspected, I get the previous LED's segments lit in the next digit which I can see with an intermediate delay time, which is fast enough to display proper digits brightly and unwanted segments very dim.

But still, my 4028 theory might not be entirely true...

Any thoughts?
 
Just add in the delays. VGA monitors refresh at typical rates of 80fps and the human eye is often comfortable with that. There is no point in having ultra-high refresh rates.
 
checkmate said:
Just add in the delays. VGA monitors refresh at typical rates of 80fps and the human eye is often comfortable with that. There is no point in having ultra-high refresh rates.

You're right. But unless the PIC is used solely as a LED driver, I am wasting precious cycles here, which could be used elsewhere...

In any case, I am not looking for a solution as much as an explanation. I prefer to understand something that doesn't work, than not understand something that works, if that makes any sense :D

The elegant solution would be to replace the delays with timer reads. If < x cycles have passed, do not refresh, else refresh. The very elegant solution would first check if the desired timer is already started, as to not tie up that particular timer solely for LCD-display purposes. If already used, fine, just read values and compute elapsed time, if not start timer, read and compute. I just need to learn how to use/start/read timers on the PIC :wink:

But I am still wondering whose guilty here? The 4028? The transistors? My ignorance?
 
What you are doing is called LED multiplexing, a common trick in microcontroller programming. When I said delays, I don't mean the uC doing nothing but waiting for the specified number of cycles to pass. You could use a timer. Or you could carry on normal processing, and update the display at the end of every process, as long as the processes don't take too long. The refresh interval doesnt even have to be strictly the same.

The crux is, as long as the display is refreshed at reasonable refresh intervals, it will look fine. The human vision has memory effect. You can't see the axles of a spinning wheel.
 
Joel Rainville said:
checkmate said:
Just add in the delays. VGA monitors refresh at typical rates of 80fps and the human eye is often comfortable with that. There is no point in having ultra-high refresh rates.

You're right. But unless the PIC is used solely as a LED driver, I am wasting precious cycles here, which could be used elsewhere...

In any case, I am not looking for a solution as much as an explanation. I prefer to understand something that doesn't work, than not understand something that works, if that makes any sense :D

You use a timer interrupt to do the multiplexing of the display, check my seven segment tutorial for an example. The tutorial only uses two displays, but the principle is exactly the same.

This frees the processor to do other work, and the display is taken care of transparently by the interrupts.
 
Nigel Goodwin said:
Joel Rainville said:
checkmate said:
Just add in the delays. VGA monitors refresh at typical rates of 80fps and the human eye is often comfortable with that. There is no point in having ultra-high refresh rates.

You're right. But unless the PIC is used solely as a LED driver, I am wasting precious cycles here, which could be used elsewhere...

In any case, I am not looking for a solution as much as an explanation. I prefer to understand something that doesn't work, than not understand something that works, if that makes any sense :D

You use a timer interrupt to do the multiplexing of the display, check my seven segment tutorial for an example. The tutorial only uses two displays, but the principle is exactly the same.

This frees the processor to do other work, and the display is taken care of transparently by the interrupts.

Thanks Nigel, but I prefer that my library use as little resources as possible. And while I haven't played with timer interrupts yet, this is typically something more costly than just reading a timer's value at each processing "cycle" or iteration, don't you think? I don't want to use interrupts in the library and add that overhead to a project that might otherwise not need them...

I am not looking for a solution as much as an explanation. :wink: :wink: :wink:
 
checkmate said:
you could carry on normal processing, and update the display at the end of every process, as long as the processes don't take too long. The refresh interval doesnt even have to be strictly the same.

The problem here is not that the process takes too long, but that it could take too short of a time, so that's why I was talking about reading a timer's value (without using interrupts) at every processing iteration and refresh the display only when n time has passed.

Thanks to your input, I now know this is called multiplexing.

But really, that is not what I am asking in this thread! :wink: :wink: :wink: The software part, I can deal with. It's the electrical/electronics part that I'm pretty clueless about... Anyone?
 
Joel Rainville said:
Thanks Nigel, but I am trying to be as efficient as possible here, and while I haven't played with timer interrupts yet, this is typically something more costly than just reading a timer's value at each processing "cycle" or iteration.

I am not looking for a solution as much as an explanation. :wink: :wink: :wink:
This is entirely wrong, as you have not understood the advantages of interrupts.

Well, you have already stated out the problem, which is the PIC requires one instruction cycle to select the 7-segment, then update it's value in the next. It cannot do both tasks in the same instruction cycle, which is the reason why the display looks strange as your process gets shorter. So, what else do you exactly need to be explained about?
 
Joel Rainville said:
Thanks Nigel, but I prefer that my library use as little resources as possible. And while I haven't played with timer interrupts yet, this is typically something more costly than just reading a timer's value at each processing "cycle" or iteration, don't you think? I don't want to use interrupts in the library and add that overhead to a project that might otherwise not need them...

The use of timer interrupts is going to far more efficient than not using them, and a MUCH better use of available resources.

If you look back through previous posts you will find that I'm NOT a believer in using interrupts just because they are there, but for this use it gives tremendous advantages and no disadvantages.

Check my tutorial for how simple it is!.

You also ask about hardware?, again check my tutorial hardware, although it's only two digits the principle is the same. Because it's only two, I use a single pin to select either one, this obviously can't be done with more than two digits - so you would need a seperate I/O pin for each digit.
 
checkmate said:
This is entirely wrong, as you have not understood the advantages of interrupts.

Not only do I understand the advantages of interrupts, I do know their disadvantages as well. The "Complete Mid-Range Reference Manual" gives a good example of the cost in instruction cycles when using interrupts. 10 cycles are wasted by saving registers, interrupt latency, and restoring registers.

As I said, I do not want to add that overhead in a library.


checkmate said:
the PIC requires one instruction cycle to select the 7-segment, then update it's value in the next. It cannot do both tasks in the same instruction cycle, which is the reason why the display looks strange as your process gets shorter.

The fact that it cannot do both tasks in the same instruction cycle doesn't explain anything. This makes no sense, to me at least.

Here's what my code does, in your terms this time :

1- set all segments value to 0
2- select 7 segment
3- update value
4- loop

What I want to understand is why I need a delay between steps 3 and 4. I don't think your theory's right. My theory is that the 4028 isn't switching fast enough. What do you think?
 
Joel Rainville said:
Not only do I understand the advantages of interrupts, I do know their disadvantages as well. The "Complete Mid-Range Reference Manual" gives a good example of the cost in instruction cycles when using interrupts. 10 cycles are wasted by saving registers, interrupt latency, and restoring registers.

As I said, I do not want to add that overhead in a library.
Good, since you are clear on interrupts. The crux is that all these interrupt delays only occur once, when the interrupt occurs. If interrupts occur rarely in relative terms, its cpu load is minimal. The other scheme which you mentioned, is called polling. Here, the overheads of polling are enforced every iteration, and is feasible only if the condition is satisfied extremely frequently in relative terms.

Joel Rainville said:
The fact that it cannot do both tasks in the same instruction cycle doesn't explain anything. This makes no sense, to me at least.

Here's what my code does, in your terms this time :

1- set all segments value to 0
2- select 7 segment
3- update value
4- loop

What I want to understand is why I need a delay between steps 3 and 4. I don't think your theory's right. My theory is that the 4028 isn't switching fast enough. What do you think?
Everything is explained in that single additional cycle. During that cycle, the selected display is updated with the value of the previous display, for that cycle only, before it gets refreshed in the next cycle. If the process is too short, this single cycle glitch may form a siginificant fraction of total refresh time, and this glitch will be more apparent. If you want a strictly glitch-free output, you may latch the display inputs. But this is not used in practise, because latches cost money, and that the human eye is not that sensitive.

I haven't read the datasheet for 4028, but I'm pretty sure that the 4028 can operate at much higher frequencies than the PIC itself.
 
Joel Rainville said:
Nigel Goodwin said:
The use of timer interrupts is going to far more efficient than not using them, and a MUCH better use of available resources.

Nigel, see my previous post. Am I mistaken about interrupts' cycles cost?

No, but it's far less than any other method - you are incorrect what you consider a 'waste' of resources.

Using a timer and sitting waiting for it to time out wastes the entire time period of the timer, which should be in the 10's of milliseconds - a few 10's of microseconds are around a thousand times more efficient.

The display should multiplex at a fast enough rate to eliminate visible flicker, but it doesn't want to go too fast - my tutorial code uses a 16mS refresh rate. You may well have to increase the refresh rate because of your higher number of digits, but it's as simple as altering one byte written to the timer. With interrupts you should have well in the high 90% of free processor time for your main program.

It's important (VERY important) that the display refresh is both regular, continuous, and the same all the time (to prevent flicket and uneven brightness across the display). The timer interrupt routine provides this, it's VERY difficult to arrange it otherwise, if you want the program to do anything else.
 
First of all, thanks for not losing your patience ;) I appreciate :D

checkmate said:
Everything is explained in that single additional cycle. During that cycle, the selected display is updated with the value of the previous display, for that cycle only, before it gets refreshed in the next cycle. If the process is too short, this single cycle glitch may form a siginificant fraction of total refresh time, and this glitch will be more apparent. If you want a strictly glitch-free output, you may latch the display inputs. But this is not used in practise, because latches cost money, and that the human eye is not that sensitive.

I understand what you're saying now. I have seen this to be true when step 1 wasn't there. But since I am turning off every segments before going to the next LEDs, shouldn't that resolve that issue?

checkmate said:
I haven't read the datasheet for 4028, but I'm pretty sure that the 4028 can operate at much higher frequencies than the PIC itself.

I have read the datasheet, but can't seem to find anything speed-related, except for the attached information, which just looks like chinese translated to japanese and back to chinese to me ;) I have tried to figure out the 4028's minimum operating frequencies using these numbers, that's where I came up with the 1.2MHz figure in my first post...

Maybe I should have started this thread simply by asking what those numbers mean! :D
 

Attachments

  • 4028_excerpt.png
    4028_excerpt.png
    14.5 KB · Views: 1,075
Nigel Goodwin said:
Using a timer and sitting waiting for it to time out wastes the entire time period of the timer, which should be in the 10's of milliseconds - a few 10's of microseconds are around a thousand times more efficient.

I understand that this would very inefficient. This is not what I am saying.

The way I see it :

Code:
- do normal processing
- read timer value
- compare to previous value 
  - Update LEDs if time elapsed long enough
  - or get back to normal processing for another iteration
 
Joel Rainville said:
Nigel Goodwin said:
Using a timer and sitting waiting for it to time out wastes the entire time period of the timer, which should be in the 10's of milliseconds - a few 10's of microseconds are around a thousand times more efficient.

I understand that this would very inefficient. This is not what I am saying.

The way I see it :

Code:
- do normal processing
- read timer value
- compare to previous value 
  - Update LEDs if time elapsed long enough
  - or get back to normal processing for another iteration

How often would you be checking the timer?, I suspect whatever speed you do so is going to make it grossly inefficient, or result in a very poor display. It's also going to make your main program far more complicated, as you have to arrange for frequent jumps to check for the timer and process any required refresh.
 
Nigel Goodwin said:
Joel Rainville said:
Nigel Goodwin said:
Using a timer and sitting waiting for it to time out wastes the entire time period of the timer, which should be in the 10's of milliseconds - a few 10's of microseconds are around a thousand times more efficient.

I understand that this would very inefficient. This is not what I am saying.

The way I see it :

Code:
- do normal processing
- read timer value
- compare to previous value 
  - Update LEDs if time elapsed long enough
  - or get back to normal processing for another iteration

How often would you be checking the timer?, I suspect whatever speed you do so is going to make it grossly inefficient, or result in a very poor display. It's also going to make your main program far more complicated, as you have to arrange for frequent jumps to check for the timer and process any required refresh.

The main program just calls (jumps at) a subroutine (led7_refresh in my code) every iteration. That subroutine checks the timer and returns. 3 jumps, 6 cycles.

I will get a poor display only if the main program's processing takes too long... It is my understanding that as long as the refresh rate is high enough (say 90Hz), it shouldn't flicker, even if it's not regular, right? One of your preivous posts seems to state the opposite. Irregular refresh rates is seen among toher things in games on consoles and PCs, but as long as it's high enough (and in sync with the monnitor's refresh rate), nothing flickers...

I think I will implement both an interrupt solution and a timer polling one, and let the library user choose the preferred method based on the main program's load. This will be a good learning exercise.

Thanks for your time (and your website!), I appreciate the help and the passing of information you are dedicating yourself to.
 
Hi Joel,

When multiplexing 7-segment displays you need to be concerned with duty cycle and refresh rate...

Duty cycle is the portion of time each digit is turned on in one complete cycle... If you have an 8-segment 9-digit display and you're turning on digits sequencially, your duty cycle is approximately 11% (1/9)... If you're turning on segments sequencially, your duty cycle is approximately 12.5% (1/8)... The higher the duty cycle the better the brightness... Don't go less than 10% duty cycle...

Refresh rate is the frequency at which you're updating the whole display... If for example you turn on each digit for 2-msecs (the time between your steps 3 and 4), it will take 18-msecs to scan the entire display and you'll realize (1.0-sec/0.018-sec) a 55-Hz refresh rate... Anything less than about a 50-Hz refresh rate will usually result in display flicker...

Using Timer 2 generated 1-msec interrupts and an interrupt service routine to scan the display works very well... Yes, you have overhead in the ISR but scanning the display becomes almost transparent and your main program need only update a nine byte display buffer... I think you'll find ISR "overhead" inconsequential... With 2000 instructions between 1-msec interrupts (8-MHz clock), you'll probably only use 30-50 of those 2000 instructions in your ISR...

Hope this helps... Regards, Mike

PS: where did you get this little 99 cent 9-digit display?
 
Joel Rainville said:
The main program just calls (jumps at) a subroutine (led7_refresh in my code) every iteration. That subroutine checks the timer and returns. 3 jumps, 6 cycles.

But how long is each iteration?, if you call the routine 10 times before the timer ends, then that's 60 cycles wasted, lots more than the interrupt routine overhead.

I will get a poor display only if the main program's processing takes too long... It is my understanding that as long as the refresh rate is high enough (say 90Hz), it shouldn't flicker, even if it's not regular, right? One of your preivous posts seems to state the opposite. Irregular refresh rates is seen among toher things in games on consoles and PCs, but as long as it's high enough (and in sync with the monnitor's refresh rate), nothing flickers...

If different digits (or segments) are ON for different lengths of time, then the display will have noticeably different brightness levels - any irregular variations will show as brightness variations on the displays.

Obviously you have a certain amount of error possible before it's noticeable, but once you get above that it becomes quite annoying :lol:

With an interrupt based routine you get nice regular even multiplexing, with each digit (in your case) being ON for 1/(number of digits) of the time.
 
Status
Not open for further replies.

Latest threads

Back
Top