SLA Charger/Backup System

MMcLaren

Joined Feb 14, 2010
861
I see you have the LCD back-light control and the piezo buzzer on the same port as the LCD interface and I suspect that's your problem. If the MikroC LCD routines aren't clobbering those two pins then you just need to take care not to clobber the LCD pins in your interrupt routine when you go to change either of those two pins. There's a chance you might not have to ditch the MikroC LCD library routines...

Are you using a 'shadow' variable for the back-light pin and the piezo speaker pin?
 
Last edited:

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Is it me or is it just a standard code using MikroC library.
I can do that using MikroC library.

I am trying to use the interrupt to put messages into LCD
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
I see you have the LCD back light control and the piezo buzzer on the same port as the LCD interface and I suspect that's your problem. The MikroC LCD routines are probably overwriting those two pins. Hmm...
My issue is something I ran into before and resulting in the project halt.
This new project also showed the same result.

I need to put around 6 to 8 messages to the LCD at different times. When I use the MikroC library, I can display around 4 messages. As I put more messages it gets jumbled up , all the messages. John guessed it is due to blocking routine in the MikroC library.
I need to use some delays and the buzzer with LCD. With blocking code this cannot be done.
That is why I like to know if there is way to use interrupt to drive the message part using interrupt driven Timer delays.
PIC space sky rockets even with 4 messages.
 

JohnInTX

Joined Jun 26, 2012
4,787
My issue is something I ran into before and resulting in the project halt.
This new project also showed the same result.
I need to put around 6 to 8 messages to the LCD at different times. When I use the MikroC library, I can display around 4 messages. As I put more messages it gets jumbled up , all the messages. John guessed it is due to blocking routine in the MikroC library.
I need to use some delays and the buzzer with LCD. With blocking code this cannot be done.
That is why I like to know if there is way to use interrupt to drive the message part using interrupt driven Timer delays.
PIC space sky rockets even with 4 messages.
I actually think it may be due to another cause but lets put that aside from now.

Rifaa is fortunate that everyone has jumped in to help but we need to get organized and prioritize what we need to do and agree on an workable approach from the several suggested. To do otherwise will create confusion and wasted effort, I am afraid. One source of confusion is that this current iteration started as what to do with ICSP pins and now we are back to the battery charger on a target board. I am not scolding, just trying to focus our efforts. To that end, I observe these things to do more or less in order:

As it stands, the requirement to generate a 3KHz tone to drive the beeper with the current pinout will drive the timing design. Its not a big deal since we did that in the LED dimmer / software PWM thing Rifaa referenced. It does mean though that if we are to avoid blocking code, the 3KHz will be generated by an interrupt every 150us. We were successful in doing that in the LED dimmer so I would propose we use it.

What timer to use for the 150us is probably not important since the current pinout does not support the hardware PWMs. I would use TMR0 from the LED dimmer and keep TMR2 in reserve in the event that the hardware PWMs become available.

The LED dimmer did all of its other functions in main using round-robin scheduling and timing derived from the 150us interrupt. All functions were non-blocking so as not to interfere with each other. I would recommend we stick with something like that.

A base unit of time, the system TIK, was derived from the 150us interrupt to form the basic timebase for all other timed operations.

The timing for the main functions was derived from a set of registers that were decremented to 0 by the system TIK. When called, a main function had only to examine its timer for 0 to see if it was time to do the next thing.

That describes the main structure that we got to on the LED dimmer (and one that I use constantly so... guess why I like it!).

Next, the utility stuff like ADC, LCD, switches etc.
The described framework handles this sort of thing as non-blocking main functions whose timing was not as critical. Details are TBD but here are some things to chew on..

ADC:
I would schedule the ADC like any other main task (non interrupt). When called, any ADC result is stored in the appropriate place, the channel is bumped and the ADC is started. Then it leaves. Its likely that the conversion will be done by the time that the ADC function gets called again but if not, just exit and wait until the next time. I recommend averaging the ADC readings - in a battery charger you want to filter the ripple and averaging is a good way to do that. The result is that as long as the ADC function is called each pass through main, it emits a continuous stream of averaged, up to date values that main can access whenever it needs to.

BUTTONS:
We did debouncing in the LED dimmer. There are countless other ways to do it but we need to agree on an approach.

LCD: Assuming everything else is non-blocking, we actually might get away with MikroC's libraries (bet you didn't expect that..). After init, it probably doesn't wait for long for a character write and it would not interfere with the beeper etc. It would just stall the ADC etc. temporarily while writing to it - still not desirable. The code that Picbuster posted assumes that the databus occupies a nibble. Rifaa's circuit takes advantage of Mikro's ability to scatter the databus over various bits and ports. So we have to decide how to do that. I would probably recommend writing the LCD code based on that Picbuster posted and add utilities as required.

The problem with the LCD messing up when lots of messages are stored may be a problem with MikroC - IIRC it copies ROM strings to RAM during startup so that it can more easily access them at runtime - this chip doesn't have a lot of RAM.. Time to inspect the generated assembler code.

Those are the starters as I see them. Its great to have all of this talent on tap if we can agree on the goal and path to get there. And Rifaa, make sure that your spec is firm in your mind. Clean up any stray ideas and post it. It doesn't have to be done my way, just in a known way.

From ISCP pins we evolve to project management. Hey, we can do it all!
Comments invited!
 
Last edited:

MMcLaren

Joined Feb 14, 2010
861
I understand the board has already been made. Is there a chance one of the PWM pins (RC1, RC2, RB1, RB2, or RB3) could be used to drive the piezo speaker to eliminate the need for such a demanding 150-us interrupt interval?
 

JohnInTX

Joined Jun 26, 2012
4,787
I understand the board has already been made. Is there a chance one of the PWM pins (RC1, RC2, RB1, RB2, or RB3) could be used to drive the piezo speaker to eliminate the need for such a demanding 150-us interrupt interval?
HI MIke. I think Rifaa said he already had the boards but at one point said that he could move some stuff.. ? I wanted to present at least a framework to get everyone working on the same thing. It certainly doesn't have to be done my way but the non availability of the hardware PWMs drove the 150us thinking and the rest flowed from there. Different approaches may be better if we have the hardware PWM but that's why I asked Rifaa for a spec in a single place. Any workable approach would certainly meet with my approval and the more good ideas, the better. Starting a new thread is a step in the right direction to get a known spec...
 

MMcLaren

Joined Feb 14, 2010
861
I didn't mean to detract from your organizational effort. Just trying to see if the specifications were frozen.

I won't try to contribute or interfere. I can never keep up with Rifaa threads anyway. In the time it takes me to make a thoughtful reply, several posts have occurred and everyone is working on a new problem (lol)...
 

JohnInTX

Joined Jun 26, 2012
4,787
I didn't mean to detract from your organizational effort. Just trying to see if the specifications were frozen.

I won't try to contribute or interfere. I can never keep up with Rifaa threads anyway. In the time it takes me to make a thoughtful reply, several posts have occurred and everyone is working on a new problem (lol)...
I didn't take it as detracting at all. Specs still seem flexible and hope you'll be able to chip in. I share your observation about things changing while you are writing and that's why I thought since there seemed to be a lot of options in play, it wouldn't hurt to firm up the spec. I've been busier than usual lately and your contributions would be most welcome. If you guys can agree on the path to take, I'll just let you have at it.
 

JohnInTX

Joined Jun 26, 2012
4,787
The problem with the LCD messing up when lots of messages are stored may be a problem with MikroC - IIRC it copies ROM strings to RAM during startup so that it can more easily access them at runtime - this chip doesn't have a lot of RAM.. Time to inspect the generated assembler code.
Yeah.... I remember now. And I just tested it.

The problem with MikroC's LCD routines is that they use char and *char (as in a string) as parameters. Unfortunately, those storage classes are 'initialized RAM' i.e. whatever text is in your source has to fit into the first two RAM banks at runtime, all at once. My guess is that's why a few strings work but it gets wiggy when you have lots of characters in strings. Running out of RAM should generate an error but maybe you aren't running out so much as not managing the FSR. From the manual:
Limits of Indirect Approach Through FSR
Pointers with PIC16 are “near”: they carry only the lower 8 bits of the address. Compiler
will automatically clear the 9th bit upon startup, so that pointers will refer to
banks 0 and 1. To access the objects in banks 2 or 3 via pointer, user should manually
set the IRP, and restore it to zero after the operation. The stated rules apply to
any indirect approach: arrays, structures and unions assignments, etc.
I stepped the code through Lcd_Out and it indeed uses FSR indirect to fetch the character but it doesn't manage IRP to access banks 2,3. If you have enough messages to bust into those banks, you are sunk. That stinks but that's why I write my own libraries. I wouldn't mess with it.

A better workaround is to declare your strings in ROM then write a routine to access those. You can still use MikroC LCD libraries for the character output. I tried this and it looked like it worked on the sim:
Code:
  const char code Msg[] = "This is a message in ROM";   // declare message in ROM
const char code *RomP; // declare a pointer to characters in ROM
char CharBuf;  // temp storage

// basic string output from ROM:
   RomP = Msg;  // set pointer to beginning of string
   while (*RomP != '\0'){
     CharBuf = *RomP; // fetch char from ROM to RAM and bump pointer
     RomP++;
     Lcd_Out_CP(&CharBuf);  // use MikroC lib routine to output the char
   }
Give that a try for your long strings. If you have to mix data then multiple calls will be necessary to mix data and constant OR you can use sprintf to build a string in RAM then use MikroC's library routines to output that. Keep in mind that sprintf adds a big chunk of code (too big for the free version) so you might have to get a little creative depending on what your data looks like.

I don't think using MikroC's LCD libraries are too bad time wise. Once the init is done, it adds a couple of NOPs here and there for the E pulsewidth etc. but nothing out of line.
I'd say use them for now.

Anyway, this just answers one question i.e. Can you use MikroC's LCD libraries. Answer: Yes with some help.
On to the rest of the list.
 

MMcLaren

Joined Feb 14, 2010
861
Hey John. I had forgotten that I ran across this a long time ago when using BoostC. I ended up using an 'overload' function for (BoostC specific) ROM strings;
Code:
   void PutDAT(rom char *data)  // lcd data (RS=1) string
   { char work; char ndx = 0;   //
     while(work = data[ndx++])  // while not end-of-string
       PutDAT(work);            //
   }                            //
Once I did that, I was able to verify that the operand in my PutDAT("test string") type statements were staying in program memory.

Now that I'm trying to use XC8 for everything, I need to see how to do this in XC8. I will study your example.

Way to go, John... Thank you... I hope this helps Rifaa, too...
 
Last edited:

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Wow. I love you guys. I am indeed lucky and thankful.

Lotta stuff to take in though.
Throw at me one at a time guys.

Sorry I asked a lotta different questions. The issues arise when I was making the board and I did not plan ahead. Never used ICSP before and never used this PIC's ADC. Ones the issues was answered I went on designing the board.

I have a full working MikroC not a demo. Cause earlier I had an issue with code limitation and one member gave me a "solution" to fix that. After that I can program more than 2K.

First change to TMRo to generate the system TIK and buzzer.
Second I will try the suggested "ROM" thingy and try to understand that part.

Do you want me to swap the buzzer to RC2 (CCP1) with RC7. No big deal. That would make you guys happy I think.
On second thought I will add jumpers to it so I can swap RC2 with RC7 any time if needed. That way I would not loose time. Cause to swap I have to come to workshop. Which is far (20 minutes walk).
***Believe me if you re not driving any more it is far. And at midnight too. Project is at home PC. I do the code stuff at home after finishing at workshop. And to meet you guys I had to stay awake till 4.00 AM

I believe you have addressed an old issue I had way back and provided a solution to that and also to this project.
Thank you both.
 

JohnInTX

Joined Jun 26, 2012
4,787
Beeper is on PWM1, I like that better.
I would return to the code that uses TMR0 as the system tik and strip out the firmware PWM code that drove the beeper. Then configure PWM1/CCP1 for 3KHz 50% duty cycle - no interrupts, RC2=output 0. It should beep. Turn it on/off using CCP1CON CCP1M<3,0>
Also, play with the LCD messages using the construct above and see what that does.
Have fun.

EDIT: The Lil Professor says for 3KHz 50% use (decimal):
Prescaler = 4
PR2 = 166
CCPR1L = 84
CCP1CON<5:4> = 2

@MMcLaren
Now that I'm trying to use XC8 for everything, I need to see how to do this in XC8. I will study your example.
XC8 should be better as it automatically generates different runtime code for values of the same type that can exist in ROM or RAM. IIRC any non-auto const is in ROM by default and it figures it out by itself. Haven't thoroughly tested it, though. XC8's HiTech progenitor did it that way but had occasional problems with pointers to >64K with complex types like struct.
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
IIRC: If I Recall Correctly. It generally means I am too lazy to look it up.
Coding: Don't be afraid to comment out large chunks with #if 0 / #endif so that you can concentrate on the task at hand. It may be time to consider multiple files, one for each subsystem.
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
One question
Where does below goes
Code:
  RomP = Msg;
And
Code:
     while (*RomP != '\0'){
     CharBuf = *RomP; // fetch char from ROM to RAM and bump pointer
     RomP++;
     Lcd_Out_CP(&CharBuf);  // use MikroC lib routine to output the char
   }
Does above goes in Main ?
 

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
Tried the LCD code. Had no luck. Just wanna see. Guess I need some help on that.

I wrote the code again to get familiar with it
C:
--------------------------------------------------------------------------------
********** IO Configuration ************
RA0 as AN0 - Senses Vbatt      -- Battery Voltage Monitor ADC in.
RA1 as AN1 - Senses Charge_CS  -- Battery Charge Current Monitor ADC in.
RA2 as AN2 - Senses MainPSU_OK -- Main Power Supply 18V to 19.5V Monitor ADC in.
RA3 as AN3 - Senses SubPSU_OK  -- 13.5V PSU Monitor ADC in.
RA4 not used.
RA5 as AN4 - Senses Charger_OK -- Senses the Charger output Monitor ADC in.
RA6 as Digital In - System check Switch - Active high.
RA7 as Digital In - Battery Change Switch - Active high.
RB0 as Digital Out - Active High output - Float Charging Signal.
RB1 as Digital Out - Active High output - Charger Switch on Signal.
RB2 as Digital Output -- LCD Backlit Enable.
RB3 not used.
RB4 as Digital In - Senses AC_OK -- Active High Signal.
RB5 as Digital Out - Active High output - AC_ON Signal.
RB6 as PGC for Debugging.
RB7 as PGD for Debugging.
RC0 to LCD RS
RC1 to LCD Enable
RC2 as Digital Output -- Buzzer Output
RC3 to LCD D5
RC4 to LCD D6
RC5 to LCD D7
RC6 to LCD D4
RC7 Not Used
------------------------------------------------------------------------------*/
// ****** LCD Configuration ************
sbit LCD_RS at RC0_bit;
sbit LCD_EN at RC1_bit;
sbit LCD_D4 at RC6_bit;
sbit LCD_D5 at RC3_bit;
sbit LCD_D6 at RC4_bit;
sbit LCD_D7 at RC5_bit;
sbit LCD_RS_Direction at TRISC0_bit;
sbit LCD_EN_Direction at TRISC1_bit;
sbit LCD_D4_Direction at TRISC6_bit;
sbit LCD_D5_Direction at TRISC3_bit;
sbit LCD_D6_Direction at TRISC4_bit;
sbit LCD_D7_Direction at TRISC5_bit;
// End of LCD Connections.

// Control bit Assignments.
sbit Sys_Check at RA6_bit;
sbit Batt_Change at RA7_bit;
sbit Float_Charge at RB0_bit;
sbit Charger_On at RB1_bit;
sbit AC_OK at RB4_bit;
sbit AC_On at RB5_bit;
sbit Back_lit at RB2_bit;
// Flags for above
bit SysCheck, BattChange, FloatCharge, ACOK, ACON, Backlit;
// Rest of Flags
bit BattStatus, CheckBattery;

// End of Control bit Assignments.

/******* PROGRAM CONFIGURATION **********/
#define TMR0period_us 150

// Mask bits


/******* CALCULATED CONSTANTS ***********/
#define TMR0reload 256-TMR0period_us



/********  ISR ***************************/
void interrupt(){

}  // End of IRQ service


/******** Main *************************/
void main(){
    INTCON = 0;                        // Clear IRQ's
    PIE1 = 0;                          // Clear IRQ's
    PIE2 = 0;                          // Clear IRQ's
    OPTION_REG = 0x88;                 // No pullups TMR0 runs as Tcyc

    ANSEL = 0x0F;                      // RA <0:4> as Analog Inputs
    ANSELH = 0x00;                     // Rest of Analog as Digitla I/O
    CM1CON0 = 0x07;                    // Disable comparator 1
    CM2CON0 = 0x07;                    // Disable comparator 2
  
    PORTA = 0;                         // Clear Ports
    PORTB = 0;                         // Clear Ports
    PORTC = 0;                         // Clear Ports
    TRISA = 0xFF;                      // PortA as Inputs
    TRISB = 0xD8;                      // PortB 'b11011000'
    TRISC = 0x00;                      // PortC as Outputs
// -------- Clear Flags   ----------------------
    SysCheck = 0;                      // Clear Flag bit
    BattChange = 0;                    // Clear Flag bit
    FloatCharge = 0;                   // Clear Flag bit
    ACOK = 0;                          // Clear Flag bit
    ACON = 0;                          // Clear Flag bit
    BattStatus = 0;                    // Clear Battery Status flag
    Backlit = 0;
    CheckBattery = 0;
// -------- Fire up Timers  --------------------
    TMR0 = 0;
    INTCON.T0IF = 0;
    INTCON.T0IE = 1;
    TMR2 = 0;
    PR2 = 166;                         // PR2 Register is 166 decimal
    T2CON = 0x05;                      // PostSclr = 1:1,TMR2 ON, PreSclr 4
    PIR1.TMR2IF = 0;                   // Clear TMR2 to PR2 interrupt Flag
    PIE1.TMR2IE = 0;                   // TMR2 to PR2 Match interrupt Disable
    CCPR1L = 0x54;
    CCP1CON = 0x3F;
    INTCON.GIE = 1;

    Lcd_Init();
    Lcd_Cmd(_LCD_CLEAR);
    Lcd_Cmd(_LCD_CURSOR_OFF);

while(1){

   
     }
}
I tried playing with the values to change the frequency but it did not help. Turning the beep on or off was a little difficult as I have to change the CCP1CON lower bits like you said. Is it actually how to do it or is it should a flag bit do it. I did not try to use a flag bit yet as I dunno how you plan to use IRQ.
I did take a screen shot though for you to see the buzzer out

RIGOL Print Screen12-Jan-16 2_24_56 AM.314.png

As you can see the frequency is not as expected.
Or am I doing something wrong ?
I dunno if TMR0 is working or not yet.

I need a confirmation on the code

Oh yeah one more issue as before I believe.
The LCD back_lit can be turned on if only I Disable the Timer stuff. I am not even running IRQ. Enable the IRQ and the back_lit turns off even when Back_lit = 1;
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
@R!f@@
A quick look:
Disable GIE. You don't have any interrupt service routines yet.
The PWM freq is 1/2 what is expected. The PWM setup looks OK. Is the internal oscillator running at its default 4MHz instead of 8MHz?
CCP1CON should be 0x2F (the two LSB of the PWM duty cycle should be 10, not 11). You duty cycle is off accordingly.
We'll worry about the LCD/Backlight and TMR0 when this is working.
 
Last edited:

Thread Starter

R!f@@

Joined Apr 2, 2009
9,918
To make sure I added
C:
 OSCCON = 0x77;
And
C:
CCP1CON = 0x2F;
INTCON.GIE = 0;
And Viola :D

RIGOL Print Screen12-Jan-16 5_32_17 PM.915.png

And this thing is LOUD...I need it to be loud
 
Top