PIC18F4520 state when ADC reaches certain value

Thread Starter

LewisMF

Joined Nov 15, 2014
119
Good morning everyone,

I am trying to program a PIC18F4520 so that when one of the ADC readings (CH0 or CH1) reach a certain threshold the PIC will go in to an 'alarm state' and will continue in that state until the device is reset by the MCLR pin.

At the moment, when the ADC reaches the threshold it will go in to the 'alarm state' but when the ADC value changes it will go back to normal condition and what I want is for it to "Latch" and stay in the 'alarm state' until reset, as explained above.

How could I implement this?

Thanks in advanced for any given help.

/***************************************************************************************************/

PS: I am using C language in C18 compiler :)
 

DickCappels

Joined Aug 21, 2008
10,661
It sounds like what you want is the controller to stop and wait fro the rest rather than to continue testing the ADC inputs.

A simple way to do that is with an infinite loop.

#include<stdio.h>
void main()
while (1)
{
// you code here
}
 

Thread Starter

LewisMF

Joined Nov 15, 2014
119
Hi DickCappels,

Thank you for your interest on helping me with this.

I already have an infinite loop, therefore the PIC is constantly reading the ADC channels 0 and 1.

What I need is if one of those ADC channels go over a certain threshold, the PIC carries out a certain function and stays in that state until reset.

Even know it is in that 'alarm state' I need the ADC channels to still be converting and checking for any other possible states but for the PIC to remain in the 'alarm state'.

I hope I explained myself correctly.
 

DickCappels

Joined Aug 21, 2008
10,661
Then you might want to modify your code so that it stops checking the ADC channel whose threshold has already been crossed.

There are probably more elegant ways around this but I would use flags (bits somewhere or write a values to a variable that was set to zero right after reset) and set the flag once a threshold is crossed and using that flag skip the ADC measurement for that channel the next time through the loop. There would be a separate flag and test for each ADC channel that is handled that way.
 

Thread Starter

LewisMF

Joined Nov 15, 2014
119
I have tried this already but the problem is that I need the ADC to keep converting while in the 'alarm state' because I need to keep measuring the line in case on of the sensors on the ADC are shorted or the line has been open.

Any ideas?
 

ericgibbs

Joined Jan 29, 2010
21,439
If I follow your requirement correctly.?
Have you considered just setting an Alarm flag bit when the ADC value exceeds the set level and only clearing the flag on a MCLR reset.?
If flag is Set then remain in Alarm state but keep sampling the ADC's.

E
 

djsfantasi

Joined Apr 11, 2010
9,237
Once the threshold has been reached on a channel, you state you want a function to be performed and the program to stay "latched" in that state. Talking about the function, will it be performed one time only when the threshold is exceeded, or repeat until MCLR is activated?

This can be done in logic with two Boolean variables (or flags). One to indicate when the threshold had been exceeded and a second to indicate that this is the first time the threshold was exceeded. I'll supply some code examples when I am on a computer.
 

Thread Starter

LewisMF

Joined Nov 15, 2014
119
Hi djsfantasi,

That is correct, the function will be repeated until MCLR is activated.

Code examples would be great, thankyou. I will be waiting for them :)

Thank you for your help.
 

Thread Starter

LewisMF

Joined Nov 15, 2014
119
If I follow your requirement correctly.?
Have you considered just setting an Alarm flag bit when the ADC value exceeds the set level and only clearing the flag on a MCLR reset.?
If flag is Set then remain in Alarm state but keep sampling the ADC's.

E
Hi Ericgibbs,

I do not really understand your solution, would it be possible for you to post some kind of pseudo-code to help understand it.

Thank you for you help :)
 

ericgibbs

Joined Jan 29, 2010
21,439
hi L,
I do not use C for programming.
A simple flow path would be something like this.
At power on, Clear the Alm Flag ;ensure its 0
Test the ADC value within your timed loop.
If the ADC => Alarm value, then Set AlmFlg =1 ; this is the Alarm state.

Within your timed ADC read loop, check to see if the AlmFlg is 0 or 1 and take the appropriate path option within your program.

The AlmFlg can be a single Bit within an Alarm Status Byte, the other Bits within the Byte could be used to latch other flag states

E
 

Thread Starter

LewisMF

Joined Nov 15, 2014
119
As requested, here is my code...

Code:
/*Libraries*/
#include <stdio.h>
#include <stdlib.h>
#include <delays.h>
#include <adc.h>
#include <my_lcd.h>
#include <p18f4520.h>

/*Fuses*/
#pragma config OSC = HS,FCMEN = OFF,IESO = OFF
#pragma config PWRT = ON,BOREN = OFF,BORV = 0
#pragma config WDT = OFF,WDTPS = 32768
#pragma config MCLRE = ON,LPT1OSC = OFF,PBADEN = OFF,CCP2MX = PORTC
#pragma config STVREN = OFF,LVP = OFF,XINST = OFF,DEBUG = OFF
#pragma config CP0 = OFF,CP1 = OFF,CP2 = OFF, CP3 = OFF
#pragma config CPB = OFF,CPD = OFF
#pragma config WRT0 = OFF,WRT1 = OFF,WRT2 = OFF
#pragma config WRTB = OFF,WRTC = ON,WRTD = OFF
#pragma config EBTR0 = OFF,EBTR1 = OFF,EBTR2 = OFF
#pragma config EBTRB = OFF

/*Pin definitions*/
#define GREEN_LED LATCbits.LATC0
#define RED_LED LATCbits.LATC1
#define YELLOW_LED LATCbits.LATC2
#define BUZZER_PIN LATCbits.LATC3
#define FAULT_RELAY_PIN LATCbits.LATC6
#define FIRE_RELAY_PIN LATCbits.LATC7

/*Variable declarations*/
unsigned int adc_result;
unsigned char config1, config2, portconfig;
unsigned int i, x=0;

/*Program Functions*/
void state_alarm(void)      //Function for alarm state
{

     while(BusyXLCD());                 //Wait while LCD is busy
     clear_lcd();                       //Clear LCD
     putrsXLCD("ALARM ON ZONE1!!");     //Output string to LCD

     RED_LED=1;            //Red LED is ON
     GREEN_LED=0;          //Green LED is OFF
     YELLOW_LED=0;           //Yellow LED is OFF

     BUZZER_PIN=1;          //Buzzer ON

     FIRE_RELAY_PIN=1;     //Fire relay is activated
     FAULT_RELAY_PIN=0;    //Fault relay is inactive

     Delay10KTCYx(250);
}

void state_normal(void)     //Function for normal state
{
     while(BusyXLCD());                  //Wait while LCD is busy
     WriteCmdXLCD(0x80);                 //Go to LCD position 0x80
     putrsXLCD("CONVENTIONAL FCP");      //Output string to LCD
     while(BusyXLCD());                  //Wait while LCD is busy
     WriteCmdXLCD(0xC5);                 //Go to LCD position 0xC0
     putrsXLCD("*STAND-BY*");            //Output string to LCD

     RED_LED=0;      //Red LED is ON
     GREEN_LED=1;    //Green LED is OFF
     YELLOW_LED=0;   //Yellow LED is OFF

     BUZZER_PIN=0;   //Buzzer ON

     FIRE_RELAY_PIN=0;     //Fire relay is activated
     FAULT_RELAY_PIN=0;    //Fault relay is inactive

     Delay10KTCYx(250);
}

void state_opencircuit(void)    //Function for open circuit
{

    while(BusyXLCD());                //Wait while LCD is busy
    WriteCmdXLCD(0x80);               //Go to LCD position 0x80
    putrsXLCD("*OPEN CIRCUIT!*");     //Output string to LCD
   
    RED_LED=0;      //Red LED is OFF
    GREEN_LED=0;    //Green LED is OFF
    YELLOW_LED=1;   //Yellow LED is ON

    FIRE_RELAY_PIN=0;     //Fire relay is inactive
    FAULT_RELAY_PIN=1;    //Fault relay is activated

    BUZZER_PIN=1;       //Buzzer ON
    Delay10KTCYx(250);  //Wait 500mS
    BUZZER_PIN=0;       //Buzzer OFF
    Delay10KTCYx(250);  //Wait 500mS
}

void state_shortcircuit(void)   //Function for short circuit
{
    while(BusyXLCD());                 //Wait while LCD is busy
    WriteCmdXLCD(0x80);                //Go to LCD position 0x80
    putrsXLCD("*SHORT CIRCUIT!*");     //Escribir mensaje en LCD

    RED_LED=0;      //Red LED is OFF
    GREEN_LED=0;    //Green LED is OFF
    YELLOW_LED=1;   //Yellow LED is ON

    FIRE_RELAY_PIN=0;    //Fire relay is inactive
    FAULT_RELAY_PIN=1;   //Fault relay is activated

    BUZZER_PIN=1;        //Buzzer ON
    Delay10KTCYx(250);   //Wait 500mS
    BUZZER_PIN=0;        //Buzzer OFF 500mS.
    Delay10KTCYx(250);   //Wait 500mS
}

void initialize_LCD(void)   //LCD Initialization code
{
    OpenXLCD(FOUR_BIT & LINES_5X7);        //Four bit mode and 5x7 lines
    while(BusyXLCD());                     //Wait while LCD is busy
    WriteCmdXLCD(CURSOR_OFF & BLINK_OFF);  //Cursor OFF
}

/*MAIN PROGRAM*/
void main(void)
{
    ADCON1=0x0E;    //ADC_CH0 as analog input
    TRISC=0x00;     //PORTC as digital outputs

    Delay10KTCYx(255);
   
    initialize_LCD();   //Initialize LCD

    INTCONbits.GIE=0;   //Disable all interrupts

    config1 = ADC_FOSC_16 & ADC_RIGHT_JUST & ADC_16_TAD ;
    config2 = ADC_CH0 & ADC_INT_OFF & ADC_VREFPLUS_VDD & ADC_VREFMINUS_VSS ;
    portconfig = ADC_1ANA;

    while(1)
    {
         OpenADC(config1,config2,portconfig);   //Open the ADC converter
         ConvertADC();                          //Start converting
         while(BusyADC());                      //Wait while ADC is converting
         adc_result = ReadADC();                //Read the ADC result and store it in variable
         CloseADC();                            //Close the ADC

            if(adc_result<=388 && adc_result>=0.20) //ADC result for alarm condition, line current aprox. 25mA
            {
                     state_alarm();    //Execute the function 'state_alarm'
            }

             else if(adc_result>388 && adc_result<583) //ADC result for normal condition, line current < 25mA
                {
                     state_normal();    //Execute the function 'state_normal'
                }

             else if (adc_result>=583)   //ADC result for open circuit, line current = 0mA
                {
                     state_opencircuit();    //Execute the function 'state_opencircuit'
                }

             else if(adc_result<0.20)     //ADC result for short-circuit, line current > 100mA
                {
                     state_shortcircuit();    //Execute the function 'state_shortcircuit'
                }
        }
}
If you have any questions regarding the code, please do not hesitate to ask.
 

djsfantasi

Joined Apr 11, 2010
9,237
...And as promised, here is my example. Note how I defined boolean variables to represent the various states. Then, when deciding whether or not to call your "function", I test for
Has this channel gone over the threshold and has it not done so before?
I.e., this is the first time I have seen the threshold exceeded. Given your example, I am confident that you can understand the example and modify it to your requirement,
Code:
// pseudo-code

byte inputValue=0; // size dependent on what the ADC returns
boolean ch0ThresholdExceeded=false;
boolean ch0TriggeredBefore  =false;

#define Threshold 128

void yourFunction(byte myValue) {
  // code to do whatever you need
}

void main() {

  while (-1) {
     inputValue=readADC(0); // read from the ADC
     ch0ThresholdExceeded= (inputValue >= Threshold); // has the threshold
                                                      // been exceeded?
    if (ch0ThresholdExceeded & ! ch0TriggeredBefore) {
      yourFunction(inputValue); // only call function the first time
      ch0TriggeredBefore=true; // set the flag to prevent repeated execution
                                // of the function
    }
    // a similar block of code would be used for channel 1
  }
}
 

DickCappels

Joined Aug 21, 2008
10,661
From my less-than-perfect understanding of what you want to accomplish, you want to prevent this test from being made after the alarm condition has been entered, correct?

else if(adc_result>388 && adc_result<583) //ADC result for normal condition, line current < 25mA
{
state_normal(); //Execute the function 'state_normal'
}
 

Thread Starter

LewisMF

Joined Nov 15, 2014
119
From my less-than-perfect understanding of what you want to accomplish, you want to prevent this test from being made after the alarm condition has been entered, correct?

else if(adc_result>388 && adc_result<583) //ADC result for normal condition, line current < 25mA
{
state_normal(); //Execute the function 'state_normal'
}
Hi DickCappels,

Yes I want to prevent the state_normal(); function from being entered when the PIC's ADC reaches the alarm threshold and keep the PIC in the state_alarm(); function.

But at the same time, I need the ADC to keep converting in case a short circuit or an open circuit occurs (the other two states)
 

Thread Starter

LewisMF

Joined Nov 15, 2014
119
...And as promised, here is my example. Note how I defined boolean variables to represent the various states. Then, when deciding whether or not to call your "function", I test for
Has this channel gone over the threshold and has it not done so before?
I.e., this is the first time I have seen the threshold exceeded. Given your example, I am confident that you can understand the example and modify it to your requirement,
Code:
// pseudo-code

byte inputValue=0; // size dependent on what the ADC returns
boolean ch0ThresholdExceeded=false;
boolean ch0TriggeredBefore  =false;

#define Threshold 128

void yourFunction(byte myValue) {
  // code to do whatever you need
}

void main() {

  while (-1) {
     inputValue=readADC(0); // read from the ADC
     ch0ThresholdExceeded= (inputValue >= Threshold); // has the threshold
                                                      // been exceeded?
    if (ch0ThresholdExceeded & ! ch0TriggeredBefore) {
      yourFunction(inputValue); // only call function the first time
      ch0TriggeredBefore=true; // set the flag to prevent repeated execution
                                // of the function
    }
    // a similar block of code would be used for channel 1
  }
}
Hi djsfantasi,

Thank you for the pseudo-code.

I am going to test it in my program and I will get back to you :)
 

Thread Starter

LewisMF

Joined Nov 15, 2014
119
Hi djsfantasi,

I have been trying to implement this in C18 using standard C but there are no built in boolean values.

Is there any way I can implement this using variables? Or any other way to do it?

Thanks in advanced for your help! :)
 

Thread Starter

LewisMF

Joined Nov 15, 2014
119
I have just tested the code as you explained and it works, the function does not repeat itself but there is a problem...

When the alarm threshold is crossed, the PIC executes the state_alarm(); function and stays there as long as the ADC value is over the threshold, the moment the ADC's value goes below the threshold again, the PIC goes back to execute the state_normal(); function.

So basically I am in the same situation as before...

What I need is for the PIC to stay in the state_alarm(); function until reset, independent of the ADC's value but at the same time, still sample the ADC in case of short or open circuit on the line.

Here is the new code:

Code:
/*Libraries*/
#include <stdio.h>
#include <stdlib.h>
#include <delays.h>
#include <adc.h>
#include <my_lcd.h>
#include <p18f4520.h>

/*Fuses*/
#pragma config OSC = HS,FCMEN = OFF,IESO = OFF
#pragma config PWRT = ON,BOREN = OFF,BORV = 0
#pragma config WDT = OFF,WDTPS = 32768
#pragma config MCLRE = ON,LPT1OSC = OFF,PBADEN = OFF,CCP2MX = PORTC
#pragma config STVREN = OFF,LVP = OFF,XINST = OFF,DEBUG = OFF
#pragma config CP0 = OFF,CP1 = OFF,CP2 = OFF, CP3 = OFF
#pragma config CPB = OFF,CPD = OFF
#pragma config WRT0 = OFF,WRT1 = OFF,WRT2 = OFF
#pragma config WRTB = OFF,WRTC = ON,WRTD = OFF
#pragma config EBTR0 = OFF,EBTR1 = OFF,EBTR2 = OFF
#pragma config EBTRB = OFF

/*Pin definitions*/
#define GREEN_LED LATCbits.LATC0
#define RED_LED LATCbits.LATC1
#define YELLOW_LED LATCbits.LATC2
#define BUZZER_PIN LATCbits.LATC3
#define FAULT_RELAY_PIN LATCbits.LATC6
#define FIRE_RELAY_PIN LATCbits.LATC7

/*Variable declarations*/
unsigned int adc_result;
unsigned char config1, config2, portconfig;
unsigned int exceeded=0, triggered=0;       //Exceeded and triggered variable for ADC channel 0

/*Program Functions*/
void state_alarm(void)      //Function for alarm state
{

     while(BusyXLCD());     //Wait while LCD is busy
     clear_lcd();           //Clear LCD
     putrsXLCD("ALARM ON ZONE1!!");     //Output string to LCD

     RED_LED=1;      //Red LED is ON
     GREEN_LED=0;    //Green LED is OFF
     YELLOW_LED=0;   //Yellow LED is OFF

     BUZZER_PIN=1;   //Buzzer ON

     FIRE_RELAY_PIN=1;     //Fire relay is activated
     FAULT_RELAY_PIN=0;    //Fault relay is inactive

     Delay10KTCYx(250);
}

void state_normal(void)     //Function for normal state
{
     while(BusyXLCD());                  //Wait while LCD is busy
     WriteCmdXLCD(0x80);                 //Go to LCD position 0x80
     putrsXLCD("CONVENTIONAL FCP");      //Output string to LCD
     while(BusyXLCD());                  //Wait while LCD is busy
     WriteCmdXLCD(0xC3);                 //Go to LCD position 0xC0
     putrsXLCD("*STAND-BY*");            //Output string to LCD

     RED_LED=0;      //Red LED is ON
     GREEN_LED=1;    //Green LED is OFF
     YELLOW_LED=0;   //Yellow LED is OFF

     BUZZER_PIN=0;   //Buzzer ON

     FIRE_RELAY_PIN=0;     //Fire relay is activated
     FAULT_RELAY_PIN=0;    //Fault relay is inactive

     Delay10KTCYx(250);
}

void state_opencircuit(void)    //Function for open circuit
{
    clear_lcd();                      //Clear the LCD

    while(BusyXLCD());                //Wait while LCD is busy
    WriteCmdXLCD(0x80);               //Go to LCD position 0x80
    putrsXLCD("*OPEN CIRCUIT!*");     //Output string to LCD
   
    RED_LED=0;      //Red LED is OFF
    GREEN_LED=0;    //Green LED is OFF
    YELLOW_LED=1;   //Yellow LED is ON

    FIRE_RELAY_PIN=0;     //Fire relay is inactive
    FAULT_RELAY_PIN=1;    //Fault relay is activated

    BUZZER_PIN=1;       //Buzzer ON
    Delay10KTCYx(250);  //Wait 500mS
    BUZZER_PIN=0;       //Buzzer OFF
    Delay10KTCYx(250);  //Wait 500mS
}

void state_shortcircuit(void)   //Function for short circuit
{
    clear_lcd();                       //Clear the LCD

    while(BusyXLCD());                 //Wait while LCD is busy
    WriteCmdXLCD(0x80);                //Go to LCD position 0x80
    putrsXLCD("*SHORT CIRCUIT!*");     //Escribir mensaje en LCD

    RED_LED=0;      //Red LED is OFF
    GREEN_LED=0;    //Green LED is OFF
    YELLOW_LED=1;   //Yellow LED is ON

    FIRE_RELAY_PIN=0;    //Fire relay is inactive
    FAULT_RELAY_PIN=1;   //Fault relay is activated

    BUZZER_PIN=1;        //Buzzer ON
    Delay10KTCYx(250);   //Wait 500mS
    BUZZER_PIN=0;        //Buzzer OFF 500mS.
    Delay10KTCYx(250);   //Wait 500mS
}

void initialize_LCD(void)   //LCD Initialization code
{
    OpenXLCD(FOUR_BIT & LINES_5X7);        //Four bit mode and 5x7 lines
    while(BusyXLCD());                     //Wait while LCD is busy
    WriteCmdXLCD(CURSOR_OFF & BLINK_OFF);  //Cursor OFF
}

/*MAIN PROGRAM*/
void main(void)
{
    ADCON1=0x0E;    //ADC_CH0 as analog input
    TRISC=0x00;     //PORTC as digital outputs

    Delay10KTCYx(255);
   
    initialize_LCD();   //Initialize LCD

    INTCONbits.GIE=0; //Disable all interrupts

    config1 = ADC_FOSC_16 & ADC_RIGHT_JUST & ADC_16_TAD ;
    config2 = ADC_CH0 & ADC_INT_OFF & ADC_VREFPLUS_VDD & ADC_VREFMINUS_VSS ;
    portconfig = ADC_1ANA;

    while(1)
    {
         OpenADC(config1,config2,portconfig);   //Open the ADC converter
         ConvertADC();                          //Start converting
         while(BusyADC());                      //Wait while ADC is converting
         adc_result = ReadADC();                //Read the ADC result and store it in variable
         CloseADC();                            //Close the ADC

            if(adc_result<=295 && adc_result>=0.20) //ADC result for alarm condition, line current aprox. 25mA
            {
                exceeded = 1;     //Set threshold exceeded flag to 1 
               
                if(exceeded != triggered)   //If exceeded is != to trigger...
                {
                     state_alarm();     //Execute function state_alarm();
                     triggered = 1;     //Set triggered flag to 1 so the function is not repeated
                }
            }

             else if(adc_result>295 && adc_result<531) //ADC result for normal condition, line current < 25mA
                {
                     state_normal();
                }

             else if (adc_result>=531)   //ADC result for open circuit, line current = 0mA
                {
                     state_opencircuit();
                }

             else if(adc_result<0.20)     //ADC result for short-circuit, line current > 100mA
                {
                     state_shortcircuit();
                }
        }
}
As you can see, in the main function, the first check that is carried out is the alarm threshold check, which is the part of most interest.

:)
 
Top