SLA Charger/Backup System

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
I made a new code.(did not post it)
Mostly the same.
TMR0 runs the buzzer and LED flasher and most of the timers stuff such as debouncing.
TMR1 runs at 1Hz.
So basically this code runs two timers.
Still the LCD message part is messed up.

The posted code is made to interrupt at 5ms like you asked.
But the second one I made runs at ~3ms (TMR0) but the TMR1 is accurate at 1Hz. I timed it, so the timer I need to top up the SLA can use the 1hz timer.
But I believe I can make the second code to run at 5ms. But it is not important cause, since you asked to make 5ms on the dot for the posted code, I made the second one to learn.

And thanks to you I am learning a lot. Guess what, I made a buzzer using your lessons to be used in the access controller. It did not came with an alarm. The alarm uses TMR for buzzer and a 1 minute derived timer and debouncer. It beeps when lock is unlocked and alarms continuously if door is opened for more than a minute. Uses two inputs :D. All without dumb delays. It is mounted and working. It took me 3 nights, like 9 hrs:p. It is fast for me u know. figuring out the interrupt and timer stuff which I never did before

Just need to finish the LCD code so the Backup can be mounted.:(
 

JohnInTX

Joined Jun 26, 2012
4,787
So the posted code is OK and we can move to the LCD?
All without dumb delays. It is mounted and working. It took me 3 nights, like 9 hrs:p. It is fast for me u know. figuring out the interrupt and timer stuff which I never did before
The first time is always the hardest. I believe you'll find that it is time well spent. It will give you a basis for all future coding projects rather than having to fiddle with dumb delays on an ad-hoc basis on each different one. FWIW, I use this method for everything, even simple little things - it always works and simple little things have a way of growing into complex big things.
 
Last edited:

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Posted code is OK and the Second one I made is also OK. I can use either if I want.
All I like to learn is to store the message in ROM and use pointers to Display using interrupt(if needed).
Too many messages is giving me jumbled characters. The code works with the jumbled display.

I will be using 5 ADC inputs. I dunno your approach. Read ADC using IRQ or all in main, or not.
If LCD part is figured out I can finish an earlier project that is also halted due to jumbled characters. No one gave me a way to figure out the issue. I cannot find any solution in the net too. Been a couple of years since I gave up on that.
 

JohnInTX

Joined Jun 26, 2012
4,787
Posted code is OK and the Second one I made is also OK. I can use either if I want.
All I like to learn is to store the message in ROM and use pointers to Display using interrupt(if needed).
Too many messages is giving me jumbled characters. The code works with the jumbled display.

I will be using 5 ADC inputs. I dunno your approach. Read ADC using IRQ or all in main, or not.
If LCD part is figured out I can finish an earlier project that is also halted due to jumbled characters. No one gave me a way to figure out the issue. I cannot find any solution in the net too. Been a couple of years since I gave up on that.
For the ADC, I just run it in main. Each time through the main loop, examine the DONE bit. If DONE, take the value and store it in an array of channel values, change the channel and start the conversion on the new channel. That way, the last ADC value for each channel is always present in the array - when you decide you need the value of something, just get it from the array, you don't have to start a conversion and wait... (there's that word 'wait' again). You probably want to average the readings so you can keep a sum and count for each channel and post the averaged reading in the array of current values. Chew on that while I look at your code.

I remember now about the LCD. The library routines want to have everything in RAM... We can fix that.
 

JohnInTX

Joined Jun 26, 2012
4,787
I thought a bit on the LCD and revisited post #29 which discusses why you can't have lots of strings for the LCD in a MikroC program. Here's how I would approach it.
Use the LCD libraries for now - I looked at the code and it doesn't generate any serious delays.
Use the code snippet in #29 to output fixed strings of characters from ROM.
Things like Lcd_Cmd can be used as is to locate the cursor etc. for now. There aren't that many of them.
Use the standard LCD libraries for variable data.

This approach has some problems mainly, its messy. To get around that, I would introduce the concept of a 'screen' or 'form' if you prefer. Each thing the display has to show is defined as a form of fixed text that gets filled out with some variable data. For example:
VOLTS 14.2
AMPS 22.3
The volts and amps text is constant from ROM and the values are displayed from RAM. To maintain this display could use two routines
showVAform() // set up the form with the VOLTS and AMPS text. Clear out any other stuff. Leave the value fields blank. This routine uses the display from ROM routine.
updateVAform() // get, format, locate cursor and show the current values.
The reason for having two routines is because you don't need to refresh the constant text - saves time.

Create a form for each display that has text and variable data. Set up the form then update the data - nice. A side benefit of this is that the forms begin to look alike and that can simplify coding. I did one that had 60-70 forms in several different formats. Not only did it keep me sane but the user interface was consistent.

What do you think about the 'forms' concept? If you like it, make the VA form (or something like it) and get that working. You also can try sending a list of ROM messages to the display to confirm that the basic concept of ROM->display works (I think it will). When you get that far, stop and I'll show you how to embed the LCD controls into the ROM string so you don't need multiple calls to make it. I'd do it now but want to test it here first.

Have fun.
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Hmm.
OK...I will try what you asked.
When you say "What do you think about the 'forms' concept?" I get o_O
Cause I never tried that. :(

I wanted to know what post #29 was all about before as that part was all new to me:oops:

Let me try first. I will get back to you as I have finished the code or if get stuck.
Thanks
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
So like after 4 hrs I was able to change the new code (TMR0 and TMR1) as follows. ( I liked your beeping style so I wanna do tht :p)
As before TMR1 is the 1Hz Generator. I can toggle the Led EXACTLY, 1sec ON and 1sec OFF.
TMR0 does the following,
Flash LED ( at around 5Hz, to indicate the PIC is running, which by the way was a later addition -- and is also used for testing the derived timer periods from scope)
Beeping..All kinds of...:D
Switch debouncing.

I changed TMR0 to generate 1ms interrupts and from that another 5ms one is derived, ( I am not using the 5ms one as of yet but I can toggle a pin to check from Scope)
Different beeps for different occasions. Button beeps, and alarm beeps and all that, are there.
Two button debouncing and flag setting is done...
All Good.

Tomorrow I will add a simple routine like a startup message and a Battery Change routine with messages and post back. :)
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
It's working.
All the messages are displaying correctly :D
So far I am trying using blocking type from MikroC lib.
The messages are in ROM. Copied to RAM and displayed.
Once satisfied I will try to use the interrupt to update the LCD.
Thanks John. :)
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
@JohnInTX
You said some thing about averaging...!
I am using 100nf caps on each ADC. Do I still need software averaging ?
If so I like to know how it is done. A simple example will be enough to give me an idea.
My readings gives ±0.02 compared to my Fluke. Which I believe is quite OK for this.
What do you think ?
 

JohnInTX

Joined Jun 26, 2012
4,787
It's working.
All the messages are displaying correctly :D
So far I am trying using blocking type from MikroC lib.
The messages are in ROM. Copied to RAM and displayed.
Once satisfied I will try to use the interrupt to update the LCD.
Thanks John. :)
Cool! I assume by 'interrupt' you mean that you will schedule display updates with an interrupt-driven tik. I wouldn't actually do the LCD write itself within an interrupt routine. The PIC is not particularly suited to doing lots of stuff in an interrupt service routine.

You said some thing about averaging...!
I am using 100nf caps on each ADC. Do I still need software averaging ?
If so I like to know how it is done. A simple example will be enough to give me an idea.
My readings gives ±0.02 compared to my Fluke. Which I believe is quite OK for this.
What do you think ?
If you are getting accurate, stable readings you can probably dispense with averaging - at least for now. I've always used some sort of software averaging so that I have control over the system response to changes etc. but it sounds like you can skip it for now. In the event that you eventually decide you need it you can make it easier to add if you code with it in mind - something like post #65. Write your main code to expect always valid values for each analog input, with that value being generated 'behind the scenes'. In the simple case, each ADC channel gets read in sequence, scaled appropriately (to Volts, Amps etc) and posted in the analog inputs array. Main routines just index the array to read the last-posted value whenever they need it.
ADC_VALUE(i) ->scaling arithmetic(i) -> array -|--> main code
The array of values serves as the interface between the ADC driver that produces values and main which consumes them.

If you added averaging, it would go here:
ADC_VALUE(i) ->averager ->scaling arithmetic(i) -> array -|--> main code
All we have done is place the averager between the raw ADC code and the scaling arithmetic. For practical purposes, the consumer (main) doesn't know the difference. How the averager actually is coded can wait until we decide we need it. As long as the code is modular like I suggested, it can be plugged in later.

EDIT:
You might want to give some thought on how the values in the array are expressed. One obvious way would be to store as an array of floats, each scaled to be Volts/Amps etc. That would work but one refinement on the scheme might be to ask 'How often do I need to read these values?' vs. how often are they generated. Rather than do the complete read-scale-store Volts/Amps as floats each time you get a new ADC reading, consider just storing the raw (or averaged) ADC reading in the array as an unsigned int. Access the values in the array using a function i.e. getVolts() - a function that gets the valid ADC reading from the volts channel and scales it. Now, you only have to run the math when you need it - AND - it makes your ADC code simpler as you don't have to pick a scaling routine for each channel. Thinking on it while writing, I like this approach better. So the flow becomes:
ADC_VALUE(i) -> array --|--> getVolts() -> main. getvolts() knows the index i where the volts value is in the array, gets and scales it returning Volts.
I like that much better.

Hopefully, I haven't confused you. I thought about deleting the original scheme but its a good illustration of the process of re-examining ideas and seeing better ways to do things.

Looks like you are making great progress!
 
Last edited:

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
I would get back to averaging later then.

I am making progress I never thought I could u know. Thanks to you. :)
I am planning to update LCD via interrupt exactly like you said.

Thanks
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Hey
I ran into a little hiccup.


C:
sbit Charger_On at RB1_bit;
sbit Back_lit at RB2_bit;
sbit AC_On at RB5_bit
sbit Float_Charge at RB0_bit;
C:
#define Backlitmask 0x04  // Back light at RB2
#define Backlit_On (PORTBimg |= Backlitmask) // Back light On
C:
//In IRQ
PORTB = PORTBimg;
C:
//In MAIN
Backlit_On;
I need to switch the ports as above from time to time. As they are on PORTB, I am getting pulses when buzzer sounds even though I can turn them on fully.

I like to know how to turn them ON and OFF, fully without "PORTBimg' and "IRQ" effecting the output.
I tried a lot but I am missing something.

Right now the back light is ON in main but when the buzzer sounds the LCD dims. So I checked from scope and I saw pulses at output during beeping

I believe you will have a masking method to ON and OFF a single output. I cannot think of it yet

{ed}
The LCD messages are working using interrupt as long as I comment out the "PORTC = PORTCimg;" part in IRQ.
If I do use "PORTC = PORTCimg;" the LCD is giving jumbled and strange characters. I think it is due to IRQ updating PORTC with shadow register.
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
If you need to do output to a shadowed port (PORTB in this case) in both interrupt and main routines, you have to temporarily disable the interrupts when doing shadowed outputs to PORTB from main. (and you can't mix shadowed and non-shadowed on the same port). I thought the LCD stuff was all on PORTC?
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
LCD stuff is on PORTC and so is the buzzer. Above post is corrected.:oops:

The buzzer is messing up the LCD messages. So I moved it to RA4 which is unused and is not an analog input either.
Now it is working...PORTC is free used only for LCD. So Now I believe the LCD issue is fixed. No Message jumbling and character running around. Messages are displayed as long as I need ( ALL OF EM ) using flag bits toggled and timed in IRQ. Real Nice approach you taught me. Thank you John. :)

Is there any masking method to fully switch ON an output. Since there are outputs and inputs on PORTB which is shadowed. Right now I am pulsing a LED and lighting LCD backlite on PORTB. Backlite is stable. So I think it is working but I need to make sure.
If there is a masking method, I like to know the "#define" thingy :D

Will ADC readings ( PORTA )be good while the buzzer is on PORTA, since PORTA is now shadowed ?
 

JohnInTX

Joined Jun 26, 2012
4,787
Hmmm.. I thought we were using the PWM output on RC2 for the 50% duty cycle buzzer? If you were using a shadowed PORTC interrrupt-driven beep generator with the LCD library, that would cause problems but using the PWM shouldn't.

The mask thingie is just to indicate to the compiler which bit to set/clear in the shadow register.
C:
#define Amask 0x01
ShadowRegister |= Amask;  //generates bsf ShadowRegister,0
ShadowRegister &= ~ Amask;// generates bcf ShadowRegister,0
PORTx = ShadowRegister; // writes the new image to the port
You can mix ADC readings and digital outputs on shadowed PORTA if you want. I like the PWM better.

To turn on an output in a shadowed port that is also under interrupt control you need to disable the interrupt that affects the shadowed operation (timer interrupt here I think). If you don't you have the possibility of working with an out of date shadow register (it may get changed under interrupt while you are in the process of changing it in main). That's one of the hassles with shadowing. Unfortunately IMO, you still need it on the midrange PICs. In fact, my first big code retrofit to shadowing was on a battery charger controller similar to yours. After years of service, they began installing them in a different charger (more noise) and we had problems. Going from non-shadowed to shadowed in assembly language is a first rate pain. Much better to deal with it from the get-go. A real good thing to do is to try to migrate to the 18F or Enhanced MidRange. Both have LATx registers that eliminate the r-m-w problem to PORTx.

Sounds like you are getting close, though. Sorry I kind of lost focus here while working on staff duties. Hopefully, that is put to bed for awhile.
 
Last edited:

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Then ADC readings will be OK on shadowed output to Buzzer ?
I believe it is fixed. The LCD part and the buzzer is OK.

Now to see about the Masking, I will check and get back if there is an issue. If not I can finish the code.
Thanks John
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
I am using more than 40 messages each almost fills the LCD and running beautifully.
Used RAM (bytes): 68 (19%) Free RAM (bytes): 284 (81%)
Used ROM (program words): 3088 (38%) Free ROM (program words): 5104 (62%)
Really Nice. :D
Now I am at the final step.
I think I hit a road bump as I like to know how to display a 1 hour count down like "01:00:00" in the LCD.
I have a 1 second timer already setup. Which can increment to 60 seconds, then I believe I need to increment another counter every 60sec and so on.

I still have no idea on how to do that. i.e to Display the value onto LCD specifically. :(

I will need to reset a flag when count is reached to 00:00:00.

It will be great if I know how to load the timers ( LCD display counter ) with any value I need to and then count from there

Any Pointers ? :)

I know we did that before but to LCD, I like to know
 

JohnInTX

Joined Jun 26, 2012
4,787
@R!f@@
I would probably keep internal time in seconds stored in an unsigned integer. That gives you 18+ hours and it is easy to do arithmetic with. Do all the control stuff with it; CycleSecs == 0 means turn it off - that kind of thing.

To display, you have to convert that internal representation of time in seconds to hh:mm:ss as ASCII for the LCD. First, you have to break up the seconds into hh:mm:ss. You can do that with DIV and MOD operations:

C:
unsigned int SecsTime;  // number to be converted - maybe its a parameter to a function..  use a copy because it gets clobbered in this example
unsigned char Hours,Minutes,Secs;

// write some function that does this - pass parameters to your liking or use globals
Hours = (unsigned char)(SecsTime / 3600);  // 3600 secs in an hour
SecsTime = SecsTime % 3600;  // compute remaining seconds
Minutes = (unsigned char)(SecsTime / 60);  // number of minutes
Secs = (unsigned char)(SecsTime %60); // remaining seconds.
Now your time is Hours:Minutes:Secs in 3 variables. They are not ready to display yet because they are still short integers, not ASCII..
I would probably have a function that takes those chars and emits 2 ASCII characters, probably to the display directly:
C:
void show2(unsigned char d)
{
   LCD_outchar_CP ( d/10  + \'0');  // output 10's digit as ASCII
   LCD_outchar_CP (d%10 + \'0');  // output 1's digit as ASCII
}
Then the time output routine is something like:
C:
void WriteTimeToLCD_CP(unsigned int SecsTime)
{
convertSecsTohhmmss(SecsTime);  // dissect seconds into Hours, Minutes,Seconds
show2(Hours);
  LCD_outchar_CP(':');
  show2(Minutes);
  LCD_outchar_CP(':');
show2(Seconds);
}
How you want to maintain the Hours, Mins, Seconds is up to you. You can put them in a structure and pass a pointer to it in the convert routine or make them global. Its up to you - that's why I didn't show the converter as a full function - just the math required.
I like the idea of a 2 digit output routine because you might need one later. You also could convert the time from seconds to individual digits in one swoop but a separate routine is probably more useful in this and other projects i.e. always maintain the time in seconds, the math to hh:mm:ss is the same then you can use different routines for display - this one for ASCII LCD and a new one to take the same hh:mm:ss and unpack it to a 7segment LED when that's what you have.

If you used a structure, you could use it to pass time INTO the system as well. Get hours, minutes, seconds from some entry device, load the structure fields then run the conversion the other way to convert hh:mm:ss to an unsigned integer of seconds.

I'd do something like that...
Note: I desk-checked the math but not the syntax. Fix as req'd.
 
Last edited:
Top