Multiplexing 7Segs and using timer Countdown

JohnInTX

Joined Jun 26, 2012
4,787
Look at the two code blocks in #76 under EDIT2. I changed the way Int2Segs works for the DP so that we have control over flashing the DP in the time field. This second version takes a third parameter which is a mask to add a DP to any of the 3 digit fields. The too many actual parameters error is because your Int2Segs is the old (2 param) version but you are calling it like the newer (3 param) version.

Who says there ain't any simple answers?
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Yup ..I edited the post too late.

I went thru them and fixed it.

I managed to figure it out.

Now I am trying to make the time DP to pulse every 500ms. So every time it comes ON it would be a second count.

Do you think it would be easy ?

The time should be in 60s not 99s. Right.

What's next ?
 

JohnInTX

Joined Jun 26, 2012
4,787
Now I am trying to make the time DP to pulse every 500ms. So every time it comes ON it would be a second count.
Do you think it would be easy ?
Sure. I still kind of like synchronizing the DP with the actual seconds count for a smooth visual experience though.

My thoughts on keeping the elapsed time in seconds is that its simple to do arithmetic on it e.g. 'has it run to the time setpoint' etc. and then you convert it to the time display when you need it. So the native system time is in tiks (seconds) and you work with that. To convert from seconds to hh:mm (or whatever) you do some / and % like we did in Int2Secs. Meanwhile, the native system tik is one second which is easy to compute with.

So.... if you are on board with that, you can make the native system time tik 500ms. That would keep the flashing LED sync'd with the display, any compare type of math would be the same and only the tiks->hh:mm display would change.

Either way, you probably need a dedicated routine similar to Int2Segs except for hh:mm.

A related, better way would be to maintain the system timer in the interrupt routine. You would need dedicated routines to set and read the 2 byte timer (that temporarily disabled the system tik interrupt). A RUN flag would control the update and the LED flash - make the interrupt at 500ms and increment the timer every other interrupt - that kind of thing.

Give it some thought. Its actually past time to define how your charge cycle and timing needs to work anyway. That way we can design the final solution.

Have fun.
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
OK..
but first I like better explanation of below if it is not too much to ask.
// Setting is 256 - (3ms / .5usTcyc) / 64Prescaler = 256 - 93.755 counts = 162.25 ~= 162
#define TMR0set 162 // counts 162->255 then rolls over and interrupts
So I will leave the second DP as it is now. No need to waste time on something working.

Do you wanna know my charging method ?
 

JohnInTX

Joined Jun 26, 2012
4,787
Rich (BB code):
// Timer 0 set to interrupt about 3msec for now
// Setting is 256 - (3ms / .5usTcyc) / 64Prescaler = 256 - 93.755 counts = 162.25 ~= 162
#define TMR0set 162                // counts 162->255 then rolls over and interrupts
It is not too much to ask.

First off, I always try to show how some constant is derived in case changes need to be made or *ahem* there is an error. There is always a balance between not enough information e.g. TMR0=162; // done! and reciting the databook and sometimes more info is needed.. (and I usually put more info in but this started as a quickie... another lesson..)

Here we go at deriving a setting for TMR0.

TMR0 counts up. When it rolls over from FF->00, it interrupts the PIC. Contrast this with TMR1 and 2 which count up from 0 to a specific value. To get the desired number of counts on those, we start at 0 and count up to the reference value. But with TMR0, we have to determine the number of counts until it rolls over to 0 and that number is 256-the desired number of counts.

So.. how many counts = 3ms? First we need to know what one count is. That is determined by the oscillator speed (8MHz) and the prescaler. Start with the osc. In a PIC, the internal clock speed is Fosc/4. For timers its useful to express this as 1/(Fosc/4) = Tcyc i.e. time for one internal cycle. For 8MHz, Tcyc = 500ns (.5us).

So for 3ms, all we have to do is divide 3ms/.5us for the number of counts to count, yes? A few jabs at the 'Lil Professor says that we need 6000 Tcycs (at 8MHz) to get 3ms. So just jam that onto TMR0 and.. it doesn't fit the 8 bit timer.

Fortunately, we have a prescaler available which makes the input to TMR0 some factor of Tcyc. We need to get the number of counts in TMR0 to fit into 8 bits. 6000/256 says we need a prescaler of 23.4375. Since those slackers at Microchip didn't see fit to provide us with a prescaler that achieves that value we pick the next bigger one that's available. Our choices are /32 /64 /128. Not knowing exactly what we would wind up with for a mux period, I picked the middle one for the most flexibility. Science!

With the prescaler set, the input to TMR0 is Tcyc * 64 = .5us *64 = 32uS per TMR0 count. 3ms/32us per count = 93.75 counts. TMR0 rolls over at 256 so for 92.75 actual counts, TMR0 has to be set to 256 - 92.75 = 162.25. Again, Microchip has left us at the mercy of binary numbers so we round to 162. And since we know better than to use literals in the codeflow, we #define it as a symbol and use that whenever we want to reload TMR0.

Whew! If you think that was hard to read, imagine how much of a beating it is to determine and write it. But, by throwing the formula into the comments, at least we know what the programmer was thinking. That's important.

As long as we are on the subject, how much description is enough? You had valid questions that took paragraphs to (hopefully) resolve. But to put all of that into the source would not be practical. I've been typing for 20min on ONE constant. My general rule is to provide details to one 'skilled in the art' i.e. the programmer knows how TMR0 works, but just needs to know how I've worked it. If they are not sufficiently skilled, the client has to issue the PO. But since AAC is a learning and knowledge-exchange site, its all por-nada here.

A little homework for extra credit:
I picked /64 for the prescaler for utility but its not the best choice for 3ms. Why not? Hint: sec 5.1.3 of the datasheet.

A recommendation:
I made Excel spreadsheets years ago to calculate my timers/PWMs etc. If you do the same you'll 1) understand everything better than most and 2) have a useful tool.

BTW: its possible to make the compiler or assembler compute some of this. By tying everything to the XTAL freq, you can get many of these to calculate automatically. Its not worth it unless you write a lot of code but is an interesting exercise. One thing I ALWAYS do, is put some kind of oscillator frequency descriptor (compiler/assembler generated or a manual #define) and qualify all of the timer settings with that value i.e. (in assembler)

Rich (BB code):
#if(XTAL != 8000000) 
  ERROR "Timers.inc: Need to re-calculate timing!"
#endif
Just an example to think about for now.
So I will leave the second DP as it is now. No need to waste time on something working.
Agreed.
Do you wanna know my charging method ?
Maybe. We kind of got off on 'can you multiplex an LED display with 1 PIC?' - OK, yes we can. But as you've seen, we have taken some side roads. To minimize that, its important to get back to a top-down design i.e. it charges batteries by doing this.. Top level. Second level: to to that, it needs some displays, accurate timing (getting there), some buttons (hmmm..) and (??). The third level we've begun with the fancy display/DP stuff. A still lower level would be the display mux. And on and on...

When designing a program I like to conceive it from top down - the top level reads like a brochure - it does this, it displays that.. Then figure out how it does do this and display that.. and defer details to lower levels until its time to connect to the pins. Several layers of software will be between 'it does this' and 'how it does it'. It's not possible to accurately code those lower levels without knowing what the higher levels require of them. Some of our detours have been because we are exploring MikroC, seeing what it takes to get 9 digits plus LEDs to be bright enough etc. But its getting to be time to get the design stable.

Do you know how I know? We've rev'd Int2Segs 3 times and now we probably need to have a different routine altogether for the time display. My little bell says: Time to firm up the design spec.

Ultimately, the goal is to be able to just recite what the thing does 'When the START button is pressed it starts the charger and charges for 2 hours after the voltage reaches xx.x all the while displaying the volts, amps and time remaining' blah blah. Then write the lower stuff to provide a seamless interface. Want volts? Volts is available because some lower process has read the ADC, averaged it, scaled it and determined that the reading is valid so that when 'Volts Now' is needed by the top process, it only has to read a register then get on with it. That's how its done.



But with all that, you're doing great.
Onward!
 
Last edited:

THE_RB

Joined Feb 11, 2008
5,438
I only had a quick glance through the code, but it looks like you are sequencing 10 digits (0-9 including the DP) at 3mS per digit?

That means the total display refresh takes 30mS, and each digit is flashing at 1/0.030 = 33 times a second with 10% duty.

33 Hz flashing will be detectable by the eye as a flickering display and (to me) would be annoying to look at.

Is there some reason it needs to be so slow? I would think 1mS per digit would be optimal, and would have the added advantage of you being able to use the same 1mS interrupt to count seconds, so you don't need two interrupts. It does seem silly to have two separate interrupts, which are both timed periods. :)
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
I asked for the TMR0 detail cause I changed the '162' to '200'.
This eliminated the flicker completely.
I wanted to write how to get the 'ms' for a 200->255 roll over.

I changed the value based on what I can see from the segment flickering so I wanted to write the comment details.

Guess now I know how to figure it out.

@THE_RB.
The mux rate is not 33ms now, it is quicker than tht.

I started this thread to confirm tht muxing 9 digits from 1 PIC.
I saw RB did 10 digits in google.
But I am not good just starting and wanna know how to get it done.
I have several examples on muxing but this brief time with John has brought this to a whole new level for me.

I am glad for all the info and advices I gained from this thread.

But I admit driving LCD's is actually child's play compared to this.

The Charging mentioned was for to let u know my project target.
I will post the details and the code for any one interested in it in the project details once I get it done. So for now I will get this going in this thread.

Project Details;

I think you can find the thread I started to find out about li-on and lipo that I had. Since no one seems to know I googled and read a lot.
Found out tht li-on and lipo is not different at all concerning charging method. I have both lying around and I want to keep them healthy until I can use them. I bought chargers off ebay and aliexpress. They charge but I dunno the state of charge. I can buy an expensive one but it is not worth since I know I can build one tht does what I need it to do.
Batteries I have a good and bad and worse. Some can be revived it seems and I wanna do that if I can but off the shelf chargers does not charge at lower currents. It goes into it's limit to charge. Deeply discharged cell or not it does not care.
I know an LCD is easier for this. But I wanted to learn as I do this. So I chose the little 7 Segs.

Different cells charge at different rates. I get from 1000mAH to 5000mAH. Li-on and lipo charges at 0.7C to 1C .
For a 5000mAH I would need atleast 5A.
For healthy cell charge rate I would need from 700mA to 5A based on the cells.
For top up it I would need 100mA to 500mA.
To revive I would need even less than that for much longer periods.
Time for topup is based on cell temperature and charge current and time. I learned tht two hours is max .
Charging upto topup does not need to measure time as the battery voltage is monitored during charging (Constant Current)
Top-Up is CV mode.

I got all this from reading google but of course I am happy if some one can prove me wrong.

So based on all this I decided I would go for manual charging that is Charging current selection.
This way I can input what I need based on battery spec. No need to have pre programed states. This way I am happier.

The Charger is based on LM723 which I can make and I already had made a working of 35V and upto 10A supplies.
It's designed to handle 5A continues and Maximum output is 8.5V.
Voltage control is there but also 4.2V and 8.4V selection can be done thru μC.
Current limit is manual and based on the cell specs which I will set after checking the cell capacity.
It can be set from 0-5A.
The Current shunt amps is based on TI's current shunt amplifies which I already have.

The PIC working details in seq.
post #89
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
Is there some reason it needs to be so slow? I would think 1mS per digit would be optimal, and would have the added advantage of you being able to use the same 1mS interrupt to count seconds, so you don't need two interrupts. It does seem silly to have two separate interrupts, which are both timed periods. :)
3ms was a starting point which I figured would change. The reason for 2 timers at this point was to compensate for the vague spec and allow changes to one time without fouling up the other. If the final version uses the same time, the timers can of course be driven off one IRQ.
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
You can say the charger is semi-automatic.
The charger design just for personal touch.

As u knows it has a couple visual indicators.
3 sets of 3 digit displays + a few LED's.
Time display is only used during TopUp Period. A maximum of 2 hours is what I had in mind. Expiration of the countdown will terminate charging.

Voltage display is used to monitor the set value and the charge rise status.
Voltage is set at 4.20 by default for safety. 8.4 can only selected through the setting phase. Meaning the V selector relay is always deactivated and is activated by the 8.4V select option...(read below)

Current display is used for setting charging current and also to monitor current during TopUp.
The PIC does not control or set any charging current. It is left for the user to set manually. The PIC only looks for this value to terminate charge...(read below)

The LED's are as follows.
Constant Voltage mode indicator
Constant Current mode indicator
Battery Inserted indicator
Charging status indicator
Battery Over temperature indicator.
Fan on LED (not controlled by PIC)
Shut down LED

There a couple of input switches, and as follows;
A rotary encoder with a push button switch (push the shaft to select)
Battery inserted detect switch
Charge start or halt or stop switch.
Battery test switch

Charger PSU details will not be disclosed in this thread.
This thread is basically for the PIC code

This is a step by step on how I want the PIC to function


{First and foremost a little note on charge terminating method. Charge termination is done by two ways. One is when the 0.1C current is reached. Other is the 2Hr (MAX) time period is reached . Whichever comes first terminates the charge.}

At Power up it should check for any faulty segments or leds.
It is done by a turning on all the Digit segs and Led's for a brief moment showing that all the lights are working.
Start
Then it will check for heat sink temperature to turn on or off the fan needed for cooling the pass transistor sink.
This routine is in main loop since the internal temperature needs to be cooled as the battery is charging.
This routine also monitors for sink overheat (if the fan Fails) and will issue a complete charger shutdown command deactivating PSU control relay and light a LED rapidly. And stay there, telling to replace the freakin fan

Next the display will show all Zero's. Time is 0.00Hr, V is 0.00V and I is 0.00A. All LEDs off.

Then it will go into battery Inserted checking. and will stay at this point without changing the display in point mentioned above.. If battery is taken out, ( any time ) program resets to start

Once the battery is in the PIC detects and lights the LED. Reads Battery Temp ADC value from LM35. Checks for threshold value, if below moves on to user input, and if above, program halts there and over heat LED lights. It waits for temp to drop or the battery can be taken out and reset to start

if above is OK it is ready for user input.
This state check for 3 inputs and loops around from time to volts to amps. It can only be exited with a 1 second long PB switched on and is ready to start charge and waits for start switch press.
Also within this loop battery Voltage checker can be used. If Battery checker is pressed it will show the open circuit cell voltage on Volts display. It will display as long as the sw is pressed only.

Input is through encoder and select is PB on the encoder.
First PIC waits for time input.
The Time display needs to show the time in put when the encoder is rotated. Up or down, display should show the input value. Time should increment from 0.00 to 2.00 in 1 minute steps. So any value can be entered. Once the desired time is in which is shown from time display the PB is pressed and that value should be stored in the PIC. The timer is activated and counts to 0.00 from the stored value once the CV mode is activated. The timer does not count in CC mode.

Once the PB is pressed the time is set, and the PIC next goes into Voltage select. The encoder is used to switch between 4.2V and 8.4V. This display is shown at the Volt display.
By pressing PB the value is set and Volts show the input value it basically means 1 cell or 2 cell. And this set value is stored and is responsible to switch the corresponding relay that controls the supply (charger) voltage select (4.2V or 8.4V)

Next is the Current input which is basically the charge terminating current. Should be set by encoder from 0.00 to 5.00 Amps. This current value is stored and determines one condition when to terminate charge. Can be incremented or decremented in 0.01 steps., basically 10mA resolution.

When all are set. PB is pressed for 1 second and PIC should switch to charge ready state which is wait for start sw press.

Once pressed charging relay is on. Charging LED blinks CC Led is on and PIC reads the V and I, ADC and displays the decimal value in the corresponding segments. The time is stopped and shows the set value stored .

Battery temp is monitored continuously for threshold during charging. If met it will deactivate charger relay and wait for the battery to cool down or to be removed. If removed goes to start. If cool activates charger relay.

During this time the Current display will show the 0.7C value read from ADC and Voltage value at battery terminal which will be lower ( read by ADC ). This value will rise when the battery charges.
When battery voltage reaches 4.20V for one cell and 8.4V for two cell CC led is OFF and CV turns on. Then timer starts to count down from set value.

Current ADC is read continuously and compared with 0.1C value entered before and monitors the count down is zero or not. Charge terminates when (whichever comes first) condition is met. The charge LED stops blinking, remains lit. All relays turn off except shutdown relay. battery checker sw can test the battery terminal voltage as said above during this period

Wait for battery removal. When removed resets to start and waits again to go on.

Did I miss anything. The writing part was hard you know

Buzzer is out, so no dumb delays. :D I used all the IO's :eek:
 
Last edited:

THE_RB

Joined Feb 11, 2008
5,438
Thanks for explaining more about the project, it's good to see the overview of exactly what it does.

That has a heck of a lot of features for a first project! I would suggest you code it in a way that you can get individual features working by themselves. That will make it easier to get working, and also will provide you with re-usable features that you can use in future projects too.

And don't forget that flowchart showing the program flow and decisions (like I said to you in the other thread).
:)
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Very not good at flow chart but am trying.

The VA meter code was actually quite easy. Just the msgs is screwing it.

The difficult part is the muxing the segments for me that is using timers. Still do not get the full hang of it. I believe I can code the rest but the display part.

I have started fresh code, trying not to (tempting)block copy and paste. commenting properly and gonna attempt one function at a time.
I am going to code the encoder part.

by the way the muxing is 1ms. TMR0 is 224 if I remember correctly from last night.
Before it was 200, per calculation it is 1.7ms roughly. Still flicker is gone at 1.7ms.

Which is better 1.7ms or 1ms ?
 
Last edited:

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Rich (BB code):
/*******  LED/Display controls ****************************/
 // These turn on, off, toggle LEDs in the display segment
 // array according to the passed mask.  Any '1' in the mask
 // will have the indicated action on its asscoiated LED
 // Multiple LEDs can be controlled in one call
 // These assume that a 1 output turns the LED on.
 // Added: FLASH MASK control

 void LEDs_on(SegBits ledMask)      // masked LEDs on
 {
      DigitsBuf[dLEDs] |= ledMask;
 }
 void LEDs_off(SegBits ledMask)     // masked LEDs off
 {
      DigitsBuf[dLEDs] &= ~ledMask;
      LEDflash_mask &= ~ledMask;    // stop flashing on OFF LEDs
 }
 void LEDs_toggle(SegBits ledMask)  // toggle masked LEDs
 {
      DigitsBuf[dLEDs] ^= ledMask;  // not useful iff LED is flashing
 }
 void LEDs_direct(SegBits ledMask)  // write mask value directly to all LEDs
 {
      DigitsBuf[dLEDs] = ledMask;
 }
 void LEDs_flash(SegBits ledmask)   // flash any LEDs under mask
 {
      LEDflash_mask |= ledmask;     // IRQ / mux does the flashing
 }
 void LEDs_UNflash(SegBits ledmask) // Stop flashing any LEDs under mask
 {
      LEDflash_mask &= ~ledmask;
 }
Question is Do I meed all the "void()"

I only need to use flash only at 100ms. i.e
during charging blink at 100ms
and when shut down mode at 100ms.

Every time else the LED is just lit

Of course the 1 sec tick is seen by DP.

Rich (BB code):
 void initLEDdisplay()
 {
  unsigned short i;
    for (i = 0; i < N_DIGITS; i++)
      DigitsBuf = 0;
      LEDflash_mask = 0;
      digitN = 0;
  // init multiplexer here
 }
 


Above the red high light, what is tht for ?
 

JohnInTX

Joined Jun 26, 2012
4,787
Some quick answers:
Question is Do I meed all the "void()"
Yes, you should use it. The default return value for a function is a signed int. Void says the function returns nothing - so if you try to use a value from it the compiler will flag an error to help you out. Early C was did not do much type checking. If there was a type conflict, it applied default rules that may or may not have worked. Modern C will type check everything you tell it to - a BIG help.

// init multiplexer here
This is a placeholder to remind you/me that this is the place to init any values that the mux needs before starting. The mux as written makes use of C initializing global variables to 0 (digitN in this case) so no further init is needed. The line can be deleted.

I have started fresh code, trying not to (tempting)block copy and paste. commenting properly and gonna attempt one function at a time.
Why start fresh code and if you do, block copy is the way to go. My earlier reference to an error with block copy related to the fact that it included different names in each section which I neglected to update. Personally, if I want to clean up after experimenting I copy the entire file for safekeeping and as a reference then clean up the existing one.

If you are doing that, I think it would be a great time to break this into multiple files. I like my code broken into functional blocks i.e. one main, one for the display, one for the encoder, one for all the timer stuff etc. Each .C file will have a .H(eader) file which lists the functions in the C file. Other files #include the .h file(s) that they need to know how the function calls are written. Expect lots of files on a more complex system - one large project I have contains 60+ C files each with a .h file and that doesn't include libraries. I also put all IO definitions (sbit SegA etc) into a single xxxIODEF.h file - for clarity, to support different PCB revisions and the like. All global variables go in their own xxxglbls.c and xxxglbls.h files (they are declared in the .C, described to other files in the .h). The xxx is the project code so that I can reuse the naming convention i.e. CHGRglbls.c PUMPCTRLglbls.c etc. and not have to 1)reinvent names for the same kind of file (globals) and 2)not be in the wrong folder when editing/reading the file etc.

My convention is to keep main just that, the top level program and the interrupt handlers. Then, in a few pages, I have a complete overview of the program e.g. 'it displays AMPS' without having to worry about how it displays AMPS. If I want to know that, I refer to a lower file.

by the way the muxing is 1ms. TMR0 is 224 if I remember correctly from last night.
Before it was 200, per calculation it is 1.7ms roughly. Still flicker is gone at 1.7ms.
Which is better 1.7ms or 1ms ?
If 1.7 ms is good, use that. My reasoning: the interrupt routine has some unseen overhead because it has to save any system registers that the various services use so that it can restore them before returning to the interrupted program. The less you burn this overhead, the more processing time you have for other things. Leave it at 1.7 for now, its easy enough to change.

Very not good at flow chart but am trying.
It comes with practice but at this point, maybe a flow chart is too detailed. Consider describing the program with an inverted tree chart. At the top (root of tree) describe the broad function of the system - reads VOLTs and AMPs, displays V/A/Time, encoder for data entry, beeps warnings, times the cycle, flashes LEDs etc. - Don't try to put anything into sequence yet with a flow chart, just identify the basic functions that the system does and what it needs to do them on a horizontal line.

For each broad function e.g. reads VOLTs and AMPs draw a vertical line to a box that says just that. That vertical line passes V/A readings up to the top level (which has no idea how the readings are obtained nor does it need to). Next, under that box, identify what you need to generate the readings - the ADC and some scaling arithmetic I would guess so drop another sub-tree with two boxes on the 3rd level. One box is the ADC and the other is the scaling. Or maybe, the scaling is integrated into the ADC box. Doesn't matter at this point. What does matter is that level 2 obtains ADC information from the lower level and passes makes it available to the upper level.

Do something similar for each major function. If the encoder is used to enter several parameters, put those on a new down-line from the top (2ed level) so that the main (top) program can use them (again, main does NOT care how the params get there, just so they do). The raw information comes from the hardware encoder so maybe the encoder interface goes on the 3rd level and below that is an interrupt-on-change function / MikroC library etc.

A few things should become apparent here. Notice that as we go down in levels, we get closer to the actual hardware and that as we go up, it becomes more data-centric. At the very top is the actual function of the battery charger - I call it the 'brochure' level - it says what it does but defers how it does it. Testing becomes easier as well. If you want to test something voltage-related but haven't written the ADC yet, just poke a value into 'Volts' and see what it does. Changing to an external ADC? No problemo, just replace the ADC box with the new one and everything else stays the same.

Finally, since the top level simply makes use of values and services, you could rip it off and replace it with something that does something else completely. You already have the hard stuff done!

An important point here: the 2ed level box providing volts and amps does so autonomously and provides real-time results. The higher level can read the current values (from the last conversions) immediately without stopping to start an ADC conversion and wait for the results. One reason you are having problems with the flow-chart is that you have lots of parallel things going on (display, ADC, encoder, buttons etc etc). Flow charts are pretty much sequential become cumbersome quickly. They are better used on the very top level (where details are deferred) and the very lowest levels where there is nothing deferred e.g. display the next digit, process the encoder interrupt.

So how to code this monster? You already have! Each downline system function gets called in sequence in main() and performs its task (without waiting). One such task was that flashing LED with its derived timer. After visiting each one, the top level task is run with the new updated system data from all of the downlines - and round and round it goes.

This is a different way of thinking than a simple 'do this - wait - do that - wait' model and may require some time to become comfortable to you. Some disagree but I consider it superior to a simple procedural model. But, personally, I think the time invested in becoming proficient with it is time well spent. You can easily invest the same time resolving things like 'hey, when it beeps the LED stops flashing - or - Ouch! when it beeps, the traverse goes right past the limit switch - *crunch*'.

What do you think?
 
Last edited:

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Read a lot but still I am getting lost along the way. Me think it's very confusing.

This is really hard.
Can I try my way to code.
You showed me the difficult part. So I am trying to do this now.

I started fresh. Other files are there to refer.
I like to know about .h files cause I have seen them. Dunno how to create them yet.

I am coding. I got the digits and LED checking part done.
Got the fan on off part
Got the over heat shut down part
And managed to blink the led when over heat too.

I tried to switch off the digits but I see the segments still flickering but very dim.

Is it possible to switch of the Digits only completely. I can do what ever I want with the LEDs, thanks to John for the coding method.

Do I need to turn off the timer or something.?

After a few tries I think I can draw a diagram.

Like John said, flow chart drawing is confusing me even more.

I thought if it is in my head I can try to code. Atleast.

Rich (BB code):
/******* All light check for fault *************************/
void initLightchk()
{
    Time = Volts = Amps = 888;      // Value to light all segments.
    Int2Segs(Time,dTime,SegDP);     // Conver to display.
    Int2Segs(Volts,dVolts,SegDP);   // Conver to display.
    Int2Segs(Amps,dAmps,SegDP);     // Conver to display.
    LEDs_on(ALL_LEDs);              // All LED's on.
    delay_ms(1000);                 // 1 second visual indication.
    Time = Volts = Amps = 000;      // Resets Digit's to 0
    Int2Segs(Time,dTime,SegDP);     // Conver to display.
    Int2Segs(Volts,dVolts,SegDP);   // Conver to display.
    Int2Segs(Amps,dAmps,SegDP);     // Conver to display.
    LEDs_off(ALL_LEDs);             // All LED's off
}
The light testing code is just one time and is only at power on. No more.
U see red light.
The display reads zero.
How can I blank out the display completely.

Due to loaded Time = Volts = Amps = 000; I can still see them when I tried PORTC = PORTE.RE2 = 0; in a loop(shutdown) below

Rich (BB code):
void ShutDown()
{
  while(1)
  {
   PORTC = PORTE.RE2 = 0;
   SD_Rly = 0;
   LEDs_flash(SD_LED);
   Sink_Temp = ADC_Read(3);        // Get 10-bit results of HeatSink temperature.
   if(Sink_Temp<=300){
     HeatSink_threshold = 0;
     break;
   }
  }
}
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
This is really hard.
Can I try my way to code.
You showed me the difficult part. So I am trying to do this now.
It IS a lot to swallow at once. Go ahead and code how you are comfortable. We can take it from there.

I tried to switch off the digits but I see the segments still flickering but very dim.
Is it possible to switch of the Digits only completely. I can do what ever I want with the LEDs, thanks to John for the coding method.
Except blank them, I guess. A couple of ways to do it.. You can create a new entry in the segment table (digit 0ah maybe) that does not turn any segments on. You also can add other patterns and symbols - maybe you want to display '---' or 'OFF'. These can be done with an extended segment table. The problem with that approach is that its sometimes harder to do.. display VOLTS or write 0ah,0ah,0ah etc.

If I were wanting to blank the display, I'd probably declare a 1 bit variable called 'BlankDISP' and if (BlankDISP) .. {dont turn on the digits} OR alternately, override the segment output to turn them all off. Since you share the display with the LEDs, you might have to put the digit blanking in the switch statement that decodes the digit drivers or something like. If I were going to the trouble and since the switch is what turns on the digit, I'd declare 4 bits, BlankVOLTS, BlankAMPS, BlankTIME , BlankLEDs and detect each set of digit blanks in the cases of the switch. Nice. Something like this..
Rich (BB code):
     switch (digitN){            // all digits are off here
                                 // turn on new digit iff not blanked
                                 // (also brute force)
            // digits 0-2 are TIME
       case 0: if !(BlankTIME) dig0sel = 1;
               break;
       case 1: if !(BlankTIME) dig1sel = 1;
               break;
       case 2: if !(BlankTIME) dig2sel = 1;
               break;
           // digits 3-5 are VOLTS
       case 3: if !(BlankVOLTS) dig3sel = 1;
               break;
       case 4: if !(BlankVOLTS) dig4sel = 1;
               break;
       case 5: if !(BlankVOLTS) dig5sel = 1;
               break;
               // digits 6-8 are AMPS
       case 6: if !(BlankAMPS)dig6sel = 1;
               break;
       case 7: if !(BlankAMPS) dig7sel = 1;
               break;
       case 8: if !(BlankAMPS) dig8sel = 1;
               break;
               // digit 9 is LEDs
       case 9: if !(BlankLEDs)digLEDsel = 1;
               break;
     }//switch
I like to know about .h files cause I have seen them. Dunno how to create them yet.
Post or PM a working piece of code and I'll break it into separate files with the .h stuff. You can see how it works from there. You also might get a good C reference manual, maybe K&R 2ed. ed. even.
Like John said, flow chart drawing is confusing me even more.
I thought if it is in my head I can try to code. Atleast.
What you are experiencing is common but eventually, EVERY detail of EVERY module must be thought out and expressed in code. While it can seem easier to just fire up the IDE and code away, you'll find on the more complex projects that some sort of paper design that you can trace the flow on and see how things interact will save you lots of time and revisions. Think twice-Code once.

Sounds like you are making good progress!
 
Last edited:

THE_RB

Joined Feb 11, 2008
5,438
...
Except blank them, I guess. A couple of ways to do it.. You can create a new entry in the segment table (digit 0ah maybe) that does not turn any segments on. ...
...
That is how I would do it. It's good to be able to display a blank digit, anywhere it is needed. On a 9 digit display he might even want to show two numbers separated by a blank digit.

If the segment decoding has a table for 0-9 he could add a default condition for anything >9 turns all the segments off.
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Deleted 2 posts.

Still I do not get how to get '-' ...that is only SegG to turn on during the over heat routine.
Using OFF is think too much.
I like the idea off all digits showing "-'

Code is growing and getting better.
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
To add things like a '-' and 'OFF' and '???' all you have to do is this:

Define and name the segment pattern that makes up each symbol/letter etc.
Add the pattern to the segment table so that it can be fetched just like the digits 0-9 from index 0 through 9. In the code example, I expanded the table to include hex A,b,C,d,E,F. The segment patterns are defined as DigitA-DigitF like before (adding up the individual bits that drive each segment to make the pattern) and the value of each is just 0ah-0fh because that's where they are installed in SegTable[].

Here's a sample:
Rich (BB code):
// Digits 0-9
#define Digit0 (SegA + SegB + SegC + SegD + SegE + SegF)
#define Digit1 (SegB + SegC)
#define Digit2 (SegA + SegB + SegD + SegE + SegG)
#define Digit3 (SegA + SegB + SegC + SegD + SegG)
#define Digit4 (SegB + SegC + SegF + SegG)
#define Digit5 (SegA + SegC + SegD + SegF + SegG)
#define Digit6 (SegA + SegC + SegD + SegE + SegF + SegG)
#define Digit7 (SegA + SegB + SegC)
#define Digit8 (SegA + SegB + SegC + SegD + SegE + SegF + SegG)
#define Digit9 (SegA + SegB + SegC + SegD + SegF + SegG)
// A-F hexadecimal - character codes 0ah - 0fh
#define DigitA (SegA + SegB + SegC + SegE + SegF + SegG)
#define DigitB (SegC + SegD + SegE + SegF + SegG)
#define DigitC (SegA + SegD + SegE + SegF)
#define DigitD (SegB + SegC + SegD + SegE + SegG)
#define DigitE (SegA + SegD + SegE + SegF + SegG)
#define DigitF (SegA + SegE + SegF + SegG)

//Segment Look-up table - one entry for each digit/character.  Position in the array
// determines its character code - 0-9, A-F are their hex equivalents.
const SegBits SegTable[] = {Digit0,Digit1,Digit2,Digit3,Digit4,Digit5,\
                                  Digit6,Digit7,Digit8,Digit9,\
                                  DigitA,DigitB,DigitC,DigitD,DigitE,DigitF};
Note that by adding digits A-F to the SegTable[] character codes 0ah-0fh are implemented as A-F and now you can display in hex - if you want.

Symbols could also be defined and put in SegTable[]. That would allow them to be referred to by character codes - more useful in a printf kind of deal than with just a few digits/chars. So I'd just define some canned messages as arrays of raw segment patterns that you've defined:
Rich (BB code):
 // Symbols are stored in messages of raw segment patterns (not character codes)
#define DigitDASH (SegG)
#define DigitBLANK 0
#define DigitQUESTIONMARK (SegA + SegB + SegE + SegG)
//Canned messages - These are stored as raw segment patterns. The symbols do not
// have character codes assigned in this implementation.
const LEDmsg_OFF[] = {Digit0,DigitF,DigitF};     //'OFF'
const LEDmsg_WTF[] = {DigitQUESTIONMARK,DigitQUESTIONMARK,DigitQUESTIONMARK}; // '???'
const LEDmsg_D_Q[] = {DigitDASH,DigitBLANK,DigitQUESTIONMARK}; // '- ?'
const LEDmsg_BLANKALL[] = {DigitBLANK,DigitBLANK,DigitBLANK};  // '   '
So to write a canned message to one of the pre-defined 3 digit fields you'll need to write a routine similar to Int2Segs (with a passed offset determining where the message goes in DigitsBuf) but instead of extracting digits from an integer, it will just copy the 3 bytes of raw segment patterns to 3 bytes in DigitsBuf at the appropriate offset to hit the right display and whoopie!* there's the message. I'd do the function header like this:

Rich (BB code):
void CannedMsg(SegBits *msg, unsigned short ofs, SegBits DPmask);
You may note that we now have 2 different ways to blank a display. Our specification process is still suspect but indeed, each way has its advantages:

As long as you are going to have a canned message system for 'OFF' '---' etc, you might as well define one more message as ' '. That will blank the display field just like shutting off the digit drive version does and its nearly free at 3 bytes of ROM. (after implementing the canned msg system).

That kind of obviates the blanker that we did in the switch statement. Both do the same thing with a special exception - the switch-based blanker does not clobber the segment register so that you could flash a message e.g *OFF* by toggling a bit without having to know what message (or value) is being flashed.

If you don't have a need for such dual purpose awesomeness, I'd just use the canned message system. Once you have it, you'll be surprised at what it can say..

Take yer pick and have at it!

* c.f. “Phineas J. Whoopee, you’re the greatest!”
 
Last edited:

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
You indeed are great.
No wonder you are a "Microchip Design Partner / Consultant since 1994'

Thank you for making this possible.
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
U know I sat and read for around an hour trying to wrap my head around it.
And I go

So I came up with this.

Rich (BB code):
#define DigitF (SegA + SegE + SegF + SegG)

const SegBits SegTable[] = {Digit0,Digit1,Digit2,Digit3,Digit4,Digit5,\
                                  Digit6,Digit7,Digit8,Digit9,DigitF,};
My Own

Rich (BB code):
/******* OverHeat Conversion routine  **********************/
void OFFmsg(SegBits, unsigned short ofs)
{
    unsigned short x,i;               // this gets removed by the optimizer
                                    // Appends passed decimal point to first digit
    x = i/100;
    DigitsBuf[ofs] = SegTable[0];

    x = (i/10)%10;
    DigitsBuf[ofs+1] = SegTable[10];

    x = (i%10);
    DigitsBuf[ofs+2] = SegTable[10];
}
/******* Shut Down Loop ***********************************/
void ShutDown()
{
  while(1)
  {
    OFFmsg(Time,dTime);             // Show "OFF"
    OFFmsg(Volts,dVolts);           // Show "OFF"
    OFFmsg(Amps,dAmps);             // CShow "OFF"
    SD_Rly = 0;                     // Turn Off All Relays.
    BVt_Rly = 0;                    // ------ DO --------
    Ch_Rly = 0;                     // ------ DO --------
    CVs_Rly = 0;                    // ------ DO --------
    LEDs_flash(SD_LED);             // Flash Alarm LED
    LEDs_off(CH_LED+CV_LED+CC_LED+BI_LED+BH_LED);// All LED's OFF except SD_LED
    Sink_Temp = ADC_Read(3);        // Get 10-bit results of HeatSink temperature.
   if(Sink_Temp<=300)               // 300 is dummy value for testing
   {
    HeatSink_threshold = 0;         // If heat sink is cooled reset flag
    break;                          // Move on if Temperatue is OK
   }                                // While loop exits
  }
}
And I see "OFF"



I am still trying to put together what u explained though.
 
Top