Analog and interrupt

Thread Starter

FroceMaster

Joined Jan 28, 2012
708
Hi
Have this Little project
at AN1 i have a V. controlled by a LDR and a resistor.

When the lights outside be to bright the PIC should do something, and when it's to dark it should do another thing.

Can the analog reading be triggering an interrupt, or do the code has to run over and check the voltage every run. ?
Here is my code. Chip is 16F1509
Code:
#include <htc.h>
#include <stdio.h>
#include <stdlib.h>
#include "lcd.h"
#include <string.h>
__CONFIG (CLKOUTEN_OFF & FCMEN_ON & IESO_OFF & BOREN_OFF & CP_OFF & MCLRE_OFF & PWRTE_ON & WDTE_OFF & FOSC_INTOSC);//XT
__CONFIG (LVP_ON & LPBOR_OFF & BOREN_ON & STVREN_ON & WRT_OFF);
#define _XTAL_FREQ 4000000
#define FOSC 4000000L
volatile signed int light;
volatile bit on;
//global defs

  union
{
unsigned int res;
char bytes[2];
}
ADC;
char ADC_num[6];
float lightintens;

static void interrupt isr(void)   // Here is interrupt function - the name is unimportant.
{
    //no interrupt yet.
}// end interrupt


   
void setup(void)
{    
IRCF3=1;//16MHz clock speed
IRCF2=1;
IRCF1=1;
IRCF0=1;
//REGISTER 5-1: OSCCON: OSCILLATOR CONTROL REGISTER
// bit 6-3 IRCF<3:0>: Internal Oscillator Frequency Select bits
//1111 = 16MHz
//1110 = 8MHz
//1101 = 4MHz
//1100 = 2MHz
//1011 = 1MHz
//1010 = 500 kHz(1)
//1001 = 250 kHz(1)
//1000 = 125 kHz(1)
//0111 = 500 kHz (default upon Reset)
//0110 = 250 kHz
//0101 = 125 kHz
//0100 = 62.5 kHz
//001x = 31.25 kHz
//000x = 31kHz LF
//Port analog / digital
TRISA=0; //all port ud.
TRISA1=1; //set a1 in
TRISB=0; // out
//TRISC=0b11111000;//RC0-RC2 out,RC3-RC7 in,
TRISC=0b00000000;// all other out.
GIE = 0;  // Global interrupt disable just in case
//ANSELA = 0b00000001;  // Set PORT AN0 to analog input AN1 to AN7 digital I/O
ANSELA=0; // set alle digital.
ANSA1=1; //analog
ANSELB=0;
ANSELC=0;//turn off all analog functions


///ADCON0=0b00000000;   // select right justify result. ADC port channel 0

//adcon1
ADFM=1;    //right justified
//ADCS=100; // fosc/4
ADPREF0=1;
ADPREF1=0;
//ADPREF=00; //vref+ = vdd
TRIGSEL0=0;
TRIGSEL1=0;
TRIGSEL2=0;
TRIGSEL3=0;
CHS0=1;  //an1
CHS1=0;
CHS2=0;
CHS3=0;
CHS4=0; // AN1  analog kanal  intern kanal

//timer1 settings
TMR1CS1=1; //CLOCK SOURCE S-ELECTIONS
TMR1CS0=0;
//11 =Timer1 clock source is Capacitive Sensing Oscillator (CAPOSC)
//10 =Timer1 clock source is pin or oscillator:
//If T1OSCEN = 0:
//External clock from T1CKI pin (on the rising edge)
//If T1OSCEN = 1:
//Crystal oscillator on SOSCI/SOSCO pins
//01 =Timer1 clock source is system clock (FOSC)
//00 =Timer1 clock source is instruction clock (FOSC/4)
T1CKPS1=0; // <1:0>: Timer1 Input Clock Prescale Select bits
T1CKPS0=0;
//11 = 1:8 Prescale value
//10 = 1:4 Prescale value
//01 = 1:2 Prescale value
//00 = 1:1 Prescale value
T1OSCEN=1;//LP Oscillator Enable Control bit
//1 = Dedicated Timer1 oscillator circuit enabled
//0 = Dedicated Timer1 oscillator circuit disabled
nT1SYNC=1; //: Timer1 Synchronization Control bit
TMR1ON=0;//: Timer1 On bit
TMR1H = 0x81;     // preset for timer1 MSB register
TMR1L = 0x00;     // preset for timer1 LSB register
TMR1GE=0; // gate for timer1 disable.
//T1GCON: TIMER1 GATE CONTROL REGISTER
//bit 7 TMR1GE: Timer1 Gate Enable bit
//If TMR1ON = 0:
//This bit is ignored
//If TMR1ON = 1:
//1 = Timer1 counting is controlled by the Timer1 gate function
//0 = Timer1 counts regardless of Timer1 gate function
  //Timer1 Interrupt prepare
   TMR1IE=1;// PIE1 register
   PEIE=1; //INTCON register
    //PIR1=0; // Clear all bits PERIPHERAL INTERRUPT REQUEST REGISTER 1
  
   //todo now in order to generate interrupt GEI=1 and TMR1ON=1

//timer0 settings.
TMR0IE=0; //use timer0 interrupt register.  ÆNDRET
TMR0CS=0; // bruger tæller,
TMR0SE=0; // lav til høj tælning.
IOCIE=0; // enable interrupt on change  ÆNDRET
PSA=1; //not assign prescaler. 
//other misc settings
} //end setup


void house_on (void)
{
RC0=1;
__delay_ms(5);
RC0=0;
RC1=1;
__delay_ms(5);
RC1=0;
RC2=1;
__delay_ms(5);
RC2=0;
RC3=1;
__delay_ms(5);
RC3=0;
RC4=1;
__delay_ms(5);
RC4=0;
RC5=1;
__delay_ms(5);
RC5=0;
RC6=1;
__delay_ms(5);
RC6=0;
RC7=1;
__delay_ms(5);
RC7=0;
  }
 
void lights_on (void)    //to dark or light.
{
    
                utoa(ADC_num, light, 10);
                if (light>50) on=0;
                if (light<=50)on=1;
               
               
}        // END lights on.
       
void main (void)
{
   
setup();         //runs setup
        __delay_ms(1000);
  // TMR1ON=1;    // timer1
         on=0;
       GIE=0;        //interrupt off
           while (1)// runs loop
          
       {
           lights_on();
           if (on==0)
           {
          
           __delay_ms(1000);       
           }
           if (on==1)
           {
               house_on();
           }
         }//end while endless loop
}//End main
 

Attachments

ErnieM

Joined Apr 24, 2011
8,415
The attached schematic does not show the LDR or resistor connected to AN1. In fact, it doesn't even show AN1.

See if your chip has a comparator; these can sense against a voltage and kick off an interrupt from an otherwise asleep processor.

When an AN input and a comparator input share a pin they may both be enables so the A2D and comparator read the same pin.
 

JohnInTX

Joined Jun 26, 2012
4,787
RA1 is AN1.
It looks like RA1 is set to analog (but you should remove all of the commented out junk to make it easier to read)
You don't have an interrupt routine so be sure you never set GIE. The other interrupts should not be enabled as a matter of good programming practice.

The main problem I see is that you are never actually reading the ADC. In lights_on, 'light' is converted to a string AND compared to a threshold but 'light' is never loaded with anything. You'd have to read the ADC in the result registers and convert to an integer. I would maintain 'light' as the most current light level as measured by the ADC then do any tests, conversion to ASCII etc. in separate routines.

The variable 'on' is 1)mis-declared as 'volatile' and 2)set as a side-effect in 'lights_on' - not a particularly good programming practice. 3)not a particularly good name - what is turned 'on'? Also, searching for 'on' to see where it occurs in the source gets lots of hits unrelated to the actual variable I'm looking for. Just one of my pet peeves - sorry.

In general you want to
1) Set GO/DONE to start the ADC reading
2) wait for DONE - a good time to do some housekeeping or other tasks.
3) read ADRES registers to form an unsigned int (or char if you take the upper 8 bits only - probably good enough) - into 'light' so that..
4) on = ('light' < some_threshold); // TRUE or FALSE loaded into 'on' depending on comparison
5) Take some action on 'on'.
6) repeat

The ADC is pretty fast - you might not need interrupt control at all. As you loop around main, if DONE is set, read the ADC result and set GO again. The conversion will probably be done by the time the program gets back to it. Post the results then hit it again. After the first conversion the result will always be valid so you don't have to wait on the ADC if you don't want to.

ErnieM has a good point, also. The 1509 has comparators with programmable internal reference levels and hysteresis. That would be a good fit for bang-bang control.

Have fun.
 
Last edited:

Thread Starter

FroceMaster

Joined Jan 28, 2012
708
HAve altered some codes, and yes the RA1 is AN1
The resistor and Photodiode(LDR) is placed on schematic.

The goal in the project is.
1. when surrounding lights gets to dark, the housenumber (RC0 to RC7) will lid up
2. when surrounding lights gets to bright, the housenumber will not lid up.

Reason to use PIC, : Not really anyone, ;)
Could have used some other components, but now i choose the PIC.


Now i can just alter the value reading analog to deside when the surroundning is to dark, and the housenumber should start to light up.
Code:
#include <htc.h>
#include <stdio.h>
#include <stdlib.h>
#include "lcd.h"
#include <string.h>
__CONFIG (CLKOUTEN_OFF & FCMEN_ON & IESO_OFF & BOREN_OFF & CP_OFF & MCLRE_OFF & PWRTE_ON & WDTE_OFF & FOSC_INTOSC);//XT
__CONFIG (LVP_ON & LPBOR_OFF & BOREN_ON & STVREN_ON & WRT_OFF);
#define _XTAL_FREQ 4000000
#define FOSC 4000000L
volatile signed int lightintens;
volatile bit on_housenumber;

  union
{
unsigned int res;
char bytes[2];
}
ADC;
char ADC_num[6];




   
void setup(void)
{    
IRCF3=1;//16MHz clock speed
IRCF2=1;
IRCF1=1;
IRCF0=1;
//REGISTER 5-1: OSCCON: OSCILLATOR CONTROL REGISTER
// bit 6-3 IRCF<3:0>: Internal Oscillator Frequency Select bits
//1111 = 16MHz
//1110 = 8MHz
//1101 = 4MHz
//1100 = 2MHz
//1011 = 1MHz
//1010 = 500 kHz(1)
//1001 = 250 kHz(1)
//1000 = 125 kHz(1)
//0111 = 500 kHz (default upon Reset)
//0110 = 250 kHz
//0101 = 125 kHz
//0100 = 62.5 kHz
//001x = 31.25 kHz
//000x = 31kHz LF

//Port analog / digital
TRISA=0; //all port ud.
TRISA1=1; //set a1 in
TRISB=0; // out
TRISC=0; //  out.
GIE = 0;  // Global interrupt disable just in case

ANSELA=0; // set all digital.
ANSA1=1; //analog
ANSELB=0;
ANSELC=0;//turn off all analog functions
ADFM=1;    //right justified
ADPREF0=1;
ADPREF1=0;

TRIGSEL0=0;
TRIGSEL1=0;
TRIGSEL2=0;
TRIGSEL3=0;
CHS0=1;  //an1
CHS1=0;
CHS2=0;
CHS3=0;
CHS4=0; // AN1  analog kanal  intern kanal


} //end setup


void housenumber_on (void)
{
RC0=1;
__delay_ms(5);
RC0=0;
RC1=1;
__delay_ms(5);
RC1=0;
RC2=1;
__delay_ms(5);
RC2=0;
RC3=1;
__delay_ms(5);
RC3=0;
RC4=1;
__delay_ms(5);
RC4=0;
RC5=1;
__delay_ms(5);
RC5=0;
RC6=1;
__delay_ms(5);
RC6=0;
RC7=1;
__delay_ms(5);
RC7=0;
  }
 
void check_lightintens (void)    //to dark or to light.
{
         GO_nDONE=1;                // initiate conversion on the channel 1  
             while(GO_nDONE) continue;
                 ADC.bytes[0]=ADRESL;
                 ADC.bytes[1]=ADRESH;
                utoa(ADC_num, lightintens, 10);
                if (lightintens>50) on_housenumber=0;
                if (lightintens<=50)on_housenumber=1;
               
               
}        // END lights on.
       
void main (void)
{
   
setup();         //runs setup
        __delay_ms(1000);
         on_housenumber=0;
    
           while (1)// runs loop
          
       {
           check_lightintens();
           if (on_housenumber==0)
           {
          
           __delay_ms(1000);       
           }
           if (on_housenumber==1)
           {
               housenumber_on();
           }
         }//end while endless loop
}//End main
 

JohnInTX

Joined Jun 26, 2012
4,787
Several problems here.
You are using the union to collect two bytes (ADRESH/L) into an integer but there is no guarantee that the bytes will be in the order you want - that's up to the compiler implementation.
Your use of utoa is suspect. 'lightintens' is never assigned anything - its not part of the union (even if the bytes happened to be in the proper order)
You don't need to test twice for a binary result - if lightintens is not >50, it must be <=50.
Here's how I would do it..
Code:
unsigned int LightIntensity;    // this does not need to be 'volatile' -
volatile means that the compiler has no control of its value, here it does.
unsigned char ADCtext[6];      // space for text represention of LightIntensity
bit on_housenumber;  // TRUE means its dark
  
    // read ADC then..
    // convert result to unsigned integer
    LightIntensity = (ADRESH*256) + ADRESL; // convert two bytes to int by arithmetic
        // OR
    LightIntensity = (ADRESH << 8) + ADRESL; // convert by logical shift
  
    // test value of the unsigned int (LightIntensity)
    on_housenumber = (LightIntensity <= 50); // true/false assigned to bit
        // OR
    if(LightIntensity <= 50)       // another way to to the same thing
        on_housenumber = 1;
    else
        on_housenumber = 0;
    
    // Finally, convert LightIntensity to an  ASCII string for display only
    //utoa converts UNSIGNED to ASCII - LightIntensity is the input parameter
    utoa(ADCtext,LightIntensity,10); // convert LightIntensity to text
You should use MPSIM or a PICkit debugger to step through your code - all of these problems would jump right out. For simulating the ADC you can use a stimulus file but I'd just use two char variables that I could load and pass them to the convert routine.
Any better?
 

Thread Starter

FroceMaster

Joined Jan 28, 2012
708
Hi
Have done a lot of fixing,
Have just these questions.
1. The sub "housenumber_on", can this be made some more clever, ex a loop ?
2. Will it be best to check ADC value , or somehow make a comparetor ?

Code:
#include <htc.h>
#include <stdio.h>
#include <stdlib.h>
#include "lcd.h"
#include <string.h>
__CONFIG (CLKOUTEN_OFF & FCMEN_ON & IESO_OFF & BOREN_OFF & CP_OFF & MCLRE_OFF & PWRTE_ON & WDTE_OFF & FOSC_INTOSC);//XT
__CONFIG (LVP_ON & LPBOR_OFF & BOREN_ON & STVREN_ON & WRT_OFF);
#define _XTAL_FREQ 4000000
#define FOSC 4000000L
signed int LightIntensity;
bit on_housenumber;

  union
{
unsigned int res;
char bytes[2];
}
ADC;
unsigned char ADC_num[6];




   
void setup(void)
{    
IRCF3=1;//16MHz clock speed
IRCF2=1;
IRCF1=1;
IRCF0=1;
//REGISTER 5-1: OSCCON: OSCILLATOR CONTROL REGISTER
// bit 6-3 IRCF<3:0>: Internal Oscillator Frequency Select bits
//1111 = 16MHz
//1110 = 8MHz
//1101 = 4MHz
//1100 = 2MHz
//1011 = 1MHz
//1010 = 500 kHz(1)
//1001 = 250 kHz(1)
//1000 = 125 kHz(1)
//0111 = 500 kHz (default upon Reset)
//0110 = 250 kHz
//0101 = 125 kHz
//0100 = 62.5 kHz
//001x = 31.25 kHz
//000x = 31kHz LF

//Port analog / digital
TRISA=0; //all port ud.
TRISA1=1; //set a1 in
TRISB=0; // out
TRISC=0; //  out.
GIE = 0;  // Global interrupt disable just in case

ANSELA=0; // set all digital.
ANSA1=1; //analog
ANSELB=0;
ANSELC=0;//turn off all analog functions
ADFM=1;    //right justified
ADPREF0=1;
ADPREF1=0;

TRIGSEL0=0;
TRIGSEL1=0;
TRIGSEL2=0;
TRIGSEL3=0;
CHS0=1;  //an1
CHS1=0;
CHS2=0;
CHS3=0;
CHS4=0; // AN1  analog


} //end setup


void housenumber_on (void)
{
RC0=1;
__delay_ms(5);
RC0=0;
RC1=1;
__delay_ms(5);
RC1=0;
RC2=1;
__delay_ms(5);
RC2=0;
RC3=1;
__delay_ms(5);
RC3=0;
RC4=1;
__delay_ms(5);
RC4=0;
RC5=1;
__delay_ms(5);
RC5=0;
RC6=1;
__delay_ms(5);
RC6=0;
RC7=1;
__delay_ms(5);
RC7=0;
  }
 
void check_lightintens (void)    //to dark or to light.
{
         GO_nDONE=1;                // initiate conversion on the channel 1  
             while(GO_nDONE) continue;
                 ADC.bytes[0]=ADRESL;
                 ADC.bytes[1]=ADRESH;
                 LightIntensity = (ADRESH << 8) + ADRESL;
                
                on_housenumber = (LightIntensity <= 50);
               
                             
               
}        // END lights on.
       
void main (void)
{
   
setup();         //runs setup
      
         on_housenumber=0;
    
           while (1)// runs loop
          
       {
           check_lightintens();
           if (on_housenumber==0)
           {
          
           __delay_ms(1000);       
           }
           if (on_housenumber==1)
           {
               housenumber_on();
           }
         }//end while endless loop
}//End main
 

Thread Starter

FroceMaster

Joined Jan 28, 2012
708
All has now been testet, but i have one small problem.
When the MCU is reading analog, the "pause" is to long, and the LEDS will be off.
Somehow i will need the "on_housenumber" to run even when reading analog, can this be done ?
The "pause" is not long, just some milliseconds, but enough to see the LEDS off.
 
Top