/////////////////////////////////////////////////////////////////////// // Peak Power Tracker copyright March 7, 2003 by Tim Nolan // // This code was written by me, Tim Nolan, to run the Peak Power // hardware that I designed. It was compiled with the // CCS PCM C Compiler, Version 2.703, http://www.ccsinfo.com/picc.shtml, // running under Microchip's MPLAB development system, www.microchip.com. // I used the ICD development system from Microchip for debugging and and programming, // http://www.microchip.com/1010/pline/tools/picmicro/icds/mplabicd/index.htm. // This code is more fully explainded in my Software Description document at my // my website www.timnolan.com. For more information on the chip PIC16F876 see // the data sheet 30292c.pdf at www.microchip.com. // I have decided to release the software and hardware for my Peak Power // Tracking system into the public domain with no restrictions. I hope you use // it and improve it. I only ask that you give me credit with my name, Tim Nolan // and my website www.timnolan.com somewhere in your documentation. I also // would like hear about how you're using it, send me an email! // // // v1.00 3/7/03 Software cleaned up and commented for release. // /////////////////////////////////////////////////////////////////////// #include <16f876.h> #DEVICE *=16 ICD=TRUE #use Delay(Clock=20000000) #use RS232(Baud=9600,Xmit=PIN_C6,Rcv=PIN_C7,brgh1ok) //------------------------------------------------------------------------------- // This function read the value from the PIC A/D channel. It reads the value // 32 times and averages it. The function is called with an A/D channel number and // returns a float value that is average of the A/D channel value. //-------------------------------------------------------------------------------- float average_ad(byte chan) { int i,avg_num = 32; float avg = 0, temp_avg = 0; set_adc_channel(chan); delay_us(100); for (i=1; i<=avg_num; ++i) { avg += read_adc(); delay_us(100); } return(avg/avg_num); } //-------------------------------------------------------------------------------- // This function is used to set the duty cycle of the PWM output. It uses the built // in C compiler function set_pwm1_duty to set the duty cycle. The function is called // with a pwm duty cycle percentage (0-100) and also with the maximum pwm value which // is function of the pwm frequency, see PIC data sheet for more information. //------------------------------------------------------------------------------- void pwm1_duty100(long pwm_percent, long pwm_maximum) { set_pwm1_duty((pwm_maximum/100)*pwm_percent); // set PWM #1 duty cycle } //-------------------------------------------------------------------------------- // Main code section. // For more information on the chip PIC16F876 see the data sheet 30292c.pdf. //-------------------------------------------------------------------------------- main() { CONST float CHARGED = 14.4; // intialize constants and varibles CONST long NDELTA = -1; CONST byte AMP_CHAN = 0, BAMP_CHAN = 3, BVOLT_CHAN = 2, VOLT_CHAN = 1; float current, voltage, watts = 0.0, old_watts = 0.0; float batamps, batvolts, batwatts; float temp_watts, watts100, amp100, volt100; long delta = 1, pwm_max, pwm100; long boost = 0, boost_counter; byte led_count = 0; // initialize hardware output_low(PIN_C3); // turn off FETs by clearing EN pin on FET driver setup_port_a(ALL_ANALOG); // set up A/D channels setup_adc(adc_clock_div_32); // start A/D clocks setup_timer_2(T2_DIV_BY_1, 49, 1); // pwm frequency 100kHz setup_ccp1(CCP_PWM); // Configure CCP1 as a PWM pwm_max = 200; // maximum value for PWM based on frequency pwm100 = 85; // set pwm to 85% to start pwm1_duty100(pwm100, pwm_max); // set pwm duty cycle to pwm100 value output_high(PIN_C3); // set enable pin on FET driver output_high(PIN_C0); // turn on green led delay_ms(1000); // delay to get started while(TRUE) { // loop forever // check for max battery voltage, very simple battery // charge control algorythm if (batvolts > CHARGED) { // if batvolts is > charged then battery is charged so delta = NDELTA; // start rolling back current until batvolts < charged output_high(PIN_C5); // turn on red led if battery at maximum voltage } // start hill climbing algorythm for peak power tracking else if (old_watts > watts) { // else if battery not charged yet change the value of delta = -delta; // delta to make pwm increase of decrease to maximize watts output_low(PIN_C5); // turn off red led if battery not at maximum voltage } old_watts = watts; // load old_watts with current watts value for next time pwm100 += delta; // add delta to change PWM duty cycle for PPT algorythm if (pwm100 > 100) pwm100 = 100; // check limits of PWM duty cyle and set range to 0-100 else if (pwm100 < 0) pwm100 = 0; pwm1_duty100(pwm100, pwm_max); // call function to set PWM duty cyle in PIC hardware current = average_ad(AMP_CHAN)/40.885; // read current and voltage from solar panel and going to voltage = average_ad(VOLT_CHAN)/18.2887; // battery, use average_ad function return average value batamps = average_ad(BAMP_CHAN)/41.3428; // as a float, divide by constant to scale into actual batvolts = average_ad(BVOLT_CHAN)/20.4831;// volts and amps. These constants were measured in the // actual prototype hardware. // current = average_ad(AMP_CHAN)/40.96; // These constants are calculated by the gain of the // voltage = average_ad(VOLT_CHAN)/18.618; // hardware not acutally measured. They are commented out // batamps = average_ad(BAMP_CHAN)/40.96; // if not used. If volt and amp constants not measured // batvolts = average_ad(BVOLT_CHAN)/18.618;// then use this code watts = current * voltage; // do solar watts calculations using input current and volts batwatts = batamps * batvolts; // do battery watts calculation with batamps and batvolts if ((++boost_counter % 20) == 0) { // This section calculates the boost or gain of the PPT pwm1_duty100(100, pwm_max); // set PWM duty cycle to 100% to give direct delay_ms(20); // connection between solar panel and battery amp100 = average_ad(AMP_CHAN)/40.885; // read solar amps and volts using measured constants volt100 = average_ad(VOLT_CHAN)/18.2887; if (amp100 < 0.05) { // while PWM is set to 100% duty check to see if no more sun output_high(PIN_C4); // if current is < .05 then turn off PPT turn on yellow led output_low(PIN_C3); // turn off FETs by clearing EN pin on FET driver delay_ms(1000); // delay to shut down } // this will shut system down if no more solar current watts100 = amp100 * volt100; // if not shutting system down calculate watts at 100% PWM temp_watts = watts / watts100; // divide peak power watts by 100% watts, this is gain or boost boost = (long)((temp_watts * 100) - 100); // turn boost value into a percentage for output } //send all values out the serial port printf(" %lu %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f %li %6.2f\r\n", pwm100, current, voltage, watts, batamps, batvolts, batwatts, boost, watts100); output_bit(PIN_C0, (++led_count & 0x01)); //blink green LED to show software running delay_ms(500); //delay before next loop } //end while forever } //end main