element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet & Tria Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • About Us
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Experts, Learning and Guidance
  • Technologies
  • More
Experts, Learning and Guidance
Ask an Expert Forum Trying to read Analogue voltages from thermistors on a PIC16F887
  • Blog
  • Forum
  • Documents
  • Leaderboard
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Experts, Learning and Guidance to participate - click to join for free!
Actions
  • Share
  • More
  • Cancel
Forum Thread Details
  • State Verified Answer
  • Replies 17 replies
  • Subscribers 299 subscribers
  • Views 4182 views
  • Users 0 members are here
  • microchip
  • ask_an_expert
  • mplab
  • sensor
Related
See a helpful answer?

Be sure to click 'more' and select 'suggest as answer'!

If you're the thread creator, be sure to click 'more' then 'Verify as Answer'!

Trying to read Analogue voltages from thermistors on a PIC16F887

Sumutiu
Sumutiu over 3 years ago

Hello,

I am quite new to C programming and Microchip programming in general but I did something for a personal project however I am not sure what and if I am doing something wrong.
I am trying to read the voltage from 6 thermocouples and eventually convert it into actual temperature units in order to control a relay. Now I want to mention that I want to achieve a good precision, perhaps 0.1 degreess or better if possible.

For that, I am using really precise J type thermocouples. The thermoelectric voltage in milivolts for these thermocouples is for example, 27.5mV at 500C. At 20C, it is just 1.2mV.
I worked on this project for a bit, in order to get these voltages but I am not sure what I am doing wrong. I am using the PIC16F887 because I feel like this can be done on this MCU and because it is widely available.

Project is in MPLAB and XC8 compiler.

/**************************************************************************************
 
  Thermocouple reading.
 
***************************************************************************************/
 
 
#define _XTAL_FREQ  16000000
 
#include <stdint.h>
#include "CONFIG.h"


void Initialise_Analogue_Pins(){
    ANSELbits.ANS1 = 1;
    ANSELbits.ANS2 = 1;
    ANSELbits.ANS3 = 1;
    ANSELbits.ANS4 = 1;
    ANSELbits.ANS5 = 1;
    ANSELbits.ANS6 = 1;
}

int Upper_Zone_1(){
    ADCON0bits.CHS = 0b0001;        // Select AN1.
    ADCON1bits.ADFM = 1;
    ADCON0bits.ADON = 1;
    PIE2bits.C1IE = 0;
    PIR2bits.C1IF = 0;
    __delay_ms(50);
    while(ADCON0bits.GO_DONE);
    return((ADRESH<<8) + ADRESL);
}

int Upper_Zone_2(){
    ADCON0bits.CHS = 0b0010;        // Select AN2.
    ADCON1bits.ADFM = 1;
    ADCON0bits.ADON = 1;
    PIE2bits.C1IE = 0;
    PIR2bits.C1IF = 0;
    __delay_ms(50);
    while(ADCON0bits.GO_DONE == 1);
    return((ADRESH<<8) + ADRESL);
}

int Upper_Zone_3(){
    ADCON0bits.CHS = 0b0011;        // Select AN3.
    ADCON1bits.ADFM = 1;
    ADCON0bits.ADON = 1;
    PIE2bits.C1IE = 0;
    PIR2bits.C1IF = 0;
    __delay_ms(50);
    while(ADCON0bits.GO_DONE == 1);
    return((ADRESH<<8) + ADRESL);
}

int Upper_Zone_4(){
    ADCON0bits.CHS = 0b0100;        // Select AN4.
    ADCON1bits.ADFM = 1;
    ADCON0bits.ADON = 1;
    PIE2bits.C1IE = 0;
    PIR2bits.C1IF = 0;
    __delay_ms(50);
    while(ADCON0bits.GO_DONE == 1);
    return((ADRESH<<8) + ADRESL);
}

int Upper_Zone_5(){
    ADCON0bits.CHS = 0b0101;        // Select AN5.
    ADCON1bits.ADFM = 1;
    ADCON0bits.ADON = 1;
    PIE2bits.C1IE = 0;
    PIR2bits.C1IF = 0;
    __delay_ms(50);
    while(ADCON0bits.GO_DONE == 1);
    return((ADRESH<<8) + ADRESL);
}

int Upper_Zone_6(){
    ADCON0bits.CHS = 0b0110;        // Select AN6.
    ADCON1bits.ADFM = 1;
    ADCON0bits.ADON = 1;
    PIE2bits.C1IE = 0;
    PIR2bits.C1IF = 0;
    __delay_ms(50);
    while(ADCON0bits.GO_DONE == 1);
    return((ADRESH<<8) + ADRESL);
}

// Main function
void main() {
    int Upper_Zone_1_Temp, Upper_Zone_2_Temp, Upper_Zone_3_Temp, Upper_Zone_4_Temp, Upper_Zone_5_Temp, Upper_Zone_6_Temp;
    
    Initialise_Analogue_Pins();     // Initialise analogue pins.
    //INTCON = 0xC0;                  // Enable global and peripheral interrupts

    while(1) {
        Upper_Zone_1_Temp = Upper_Zone_1();
        //Upper_Zone_2_Temp = Upper_Zone_2();
        //Upper_Zone_3_Temp = Upper_Zone_3();
        //Upper_Zone_4_Temp = Upper_Zone_4();
        //Upper_Zone_5_Temp = Upper_Zone_5();
        //Upper_Zone_6_Temp = Upper_Zone_6();
    }
}


Here is the header file:

#ifndef XC_HEADER_TEMPLATE_H
#define	XC_HEADER_TEMPLATE_H


// PIC16F887 Configuration Bit Settings

// 'C' source line config statements

// CONFIG1
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator: High-speed crystal/resonator on RA6/OSC2/CLKOUT and RA7/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled and can be enabled by SWDTEN bit of the WDTCON register)
#pragma config PWRTE = ON      // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON       // RE3/MCLR pin function select bit (RE3/MCLR pin function is MCLR)
#pragma config CP = OFF          // Code Protection bit (Program memory code protection is enabled)
#pragma config CPD = OFF         // Data Code Protection bit (Data memory code protection is enabled)
#pragma config BOREN = ON       // Brown Out Reset Selection bits (BOR enabled)
#pragma config IESO = OFF        // Internal External Switchover bit (Internal/External Switchover mode is enabled)
#pragma config FCMEN = OFF       // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is enabled)
#pragma config LVP = OFF         // Low Voltage Programming Enable bit (RB3/PGM pin has PGM function, low voltage programming enabled)

// CONFIG2
#pragma config BOR4V = BOR40V   // Brown-out Reset Selection bit (Brown-out Reset set to 4.0V)
#pragma config WRT = OFF        // Flash Program Memory Self Write Enable bits (Write protection off)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>


#endif	/* XC_HEADER_TEMPLATE_H */

  • Sign in to reply
  • Cancel

Top Replies

  • scottiebabe
    scottiebabe over 3 years ago +2 verified
    You are very close! You just need to signal the ADC to start a conversion by writing a '1' to the GO/done bit: __delay_ms(50); ADCON0bits.GO_DONE = 1; // <<<- ADD THIS :) while(ADCON0bits.GO_DONE…
  • shabaz
    shabaz over 3 years ago +1
    Hi, Regarding " At 20C, it is just 1.2mV. " How is that going to work? The ADC in that chip is 10 bits, and cannot resolve better than the 1/(2^10) of the ADC voltage (probably 3.3V or 5V). Also…
  • Sumutiu
    Sumutiu over 3 years ago in reply to scottiebabe +1
    Thanks! I worked on something so far. However I need to find a way to use just a bit less words so I can fit everything inside. Ran out of space right at the end. Anyway: #define _XTAL_FREQ 16000000 …
  • shabaz
    0 shabaz over 3 years ago

    Hi,

    Regarding "At 20C, it is just 1.2mV."

    How is that going to work? The ADC in that chip is 10 bits, and cannot resolve better than the 1/(2^10) of the ADC voltage (probably 3.3V or 5V).

    Also, how are you going to do the cold junction compensation for the thermocouple?

    If you really want to use a thermocouple then you'll need either an external ADC with ideally an amplifier, or a special microcontroller intended for such type of data acquisition. Or, ditch the thermocouples and use thermistors if they meet your needs. 

    Or, find a temperature sensor chip (can be digital, in which case no ADC is required), if it meets your needs.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • Verify Answer
    • Cancel
  • Sumutiu
    0 Sumutiu over 3 years ago in reply to shabaz

    I will use an amplifier to get the voltage up. Currently the problem is that I am trying to get the voltage at the analogue pins and I want to get a voltage as precise as possible by the MCU. Then I will use external circuitry (as just said, an op amp most likely) to process the analogue voltages from the thermocouple.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Verify Answer
    • Cancel
  • shabaz
    0 shabaz over 3 years ago in reply to Sumutiu

    What range are you expecting? You mention 0.1 degree resolution.

    You can see from thermocouple tables approximately what sort of voltage change occurs for 0.1 degree resolution, and you can see how much range you can cover with the dynamic range that you have with 10 bits. Even if you use an amplifier, you still just have 10 bits. You also mentioned 500 deg C and 20 deg C, is this the range you were hoping for? If so, what's the acceptable resolution, since you're not going to get 0.1 degree resolution with a 10-bit converter.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Verify Answer
    • Cancel
  • Sumutiu
    0 Sumutiu over 3 years ago in reply to shabaz

    The range is between 20C and 300C. 0.1C resolution is not necessary. I want to get the best resolution possible in the 10bit converter. My plan is to detect a change in temperature over a fixed set temp and control a relay to turn on and off an heating element.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Verify Answer
    • Cancel
  • Sumutiu
    0 Sumutiu over 3 years ago in reply to shabaz

    The main problem is that I can't seem to get this thing to even measure the input voltage on any of the analogue pins, for some reason. At this point, I have a feeling, it might be the MPLAB simulation rather than the code itself. I tried a different code just for this purpose: To measure imput voltage on AN1. In the Debug, ADRESH and ADRESL is 0x00. Here's the code. 

    #define _XTAL_FREQ  16000000
     
    #include <stdint.h>
    #include "CONFIG.h"
    
    
    void ADC_Initialize() {
        ADCON0 = 0b01000001;            //ADC ON
        ADCON1 = 0b10010000;            // Internal reference voltage is selected
    }
    int ADC_Read(unsigned char AnChannel) {
        ADCON0 &= 0b01000001;           //Clearing the Channel Selection Bits
        ADCON0 |= AnChannel<<2;         //Setting the required Bits
        __delay_ms(50);                 //Acquisition time to charge hold capacitor
        while(ADCON0bits.GO_DONE);
        return ((ADRESH<<8)+ADRESL);    //Returns Result
    }
    
    // Main function
    void main() {
        ADC_Initialize();
    
        int i = 0;
        int adc;
    
        while(1) {
            adc = (ADC_Read(1));
            i = adc*0.488281;
        }
    }


    ADCON0 is 0x45, ADCON1 is 0x90, ANSEL is 0xFF. Stimulus on Set Voltage for AN1 is set on 2V.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Verify Answer
    • Cancel
  • shabaz
    0 shabaz over 3 years ago in reply to Sumutiu

    This is an ancient chip (couple of decades old at least), you should be able to find example use of the ADC. It's not a chip I have ever used.

    As you say, if you're using a simulator, it might not simulate the ADC portion completely well (if at all). Probably time to try it for real, if you're planning to use this chip anyway.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Verify Answer
    • Cancel
  • scottiebabe
    +1 scottiebabe over 3 years ago

    You are very close! You just need to signal the ADC to start a conversion by writing a '1' to the GO/done bit:

        __delay_ms(50);
        ADCON0bits.GO_DONE = 1; // <<<- ADD THIS :)
        while(ADCON0bits.GO_DONE);

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • Reject Answer
    • Cancel
  • Sumutiu
    0 Sumutiu over 3 years ago in reply to scottiebabe

    Thanks! I worked on something so far. However I need to find a way to use just a bit less words so I can fit everything inside. Ran out of space right at the end. Anyway:

    #define _XTAL_FREQ  16000000
    
    #define FALSE (1==0)
    #define TRUE  (1==1)
    #define NULL 0
     
    
    #include "CONFIG.h"
    
    double UP_Z_1_V, UP_Z_2_V, UP_Z_3_V, UP_Z_4_V, UP_Z_5_V, UP_Z_6_V;
    double LO_Z_1_V, LO_Z_2_V, LO_Z_3_V, LO_Z_4_V, LO_Z_5_V, LO_Z_6_V;
    double UP_Z_1_Temp, UP_Z_2_Temp, UP_Z_3_Temp, UP_Z_4_Temp, UP_Z_5_Temp, UP_Z_6_Temp;
    double LO_Z_1_Temp, LO_Z_2_Temp, LO_Z_3_Temp, LO_Z_4_Temp, LO_Z_5_Temp, LO_Z_6_Temp;
    double UP_Z_1_ErrorSum, UP_Z_2_ErrorSum, UP_Z_3_ErrorSum, UP_Z_4_ErrorSum, UP_Z_5_ErrorSum, UP_Z_6_ErrorSum;
    double LO_Z_1_ErrorSum, LO_Z_2_ErrorSum, LO_Z_3_ErrorSum, LO_Z_4_ErrorSum, LO_Z_5_ErrorSum, LO_Z_6_ErrorSum;
    double UP_Z_1_PreError, UP_Z_2_PreError, UP_Z_3_PreError, UP_Z_4_PreError, UP_Z_5_PreError, UP_Z_6_PreError = 0;
    double LO_Z_1_PreError, LO_Z_2_PreError, LO_Z_3_PreError, LO_Z_4_PreError, LO_Z_5_PreError, LO_Z_6_PreError = 0;
    double kP, kI, kD, TempSet, Read_Temp, Error, DutyCycle;
    
    
    // Thermocouple and ADC Settings.
    double mV_Per_Degree = 10;                  // Voltage per degree (in mV).
    double Voltage_Multiplier = 489;            // Voltage Multiplier for ADC.
    
    
    // Upper Zone Temperatures.
    double UP_Z_1_TempSet = 185;
    double UP_Z_2_TempSet = 205;
    double UP_Z_3_TempSet = 200;
    double UP_Z_4_TempSet = 215;
    double UP_Z_5_TempSet = 270;
    double UP_Z_6_TempSet = 255;
    
    // Lower Zone Temperatures.
    double LO_Z_1_TempSet = 185;
    double LO_Z_2_TempSet = 205;
    double LO_Z_3_TempSet = 200;
    double LO_Z_4_TempSet = 215;
    double LO_Z_5_TempSet = 270;
    double LO_Z_6_TempSet = 255;
    
    
    // Analogue input voltage offset.
    double UP_Z_1_Offset = 95.8770;       // 2400.0mV
    double UP_Z_2_Offset = 100.7932;      // 2900.0mV
    double UP_Z_3_Offset = 91.8070;       // 3000.0mV
    double UP_Z_4_Offset = 91.8070;       // 3000.0mV
    double UP_Z_5_Offset = 89.0953;       // 3600.0mV
    double UP_Z_6_Offset = 89.893;        // 3400.0mV
    
    double LO_Z_1_Offset = 95.8770;       // 2400.0mV
    double LO_Z_2_Offset = 100.7932;      // 2900.0mV
    double LO_Z_3_Offset = 91.8070;       // 3000.0mV
    double LO_Z_4_Offset = 91.8070;       // 3000.0mV
    double LO_Z_5_Offset = 89.0953;       // 3600.0mV
    double LO_Z_6_Offset = 89.893;        // 3400.0mV
    
    
    // ADC Configuration.
    void ADC_Initialize() {
        ADCON0 = 0b01000001;            // ADC ON
        ADCON1 = 0b10010000;            // Internal reference voltage is selected
    }
    
    int ADC_Read(unsigned char AnChannel) {
        ADCON0 &= 0b01000001;           // Clearing the Channel Selection Bits
        ADCON0 |= AnChannel<<2;         // Setting the required Bits
        __delay_ms(50);                 // Acquisition time to charge hold capacitor
        ADCON0bits.GO_DONE = 1;
        while(ADCON0bits.GO_DONE);
        return ((ADRESH<<8)+ADRESL);    // Returns Result
    }
    
    
    // Read Thermocouple temperature.
    double Temp_Read_AN(unsigned char ANChannelTemp) {
        switch(ANChannelTemp) {
            case 1:
                return ((ADC_Read(ANChannelTemp)*Voltage_Multiplier/100)*(UP_Z_1_Offset/10000+0.99))/mV_Per_Degree;
                break;
            case 2:
                return ((ADC_Read(ANChannelTemp)*Voltage_Multiplier/100)*(UP_Z_2_Offset/10000+0.99))/mV_Per_Degree;
                break;
            case 3:
                return ((ADC_Read(ANChannelTemp)*Voltage_Multiplier/100)*(UP_Z_3_Offset/10000+0.99))/mV_Per_Degree;
                break;
            case 4:
                return ((ADC_Read(ANChannelTemp)*Voltage_Multiplier/100)*(UP_Z_4_Offset/10000+0.99))/mV_Per_Degree;
                break;
            case 5:
                return ((ADC_Read(ANChannelTemp)*Voltage_Multiplier/100)*(UP_Z_5_Offset/10000+0.99))/mV_Per_Degree;
                break;
            case 6:
                return ((ADC_Read(ANChannelTemp)*Voltage_Multiplier/100)*(UP_Z_6_Offset/10000+0.99))/mV_Per_Degree;
                break;
            case 7:
                return ((ADC_Read(ANChannelTemp)*Voltage_Multiplier/100)*(LO_Z_1_Offset/10000+0.99))/mV_Per_Degree;
                break;
            case 8:
                return ((ADC_Read(ANChannelTemp)*Voltage_Multiplier/100)*(LO_Z_2_Offset/10000+0.99))/mV_Per_Degree;
                break;
            case 9:
                return ((ADC_Read(ANChannelTemp)*Voltage_Multiplier/100)*(LO_Z_3_Offset/10000+0.99))/mV_Per_Degree;
                break;
            case 10:
                return ((ADC_Read(ANChannelTemp)*Voltage_Multiplier/100)*(LO_Z_4_Offset/10000+0.99))/mV_Per_Degree;
                break;
            case 11:
                return ((ADC_Read(ANChannelTemp)*Voltage_Multiplier/100)*(LO_Z_5_Offset/10000+0.99))/mV_Per_Degree;
                break;
            case 12:
                return ((ADC_Read(ANChannelTemp)*Voltage_Multiplier/100)*(LO_Z_6_Offset/10000+0.99))/mV_Per_Degree;
                break;
        }
        return NULL;
    }
    
    
    // PID System.
    double PID_System_Zone(unsigned char HeatingZone) {
        switch(HeatingZone) {
            case 1:
                kP = 0.5;        // Proportional Gain.
                kI = 0.9;        // Integral Gain.
                kD = 0.35;       // Derivative Gain.
                
                TempSet = UP_Z_1_TempSet;
                Read_Temp = Temp_Read_AN(HeatingZone);
                Error = TempSet - Read_Temp;
                UP_Z_1_ErrorSum += Error;
                
                if(Error < 0) {
                    // Turn Off Relay
                    DutyCycle = (kP*Error) + (kI*UP_Z_1_ErrorSum) + (kD*(Error - UP_Z_1_PreError));
                    UP_Z_1_PreError = Error;
                } else {
                    // Turn On Relay
                    DutyCycle = (kP*Error) + (kI*UP_Z_1_ErrorSum) + (kD*(Error - UP_Z_1_PreError));
                    UP_Z_1_PreError = Error;
                }
                
                if(DutyCycle < -100) DutyCycle = 100;
                if(DutyCycle > 100) DutyCycle = 100;
                
                if(Error < 10 && Error > -10) DutyCycle = 80;
                break;
            case 2:
                kP = 0.5;        // Proportional Gain.
                kI = 0.9;        // Integral Gain.
                kD = 0.35;       // Derivative Gain.
                
                TempSet = UP_Z_2_TempSet;
                Read_Temp = Temp_Read_AN(HeatingZone);
                Error = TempSet - Read_Temp;
                UP_Z_2_ErrorSum += Error;
                
                if(Error < 0) {
                    // Turn Off Relay
                    DutyCycle = (kP*Error) + (kI*UP_Z_2_ErrorSum) + (kD*(Error - UP_Z_2_PreError));
                    UP_Z_2_PreError = Error;
                } else {
                    // Turn On Relay
                    DutyCycle = (kP*Error) + (kI*UP_Z_2_ErrorSum) + (kD*(Error - UP_Z_2_PreError));
                    UP_Z_2_PreError = Error;
                }
                
                if(DutyCycle < -100) DutyCycle = 100;
                if(DutyCycle > 100) DutyCycle = 100;
                
                if(Error < 10 && Error > -10) DutyCycle = 80;
                break;
            case 3:
                kP = 0.5;        // Proportional Gain.
                kI = 0.9;        // Integral Gain.
                kD = 0.35;       // Derivative Gain.
                
                TempSet = UP_Z_3_TempSet;
                Read_Temp = Temp_Read_AN(HeatingZone);
                Error = TempSet - Read_Temp;
                UP_Z_3_ErrorSum += Error;
                
                if(Error < 0) {
                    // Turn Off Relay
                    DutyCycle = (kP*Error) + (kI*UP_Z_3_ErrorSum) + (kD*(Error - UP_Z_3_PreError));
                    UP_Z_3_PreError = Error;
                } else {
                    // Turn On Relay
                    DutyCycle = (kP*Error) + (kI*UP_Z_3_ErrorSum) + (kD*(Error - UP_Z_3_PreError));
                    UP_Z_3_PreError = Error;
                }
                
                if(DutyCycle < -100) DutyCycle = 100;
                if(DutyCycle > 100) DutyCycle = 100;
                
                if(Error < 10 && Error > -10) DutyCycle = 80;
                break;
            case 4:
                kP = 0.5;        // Proportional Gain.
                kI = 0.9;        // Integral Gain.
                kD = 0.35;       // Derivative Gain.
                
                TempSet = UP_Z_4_TempSet;
                Read_Temp = Temp_Read_AN(HeatingZone);
                Error = TempSet - Read_Temp;
                UP_Z_4_ErrorSum += Error;
                
                if(Error < 0) {
                    // Turn Off Relay
                    DutyCycle = (kP*Error) + (kI*UP_Z_4_ErrorSum) + (kD*(Error - UP_Z_4_PreError));
                    UP_Z_4_PreError = Error;
                } else {
                    // Turn On Relay
                    DutyCycle = (kP*Error) + (kI*UP_Z_4_ErrorSum) + (kD*(Error - UP_Z_4_PreError));
                    UP_Z_4_PreError = Error;
                }
                
                if(DutyCycle < -100) DutyCycle = 100;
                if(DutyCycle > 100) DutyCycle = 100;
                
                if(Error < 10 && Error > -10) DutyCycle = 80;
                break;
            case 5:
                kP = 0.5;        // Proportional Gain.
                kI = 0.9;        // Integral Gain.
                kD = 0.35;       // Derivative Gain.
                
                TempSet = UP_Z_5_TempSet;
                Read_Temp = Temp_Read_AN(HeatingZone);
                Error = TempSet - Read_Temp;
                UP_Z_5_ErrorSum += Error;
                
                if(Error < 0) {
                    // Turn Off Relay
                    DutyCycle = (kP*Error) + (kI*UP_Z_5_ErrorSum) + (kD*(Error - UP_Z_5_PreError));
                    UP_Z_5_PreError = Error;
                } else {
                    // Turn On Relay
                    DutyCycle = (kP*Error) + (kI*UP_Z_5_ErrorSum) + (kD*(Error - UP_Z_5_PreError));
                    UP_Z_5_PreError = Error;
                }
                
                if(DutyCycle < -100) DutyCycle = 100;
                if(DutyCycle > 100) DutyCycle = 100;
                
                if(Error < 10 && Error > -10) DutyCycle = 80;
                break;
            case 6:
                kP = 0.5;        // Proportional Gain.
                kI = 0.9;        // Integral Gain.
                kD = 0.35;       // Derivative Gain.
                
                TempSet = UP_Z_6_TempSet;
                Read_Temp = Temp_Read_AN(HeatingZone);
                Error = TempSet - Read_Temp;
                UP_Z_6_ErrorSum += Error;
                
                if(Error < 0) {
                    // Turn Off Relay
                    DutyCycle = (kP*Error) + (kI*UP_Z_6_ErrorSum) + (kD*(Error - UP_Z_6_PreError));
                    UP_Z_6_PreError = Error;
                } else {
                    // Turn On Relay
                    DutyCycle = (kP*Error) + (kI*UP_Z_6_ErrorSum) + (kD*(Error - UP_Z_6_PreError));
                    UP_Z_6_PreError = Error;
                }
                
                if(DutyCycle < -100) DutyCycle = 100;
                if(DutyCycle > 100) DutyCycle = 100;
                
                if(Error < 10 && Error > -10) DutyCycle = 80;
                break;
            case 7:
                kP = 0.5;        // Proportional Gain.
                kI = 0.9;        // Integral Gain.
                kD = 0.35;       // Derivative Gain.
                
                TempSet = LO_Z_1_TempSet;
                Read_Temp = Temp_Read_AN(HeatingZone);
                Error = TempSet - Read_Temp;
                LO_Z_1_ErrorSum += Error;
                
                if(Error < 0) {
                    // Turn Off Relay
                    DutyCycle = (kP*Error) + (kI*LO_Z_1_ErrorSum) + (kD*(Error - LO_Z_1_PreError));
                    LO_Z_1_PreError = Error;
                } else {
                    // Turn On Relay
                    DutyCycle = (kP*Error) + (kI*LO_Z_1_ErrorSum) + (kD*(Error - LO_Z_1_PreError));
                    LO_Z_1_PreError = Error;
                }
                
                if(DutyCycle < -100) DutyCycle = 100;
                if(DutyCycle > 100) DutyCycle = 100;
                
                if(Error < 10 && Error > -10) DutyCycle = 80;
                break;
            case 8:
                kP = 0.5;        // Proportional Gain.
                kI = 0.9;        // Integral Gain.
                kD = 0.35;       // Derivative Gain.
                
                TempSet = LO_Z_2_TempSet;
                Read_Temp = Temp_Read_AN(HeatingZone);
                Error = TempSet - Read_Temp;
                LO_Z_2_ErrorSum += Error;
                
                if(Error < 0) {
                    // Turn Off Relay
                    DutyCycle = (kP*Error) + (kI*LO_Z_2_ErrorSum) + (kD*(Error - LO_Z_2_PreError));
                    LO_Z_2_PreError = Error;
                } else {
                    // Turn On Relay
                    DutyCycle = (kP*Error) + (kI*LO_Z_2_ErrorSum) + (kD*(Error - LO_Z_2_PreError));
                    LO_Z_2_PreError = Error;
                }
                
                if(DutyCycle < -100) DutyCycle = 100;
                if(DutyCycle > 100) DutyCycle = 100;
                
                if(Error < 10 && Error > -10) DutyCycle = 80;
                break;
            case 9:
                kP = 0.5;        // Proportional Gain.
                kI = 0.9;        // Integral Gain.
                kD = 0.35;       // Derivative Gain.
                
                TempSet = LO_Z_3_TempSet;
                Read_Temp = Temp_Read_AN(HeatingZone);
                Error = TempSet - Read_Temp;
                LO_Z_3_ErrorSum += Error;
                
                if(Error < 0) {
                    // Turn Off Relay
                    DutyCycle = (kP*Error) + (kI*LO_Z_3_ErrorSum) + (kD*(Error - LO_Z_3_PreError));
                    LO_Z_3_PreError = Error;
                } else {
                    // Turn On Relay
                    DutyCycle = (kP*Error) + (kI*LO_Z_3_ErrorSum) + (kD*(Error - LO_Z_3_PreError));
                    LO_Z_3_PreError = Error;
                }
                
                if(DutyCycle < -100) DutyCycle = 100;
                if(DutyCycle > 100) DutyCycle = 100;
                
                if(Error < 10 && Error > -10) DutyCycle = 80;
                break;
            case 10:
                kP = 0.5;        // Proportional Gain.
                kI = 0.9;        // Integral Gain.
                kD = 0.35;       // Derivative Gain.
                
                TempSet = LO_Z_4_TempSet;
                Read_Temp = Temp_Read_AN(HeatingZone);
                Error = TempSet - Read_Temp;
                LO_Z_4_ErrorSum += Error;
                
                if(Error < 0) {
                    // Turn Off Relay
                    DutyCycle = (kP*Error) + (kI*LO_Z_4_ErrorSum) + (kD*(Error - LO_Z_4_PreError));
                    LO_Z_4_PreError = Error;
                } else {
                    // Turn On Relay
                    DutyCycle = (kP*Error) + (kI*LO_Z_4_ErrorSum) + (kD*(Error - LO_Z_4_PreError));
                    LO_Z_4_PreError = Error;
                }
                
                if(DutyCycle < -100) DutyCycle = 100;
                if(DutyCycle > 100) DutyCycle = 100;
                
                if(Error < 10 && Error > -10) DutyCycle = 80;
                break;
            case 11:
                kP = 0.5;        // Proportional Gain.
                kI = 0.9;        // Integral Gain.
                kD = 0.35;       // Derivative Gain.
                
                TempSet = LO_Z_5_TempSet;
                Read_Temp = Temp_Read_AN(HeatingZone);
                Error = TempSet - Read_Temp;
                LO_Z_5_ErrorSum += Error;
                
                if(Error < 0) {
                    // Turn Off Relay
                    DutyCycle = (kP*Error) + (kI*LO_Z_5_ErrorSum) + (kD*(Error - LO_Z_5_PreError));
                    LO_Z_5_PreError = Error;
                } else {
                    // Turn On Relay
                    DutyCycle = (kP*Error) + (kI*LO_Z_5_ErrorSum) + (kD*(Error - LO_Z_5_PreError));
                    LO_Z_5_PreError = Error;
                }
                
                if(DutyCycle < -100) DutyCycle = 100;
                if(DutyCycle > 100) DutyCycle = 100;
                
                if(Error < 10 && Error > -10) DutyCycle = 80;
                break;
            case 12:
                kP = 0.5;        // Proportional Gain.
                kI = 0.9;        // Integral Gain.
                kD = 0.35;       // Derivative Gain.
                
                TempSet = LO_Z_6_TempSet;
                Read_Temp = Temp_Read_AN(HeatingZone);
                Error = TempSet - Read_Temp;
                LO_Z_6_ErrorSum += Error;
                
                if(Error < 0) {
                    // Turn Off Relay
                    DutyCycle = (kP*Error) + (kI*LO_Z_6_ErrorSum) + (kD*(Error - LO_Z_6_PreError));
                    LO_Z_6_PreError = Error;
                } else {
                    // Turn On Relay
                    DutyCycle = (kP*Error) + (kI*LO_Z_6_ErrorSum) + (kD*(Error - LO_Z_6_PreError));
                    LO_Z_6_PreError = Error;
                }
                
                if(DutyCycle < -100) DutyCycle = 100;
                if(DutyCycle > 100) DutyCycle = 100;
                
                if(Error < 10 && Error > -10) DutyCycle = 80;
                break;
        }
        return NULL;
    }
    
    
    
    
    // Main function
    void main() {
        ADC_Initialize();
        while(1) {
            
            int8_t ZoneCounter;
            for(ZoneCounter = 1; ZoneCounter <= 12; ZoneCounter++) {
                switch(ZoneCounter) {
                    case 1:
                        while(ADCON0bits.GO_DONE);
                        UP_Z_1_Temp = Temp_Read_AN(1);
                        break;
                    case 2:
                        while(ADCON0bits.GO_DONE);
                        UP_Z_2_Temp = Temp_Read_AN(2);
                        break;
                    case 3:
                        while(ADCON0bits.GO_DONE);
                        UP_Z_3_Temp = Temp_Read_AN(3);
                        break;
                    case 4:
                        while(ADCON0bits.GO_DONE);
                        UP_Z_4_Temp = Temp_Read_AN(4);
                        break;
                    case 5:
                        while(ADCON0bits.GO_DONE);
                        UP_Z_5_Temp = Temp_Read_AN(5);
                        break;
                    case 6:
                        while(ADCON0bits.GO_DONE);
                        UP_Z_6_Temp = Temp_Read_AN(6);
                        break;
                    case 7:
                        while(ADCON0bits.GO_DONE);
                        LO_Z_1_Temp = Temp_Read_AN(7);
                        break;
                    case 8:
                        while(ADCON0bits.GO_DONE);
                        LO_Z_2_Temp = Temp_Read_AN(8);
                        break;
                    case 9:
                        while(ADCON0bits.GO_DONE);
                        LO_Z_3_Temp = Temp_Read_AN(9);
                        break;
                    case 10:
                        while(ADCON0bits.GO_DONE);
                        LO_Z_4_Temp = Temp_Read_AN(10);
                        break;
                    case 11:
                        while(ADCON0bits.GO_DONE);
                        LO_Z_5_Temp = Temp_Read_AN(11);
                        break;
                    case 12:
                        while(ADCON0bits.GO_DONE);
                        LO_Z_6_Temp = Temp_Read_AN(12);
                        break;
                }
            }
        }
    }


    The PID system might need tuning a bit. Also, I will run the thermocouples throught two OP AMPs (18.308 Gain then 10 Gain) to get 10mV/C.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • Verify Answer
    • Cancel
  • scottiebabe
    0 scottiebabe over 3 years ago in reply to Sumutiu

    You are very welcome!

    You are going to have a hard time fitting that many doubles into RAM. There is only 368 bytes available on your PIC16:

    image

    When you use the const qualifier the complier stores the variable in program memory versus ram so that can save data memory.

    image

    I did a find/replace of double to float and added const where I thought it was reasonable:

    image

    You can also experiment with placing set point constants in eeprom with:

    image

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • Verify Answer
    • Cancel
  • Sumutiu
    0 Sumutiu over 3 years ago in reply to scottiebabe

    Thanks for the tip. I followed your steps exactly by replacing each double with float but then I reached a point where I declared almost all the functions as const and I couldn't get the Program space low enough to even compile, not to even mention reaching 65% like you did. I am not sure what I am doing wrong, but could you point out to an example to where you used const?

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Verify Answer
    • Cancel
>
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube