I have programmed the PIC18F4520 with MPLAB, MCC18 and Pickit2. My code is pasted below:

#include <p18f4520.h>
#include <math.h>
#include <adc.h>
#include <delays.h>

#pragma config OSC = INTIO67                //HSPLL
#pragma config WDT = OFF
#pragma config LVP = OFF
#pragma config DEBUG = OFF

#define TRUE    1
#define FALSE   0
#define ON      1
#define OFF     0

//Defining internal clock of 32Mhz
#define FOSC 32000000L
#define TCY 8                       //Which is 4/32Mhz = 1/8Mhz.

//#define Reset() {__asm__ volatile ("reset");}

//***** Constants *****
#define Q       1.6e-19
#define K       1.38e-23
#define Rs      0.05            //Series Resistance
#define Rsh     200             //Shunt resistance
#define N       1.3
#define NS      40

double temperature = 274.0;
double short_circuit_current = 0;
double open_circuit_voltage = 0;
double IL_1 = 0.0;
double Q_1 = 0.0;
double M_1 = 0.0;
double IO_1 = 0.0;

typedef far struct 
    double Pmax;
    double Imax;
    double Vmax;
    double Gmax;
    double Rmax;
} Result;

far Result result_parallel;
far Result result_series;
double Rmax = 0.0;

#define ARRAY_SIZE 576
#pragma udata DATA
//double V_1[ARRAY_SIZE/2];
double I_1[ARRAY_SIZE];
#pragma udata

/****** Prototype methods **********/
float abs(auto float x);
void delay_usec(unsigned int);
void delay_msec(unsigned int);
double read_adc(unsigned char);
void switch_port(unsigned char, const unsigned char);

Result compute(const double,const double);
int max(const double, const double*, int length);

float abs(auto float x)
    return (x < 0) ? (x*-1.0f) : x;

void delay_usec(unsigned int usec)
    int delay = usec * TCY / 10;

void delay_msec(unsigned int msec)
    int delay = msec * 1000 * TCY / 100;
    if (delay > 255) 
        delay = msec * 1000 * TCY / 10000;

double read_adc(unsigned char channel)
    while (BusyADC());
    return ((ReadADC() + 40)/1024.0 * 5.0);
    //return ReadADC();

void switch_port(unsigned char port, const unsigned char state)
    port = state;

Result compute(const double Voc, const double Isc) 
    int i = 0;
    int maxPoint = 0;
    int arrayLength = ARRAY_SIZE;
    double V_1 = 0.0;
    Result result;

    //Calculation time
    IL_1 = Isc;
    Q_1 = (Q*Voc)/(N*K*temperature);
    M_1 = Voc / Rsh;
    IO_1 = ((double)abs(Isc - M_1))/(((double)exp(Q_1)) - 1.0);

    for (i = 0; i < arrayLength; i++)
        V_1 = ((double)i)*(Voc/(double)arrayLength);
        I_1[i] = IL_1 - IO_1 * (exp(Q * V_1/ (NS * N * K * temperature)) - 1.0) - V_1/Rsh;

    maxPoint = max(Voc, I_1, arrayLength);
    result.Imax = I_1[maxPoint];
    result.Vmax = ((double)maxPoint)*(Voc/(double)arrayLength); //V_1[maxPoint];
    result.Pmax = result.Vmax*result.Imax;
    result.Gmax = result.Imax/result.Vmax;
    result.Rmax = result.Vmax/result.Imax;

    return result;

int max(const double Voc, const double* I_1, int length)
    double V_1 = 0.0;
    double V_1index = 0.0;
    double P_prev = 0.0;
    double P = 0.0;
    int index = 0;
    int i = 0;

    for (i = 0; i < length; i++)
        V_1 = ((double)i)*(Voc/(double)length);
        V_1index = ((double)index)*(Voc/(double)length); 
        P = V_1 * I_1[i];
        if (i > 0)
            if (P < P_prev && P_prev > (V_1index * I_1[index])) 
                index = i - 1;

        P_prev = P;

    return index;

void main(void)
    //Set clock frequency
    OSCCON |= 0x70;                         //8 Mhz clock, primary clock
    OSCTUNEbits.PLLEN = 1;                  //4x Multiplier, thus we have 32 Mhz clock
    while (!OSCCONbits.IOFS);               //Wait until INTOSC is stable.

    //Enable TRISA as analog input (For ADC)
    TRISA = 0x2F;

    //PIN Outputs
    TRISB = 0;                              //Make PORTD as output
    //PORTBbits.RB0 = 0;

    //Set ADC
    OpenADC(ADC_FOSC_32 &                   //Fosc/32
         ADC_RIGHT_JUST &
         ADC_4_TAD,                         //4xTAD
         ADC_CH0 &
         ADC_REF_VDD_VSS &
         ADC_INT_OFF, ADC_5ANA);            //ADC Initialized

    while (TRUE)
        short_circuit_current = (read_adc(ADC_CH0) - 2.5) * 6 / 0.625;
        //Delay 1 millisecond
        open_circuit_voltage = read_adc(ADC_CH1) * 14/3;

        result_parallel = compute(open_circuit_voltage * 5.0, short_circuit_current * 2.0);
        result_series = compute(open_circuit_voltage * 10.0, short_circuit_current);
        Rmax = (result_parallel.Pmax > result_series.Pmax) ? result_parallel.Rmax : result_series.Rmax; 

        if (Rmax >= 0 && Rmax <= 21.0)
            switch_port(PORTB, OFF);
            switch_port(PORTBbits.RB3, ON);
            switch_port(PORTBbits.RB5, ON);
        else if (Rmax > 21.0 && Rmax <= 42.0)
            switch_port(PORTB, OFF);
            switch_port(PORTBbits.RB3, ON);
            switch_port(PORTB, OFF);
            switch_port(PORTBbits.RB4, ON);

    CloseADC();                     //Closing ADC

My problem is, when I "watch" the Rmax variable (on this line Rmax = (result_parallel.Pmax > result_series.Pmax) ? result_parallel.Rmax : result_series.Rmax;), it says NaN. I tried to set the struct to near but I get compilation problem. I set it to far but I get the same issue of NaN.


  • What am I missing?
  • What is near, far when I try to set it to struct?


Oh yes, I added a DATABANK and SECTION DATA for large array, on the PIC18F4520 linker.

Update: I see that the following line I_1[i] = IL_1 - IO_1 * (exp(Q * V_1/ (NS * N * K * temperature)) - 1.0) - V_1/Rsh;, is a "Restricted Memory" (when using MPLAB SIM). When the result calculation for Vmax, I get a NaN since the division is by zero. What is causing this?


Even when using a compiler, you need to read the PIC datasheet. If I remember right, the C18 compiler considers the access bank NEAR and banked memory FAR. Whether a variable is in near or far memory should have no effect on the logic of the program, but accessing near memory can take less cycles and instruction words.

Your statement "I did allocation additional memory for udata DATA on the PIC 18F4520 linker" makes no sense. First, the english is so bad it's unclear what you really mean. Second, messing with the linker file is only for when you know exactly what you are doing. You can't magically make more memory by lying to the linker. That will only make it so the linker can't tell that you've overflowed a memory region. The PIC still has the memory regions it has no matter what you tell the linker. The default linker file should contain memory regions that cover all of useful memory.

Another pitfal when messing with RAM layout in the linker file is that if you let a memory region cross a bank boundary, some multi-byte structure or variable can then get placed accross that bank boundary. If the compiler assumes the structure is wholly on one bank, it could produce incorrect code for accessing parts of it. There are ways around this if you need large arrays, but that's a advanced concept not worth getting into unless that is actually your problem.

Also, do you really need double precision floating point? Unless you have a very unusual application, the answer is no.

| improve this answer | |
  • \$\begingroup\$ Thanks Olin Lathrop, 1) For that statement, I rewrote it, hopefully better. :). 2) You're right, I don't really need double precision floating point, so I resorted to single precision floating point, a.k.a. float. And I left my struct to far. Let's see if this made a difference at all. \$\endgroup\$ – Buhake Sindi May 20 '12 at 19:32
  • \$\begingroup\$ sorry to bother, I have updated my question. If you have time, please see my edit... thanks (looking forward for your response). :-) \$\endgroup\$ – Buhake Sindi May 20 '12 at 21:46

I'm a bit confused as why you re-define the standard math functions "abs" and "max".

A maximum function that acts just like the C standard library math function

When I want some kind of maximum function that acts just like the C standard library math functions, I use the C standard library math functions, writing something like this:

#include math.h

// ... then much later ...

z = fmax( x, y );
a = fabs( z ) ;

// The above which seems clearer to me than, say,
// z = ( x > y ) ? x : y;
// a = ( a > 0 ) ? a : -a;

A maximum function that does not act like the C standard library math function

When I need to find some kind of maximum function that isn't covered by the C standard math library, I write a function for doing that. However, I feel that giving this new function exactly the same name as some other function already in the "math.h" C standard math library, such as "max", is asking for trouble, so I make up a new more specific name.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.