CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

pwm working fine i want, sensing the bout, pwm should vary
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
swapnil



Joined: 15 Dec 2016
Posts: 13
Location: delhi

View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger

pwm working fine i want, sensing the bout, pwm should vary
PostPosted: Fri Jan 06, 2017 3:15 am     Reply with quote

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: 9247
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Fri Jan 06, 2017 5:59 am     Reply with quote

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: 19553

View user's profile Send private message

PostPosted: Fri Jan 06, 2017 7:57 am     Reply with quote

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

View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger

PostPosted: Fri Jan 06, 2017 9:17 am     Reply with quote

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: 19553

View user's profile Send private message

PostPosted: Fri Jan 06, 2017 10:07 am     Reply with quote

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

View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger

hello sir, thanks for reply
PostPosted: Fri Jan 06, 2017 10:58 pm     Reply with quote

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: 19553

View user's profile Send private message

PostPosted: Sat Jan 07, 2017 3:45 am     Reply with quote

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: 9247
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Sat Jan 07, 2017 6:52 am     Reply with quote

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

View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger

Thanks a lot sir
PostPosted: Sat Jan 07, 2017 8:55 am     Reply with quote

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: 19553

View user's profile Send private message

PostPosted: Sat Jan 07, 2017 9:08 am     Reply with quote

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: 9247
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Sat Jan 07, 2017 11:05 am     Reply with quote

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: 19553

View user's profile Send private message

PostPosted: Sat Jan 07, 2017 12:18 pm     Reply with quote

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

View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger

hi sir Ttelmah
PostPosted: Sat Jan 07, 2017 11:22 pm     Reply with quote

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: 19553

View user's profile Send private message

PostPosted: Sun Jan 08, 2017 1:49 am     Reply with quote

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

View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger

hi sir Ttelmah
PostPosted: Tue Jan 10, 2017 8:39 am     Reply with quote

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
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
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