|
|
View previous topic :: View next topic |
Author |
Message |
swapnil
Joined: 15 Dec 2016 Posts: 13 Location: delhi
|
pwm working fine i want, sensing the bout, pwm should vary |
Posted: Fri Jan 06, 2017 3:15 am |
|
|
Code: |
#define AN0_AN1_AN2_AN4_VSS_VREF
#fuses HS,PROTECT
#use delay (clock=20MHz) // 4MHz
#define use_portb_lcd TRUE
#include <lcd.c>
#define CCP1_PWM PIN_C2
#define MAINS_SWITCH PIN_C3
#define CHARGING_LED PIN_C4
#define PCU_LOAD PIN_C5
#define LOAD PIN_C6
#define LOW_BATT_LED PIN_C7
float mean_adc(byte channel) // Reads the adc port 30 times and gives the mean value
{
const int8 AVERAGING_CYCLE_COUNT=32;
int8 i;//mean_total = 32;
int16 mean_total =0;
float mean = 0;//,mean1 = 0;
set_adc_channel(channel);
delay_us(100);
for (i=1; i<=AVERAGING_CYCLE_COUNT; i++)
{
delay_us(500);
mean_total += read_adc();
}
mean = ((float)mean_total)/ AVERAGING_CYCLE_COUNT;
return(mean);
}
//********** Main Program*******
void main ( )
{
const byte solar_voltage_channel=0;
const byte battery_voltage_channel=1;
float battery_voltage,bout,solar_voltage,sout;//,bin,sin;
set_tris_c(0b00000011);
setup_adc_ports(ALL_ANALOG); // setting up all port a as analog
setup_adc(adc_clock_div_32);
setup_ccp1(ccp_off); // Configure CCP1 as a PWM
setup_timer_2(T2_DIV_BY_4, 249, 1); // Set PWM frequency to 8kHz
//set_pwm1_duty(500);
enable_interrupts(GLOBAL);
delay_ms(10);
lcd_init();
delay_ms(100);
printf(lcd_putc,"\f WELCOME TO ");
printf(lcd_putc,"\n LUXNTEK SOLAR ");
delay_ms(1000);
while(TRUE)
{
solar_voltage=mean_adc(solar_voltage_channel);
sout=((solar_voltage*5)/255)*35;
battery_voltage=mean_adc(battery_voltage_channel);
bout=((battery_voltage*5)/255)*35;
//delay_us(1000);
printf(lcd_putc,"\fSOLAR =%2.1fV",sout);
printf(lcd_putc,"\nBATTERY =%2.1fV",bout);
delay_ms(500);
if((sout > bout)&&(bout >2 && bout <=14.50))
{
setup_ccp1(CCP_pwm);
set_pwm1_duty(500);
delay_ms(100);
set_pwm1_duty(400);
output_high(CHARGING_LED);
delay_ms(200);
output_low(CHARGING_LED);
delay_ms(200);
printf(lcd_putc,"\fBATTERY CHARGING");
printf(lcd_putc,"\n BOOST MODE ");
delay_ms(400);
}
if((sout > bout)&&(bout > 14.55))//&& (bout <= 15.5 ))
{
setup_ccp1(CCP_pwm);
set_pwm1_duty(100);
delay_ms(200);
set_pwm1_duty(200);
delay_ms(500);
set_pwm1_duty(300);
output_high(CHARGING_LED);
delay_ms (600);
output_low(CHARGING_LED);
delay_ms(100);
printf(lcd_putc,"\fBATTERY CHARGING");
printf(lcd_putc,"\n TRICKLE MODE ");
delay_ms(500);
}
else
{
//setup_ccp1(CCP_OFF);
output_high(CHARGING_LED);
delay_ms(300);
printf(lcd_putc,"\fSOLAR =%2.1fV",sout);
printf(lcd_putc,"\nBATTERY =%2.1fV",bout);
delay_ms(200);
}
}
/////////////////////////////LOAD ////LED INDICATION ///////////////////////////////////////////////////////
//if dawn to dusk and load on batt
if((sout < 5)||(bout > 12))
{
output_high(LOAD);
}
if(bout <= 11)
{
output_high(LOW_BATT_LED);
output_low(LOAD);
}
else
{
output_low(LOW_BATT_LED);
}
// Mains SNS PCU Load On
if((input(PIN_C3)==1) && (sout > bout) && (bout > 14.6))
{
output_high(PCU_LOAD);
setup_ccp1(CCP_pwm);
set_pwm1_duty(500);
}
else if(bout <= 13.9)
{
output_low(PCU_LOAD);
}
}
|
ROM space finished kindly suggest for less space. _________________ heroswap1981 |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9229 Location: Greensville,Ontario
|
|
Posted: Fri Jan 06, 2017 5:59 am |
|
|
2 possible methods
1) get rid of floating point math ! It is NOT necessary and it uses a LOT of code space. As well FP math takes a LOT of time !I've done a lot of projects over the past 25 years and never 'needed' floating point math for 'power' controllers like yours.
2) create a 'long delay' function, instead of using delay_ms(xx) throughout your code.Simply load a variable with say 1,2,5 then call the 'delay100ms' function. This 'delay100ms' function that you've coded will execute a delay based on the variable you pass. IE: pass '1' and it delays 100ms, pass '5' and it delays 500ms
One more way is to use a bigger PIC! If you plan to 'play with PICS' for years, choose one that has a LOT of memory,peripherals and features. All too often,PICS are chosen on price alone THEN you find out you need 2 more pins, a HW UART, or 5 more bytes of code space. Years ago I decided the 18F46K22 was the 'best fit' for me. More 'stuff' inside than I've ever needed and while it cost a bit more , it has reduced R&D costs tremendously and clients get their products faster.
There are other 'tricks' to getting big code into a small PIC but without knowing what PIC you're using can't say if all apply.
Jay
edit:
The processor header is missing, should be first line of the program.
also, code looks 'familiar'.... cut and pasted from someone else's posting here ?
Last edited by temtronic on Fri Jan 06, 2017 8:42 am; edited 1 time in total |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Fri Jan 06, 2017 7:57 am |
|
|
Honestly the get rid of FP, is probably the best thing.
Key is to understand that the PIC itself does not not use FP. It has a value coming in from the ADC, in simple binary counts. The only reason you have FP, is because you want to output a number in 'volts', and you want to think in volts. Now your Vref is just the supply voltage, and you are using the ADC in 8 bit mode, so 'best case', you will be lucky to have even 1% accuracy. Then your maths is wrong (not /255, /256 - a search here or online will find why this is the case), so the idea of wasting many hundreds of bytes of code to get the wrong answer, is quite funny....
The savings from using '/32' only come when you are working in binary 'integer' values. You convert to float before performing the division, so 'throw away' this saving.
Probably 80% of your size comes from working in fp...
What is this meant to do?
#define AN0_AN1_AN2_AN4_VSS_VREF
It defines a macro 'AN0_AN1_AN2_AN4_VSS_VREF', which has a value of nothing....
Not a good start. |
|
|
swapnil
Joined: 15 Dec 2016 Posts: 13 Location: delhi
|
|
Posted: Fri Jan 06, 2017 9:17 am |
|
|
Ttelmah wrote: | Honestly the get rid of FP, is probably the best thing.
Key is to understand that the PIC itself does not not use FP. It has a value coming in from the ADC, in simple binary counts. The only reason you have FP, is because you want to output a number in 'volts', and you want to think in volts. Now your Vref is just the supply voltage, and you are using the ADC in 8 bit mode, so 'best case', you will be lucky to have even 1% accuracy. Then your maths is wrong (not /255, /256 - a search here or online will find why this is the case), so the idea of wasting many hundreds of bytes of code to get the wrong answer, is quite funny....
The savings from using '/32' only come when you are working in binary 'integer' values. You convert to float before performing the division, so 'throw away' this saving.
Probably 80% of your size comes from working in fp...
What is this meant to do?
#define AN0_AN1_AN2_AN4_VSS_VREF
It defines a macro 'AN0_AN1_AN2_AN4_VSS_VREF', which has a value of nothing....
Not a good start. |
Thanks a lot for suggestions.
I have deleted the line which is defining macros. I am using 16f72 as it is easily available here, and please write code for calculations. _________________ heroswap1981 |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Fri Jan 06, 2017 10:07 am |
|
|
How are your voltages wired?. What resistors in the dividers?.
The values needed for calculations all depend on this, which you don't tell us.... |
|
|
swapnil
Joined: 15 Dec 2016 Posts: 13 Location: delhi
|
hello sir, thanks for reply |
Posted: Fri Jan 06, 2017 10:58 pm |
|
|
The resistor value for solar voltage and battery voltage is 22 k and 2.7 k. On 12 volt input we will get 1.31 volt. I have to display the batt volt and sol volt on display in one decimal place, and accordingly the pwm has to vary depending on batt volt. For batt range, 1 volt to 14.5 volt, the pwm sholud be at 50 % and after 14 volts the pwm will vary in 5 steps of 10 % each, up to 14.6 volts.
sout is greater than bout then only the pwm should start. _________________ heroswap1981 |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Sat Jan 07, 2017 3:45 am |
|
|
First of all, you are only using an 8bit ADC. Now your divider gives /9.148, so with an 8bit ADC running off 5v, giving an ADC step size of 19.6mV, your step size at the 12v rail is 179mV. Not exactly high resolution....
Honestly, rethink. Use an accurate Vref. A 2.5v bandgap reference connected to the Vref+ of your chip. Suddenly your step size halves. Then with this more stable/accurate voltage reference, switch to a chip with a 10bit ADC mode, and now your step size at the 12v level becomes 22.4mV. Much more sensible/practical. You are trying to achieve accuracies that your hardware is not actually capable of, so you need to start by modifying your circuit to give some real accuracy.
Now with this done:
Code: |
#include <16F872.h> //equivalent with 10bit ADC
#device ADC=10
#fuses HS //Never use the 'Protect' fuse until you have completed
//your development. Doing so forces every cell in the chip to be erased
//every time you re-program - wastes chip lives. Otherwise the
//programmer will erase only the cells needed.
#use delay (clock=20MHz) // 20MHz
#define use_portb_lcd TRUE
#include <lcd.c>
#define CCP1_PWM PIN_C2
#define MAINS_SWITCH PIN_C3
#define CHARGING_LED PIN_C4
#define PCU_LOAD PIN_C5
#define LOAD PIN_C6
#define LOW_BATT_LED PIN_C7
#define MV_PER_COUNT (int16)(22.87/1024)
//We will do all the 'float' maths at compile time using this
//Not in the PIC itself
#define COUNTS_TO_MV(x) (x*MV_PER_COUNT)
#define TO_MV(x) ((int16)(x*1000))
int16 mean_adc(byte channel)
{
const int8 AVERAGING_CYCLE_COUNT=32;
int8 i;//mean_total = 32;
int32 total =0;
set_adc_channel(channel);
for (i=0; i<AVERAGING_CYCLE_COUNT; i++)
{
delay_us(16); //specified Tacq for your chip
total += read_adc();
}
return(total/AVERAGING_CYCLE_COUNT);
}
//Tacq (no more is needed), is required before sampling, and between
//each reading. More != better.
//********** Main Program*******
void main ( )
{
const byte SOLAR_CHANNEL=0;
const byte BATTERY_CHANNEL=1;
int16 battery_mv, solar_mv; //,bin,sin;
set_tris_c(0b00000111);
setup_adc_ports(AN0_AN1_VSS_VREF); // Only ever configure pins you
//use as analog. Enabling others increases noise pickup on the ADC
//Also setting this to use A3 as the Vref input (2.5v).
setup_adc(adc_clock_div_32);
setup_ccp1(ccp_off); // Configure CCP1 as a PWM
setup_timer_2(T2_DIV_BY_4, 249, 1); // Set PWM frequency to 8kHz
//enable_interrupts(GLOBAL);
//Since you are not using interrupts don't enable this.
//Never enable 'GLOBAL', without at least one interrupt handler present
delay_ms(500); //Many LCD's need 250mSec+ to wake
lcd_init();
printf(lcd_putc,"\f WELCOME TO ");
printf(lcd_putc,"\n LUXNTEK SOLAR ");
delay_ms(1000);
while(TRUE)
{
solar_mv=COUNTS_TO_MV(mean_adc(SOLAR_CHANNEL));
battery_mv=COUNTS_TO_MV(mean_adc(BATTERY_CHANNEL));
//Now just have the two readings in integer mV
printf(lcd_putc,"\fSOLAR =%4.1LwV",solar_mv/100);
//In 'C' the number in front of the DP is the 'total field width',
//not the digits here . To display 2 digits, DP, one digit, needs a
//field width of 4.
printf(lcd_putc,"\nBATTERY =%4.1LwV",battery_mv/100);
delay_ms(500);
if(solar_mv > battery_mv)
{ //solar > battery
if (battery_mv > TO_MV(2.0) && battery_mv <= TO_MV(14.5))
//This scares me. Do you really envisage any situation where
//battery_mv can be below 10v?. If so you have a dead battery....
{
setup_ccp1(CCP_pwm);
set_pwm1_duty(500);
output_high(CHARGING_LED);
delay_ms(200);
output_low(CHARGING_LED);
delay_ms(200);
printf(lcd_putc,"\fBATTERY CHARGING");
printf(lcd_putc,"\n BOOST MODE ");
delay_ms(400);
}
if(battery_mv > TO_MV(14.5))
{
setup_ccp1(CCP_pwm);
set_pwm1_duty(100);
output_high(CHARGING_LED);
delay_ms (600);
output_low(CHARGING_LED);
delay_ms(100);
printf(lcd_putc,"\fBATTERY CHARGING");
printf(lcd_putc,"\n TRICKLE MODE ");
delay_ms(500);
}
}
else
{ //solar<battery turn off
setup_ccp1(CCP_OFF);
output_high(CHARGING_LED);
delay_ms(300);
}
}
/////////////////////////////LOAD ////LED INDICATION ///////////////////////////////////////////////////////
//if dawn to dusk and load on batt
//actually sounds as if you mean dusk to dawn here (solar <5v)
if((solar_mv < TO_MV(5.0)) || (battery_mv > TO_MV(12.0)))
{
output_high(LOAD);
}
if(battery_mv <= TO_MV(11.0))
{
output_high(LOW_BATT_LED);
output_low(LOAD);
}
else
{
output_low(LOW_BATT_LED);
}
// Mains SNS PCU Load On
if((input(PIN_C3)==1) && (solar_mv > battery_mv) && (battery_mv > TO_MV(14.6)))
{
output_high(PCU_LOAD);
setup_ccp1(CCP_pwm);
set_pwm1_duty(500);
}
else if(battery_mv <= TO_MV(13.9))
{
output_low(PCU_LOAD);
}
}
|
Now several comments from here.
First I'd get rid of all the 'output_high' and 'output_low' calls to control LED's. Instead have:
#define LED_ON(x) output_high(x)
and similar for the LED_OFF. The code then becomes much more readable.
LED_ON(LOW_BATT_LED);
becomes 'self documenting'.
Then remember that a PWM will remain on and running unless you turn it off. In the old tests, once it has been turned on in the high power mode, it remains on till 50mV higher when it hits trickle mode. Effectively there is a 'dead spot', where the PWM is carrying on at high power, and the LED is not updating.
Then why fiddle. You change the PWM multiple times in each step. Just set it to one rate, and change this according to the voltage.
I've probably got the rates wrong etc., but this uses only just over half the chip's ROM working in integer 'mV', instead of trying to use floating point maths. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9229 Location: Greensville,Ontario
|
|
Posted: Sat Jan 07, 2017 6:52 am |
|
|
a couple of comments about the project.
battery charging a 1/2 science ,1/2 magic and 1/2 luck
1) To charge any battery you MUST follow the manufacturers methods ! Even within a series of batteries, the specs /methods may be slightly different. This _may_ be why there's the multiple PWMs in the 'charge' code sections. I found this out 30 years ago while using Gates SLA batteries. Used in USN subs they were great in my energy control systems however WHEN properly charged I could get 10 years out of them, don't do it right maybe 2 years! So you need to read and do what the mfr says. I can see how the multiple PWM rates could be used(kick start,pulse of power, desulf,?) though without knowing the batteries spec,we don't KNOW why.
2) Power to the PIC MUST be stable! Having VDD as the Vref is bad design. ANY voltage drop in VDD WILL imediately affect the ADC. While we don't know HOW you're controlling the power section of the charger, the odds are real good VDD for the PIC is dropping. If VDD has to be used as Vref, then special care has to be taken to filter VDD to minimize fluctuations.
Jay |
|
|
swapnil
Joined: 15 Dec 2016 Posts: 13 Location: delhi
|
Thanks a lot sir |
Posted: Sat Jan 07, 2017 8:55 am |
|
|
Really thankful to you for the code, i will test the hardware tomorrow as i have to arrang the chip.872.
Please help me in adding pin no. Pin no. 7 of pic RA5 as ADC input to control the load which is connected on pin 17 RC6. The ADC pin will sense 1 volt as i will get it from op-amp using shunt and after amplification up-to 1 volt. To shut down the pin in case of over current by load.
Please let me know if i have to wire vref pin RA3 to 2.5 volt or the code you provided is setting internal reference of 2.5 volt.
If i have to vary pwm as per B input after 14 volt, 40% and 14.4 volt 30% 14.5 volt 20%. So the battery i am using which is lead acid type will charge in good way.
> 14.5 volt 10 % pwm (as already doing in trickle mode) _________________ heroswap1981 |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Sat Jan 07, 2017 9:08 am |
|
|
Doing that in code, will be far too slow. You have huge delays in the code, when it won't be sensing.
If you had mentioned this before, I'd have suggested moving up to a PIC which has a comparator, and a built in DAC. Then all you do, is program the DAC to the voltage you want to switch 'at', and feed this to one input of the comparator, and the voltage you are sensing to the other input, and the comparator will switch when the voltage passes the programmed point. Then you just have an interrupt routine that immediately shuts everything down in the 'emergency'.
The alternative is a radical 'rethink', have a timer based interrupt which performs the adc reads, and handles the emergency operations, while your main code 'plods on'. However code size if going to grow. Seriously this goes back to Temtronic's comment about making things easier by using a larger PIC. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9229 Location: Greensville,Ontario
|
|
Posted: Sat Jan 07, 2017 11:05 am |
|
|
hmm.
the chip.872.
Are you using a 16F872 ?? If so Microchip does NOT advise it's use ! Obsolete.
Jay |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Sat Jan 07, 2017 12:18 pm |
|
|
He was using the F72. Only 8bit ADC....
The direct equivalent with 10bit ADC is the 872. However like you, I'd much prefer to see him using a more modern PIC.... |
|
|
swapnil
Joined: 15 Dec 2016 Posts: 13 Location: delhi
|
hi sir Ttelmah |
Posted: Sat Jan 07, 2017 11:22 pm |
|
|
sir please let me know in line #define MV_PER_COUNT (int16)(22.87/1024)
what calculation is 22.87.
i am trying to understand the calculation. _________________ heroswap1981 |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Sun Jan 08, 2017 1:49 am |
|
|
That's from your resistors.
((22K+2.7K)/2.7K)*2.5 = 22.87
So 22.87v is the absolute maximum that the ADC could accept using a 2.5v ref.
Each count of the ADC is then 1/1024th of this (with a 10bit ADC). |
|
|
swapnil
Joined: 15 Dec 2016 Posts: 13 Location: delhi
|
hi sir Ttelmah |
Posted: Tue Jan 10, 2017 8:39 am |
|
|
Good day sir,
I am not getting the display on lcd it is showing 0.0 volt.
I burned the code in pic 16F872 and checked on real hardware.
Please let me know if i can use 16f876a as it is available here in less price. _________________ heroswap1981 |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|