Temperature indicator Project

Thread Starter

Missio2468

Joined Mar 18, 2022
69
I am creating a project of temperature indication using thermistor with pic16f877A. and 4 channel 7 segment Display.
The problem I am facing is Using timer to read adc values and converting to Celcius makes my 4 channel 7 segment display to blink after every 2 Seconds. At ln0 line the display starts to blink.If I use to print ADC values then it is not a problem but As soon as I do the conversion from adc to Celcius it starts to blink.
Attaching my code file for reference.

C:
/*
 * File:   Rectal.c
 * Author: admin
 *
 * Created on 15 March, 2022, 10:36 AM
 */

#include"config.h"
#include<math.h>
#include<stdio.h>
//Values in the datasheet
#define RT0 10000
#define B 3977
#define VCC 5.03
#define R 10000

float RT01, VR0, ln0, TX0, T0, VRT0,T_SUM0, diff;
//Defining pins
#define Rect PORTAbits.RA0
#define Skin PORTAbits.RA1
#define b PORTBbits.RB5
#define a PORTBbits.RB4 
#define f PORTBbits.RB3 
#define g PORTBbits.RB2
#define d PORTBbits.RB1 
#define e PORTBbits.RB0 
#define c PORTDbits.RD7
#define d4 PORTDbits.RD6
#define d3 PORTDbits.RD5
#define d2 PORTDbits.RD4
#define d1 PORTCbits.RC7
#define set PORTDbits.RD1 //C2-d1
#define ret PORTCbits.RC2 //C3-d0
#define down PORTCbits.RC3//d0-c3
#define up PORTDbits.RD0//d1-c2

int j,k,l,m,n,o ,T_Approx=370;
int adc,i=0;
int count=0,s=0;
void zero() {
    a=0;
    b=0;
    c=0;
    d=0;
    e=0;
    f=0;
    g=1;
          }
void one() {
    a=1;
    b=0;
    c=0;
    d=1;
    e=1;
    f=1;
    g=1;
}
void two(){
    a=0;
    b=0;
    c=1;
    d=0;
    e=0;
    f=1;
    g=0;
}
void three(){
    a=0;
    b=0;
    c=0;
    d=0;
    e=1;
    f=1;
    g=0;
  }
void four() {
    a=1;
    b=0;
    c=0;
    d=1;
    e=1;
    f=0;
    g=0;
  }
void five(){
    a=0;
    b=1;
    c=0;
    d=0;
    e=1;
    f=0;
    g=0;
  }
void six(){
    a=0;
    b=1;
    c=0;
    d=0;
    e=0;
    f=0;
    g=0;
  }
void seven(){
    a=0;
    b=0;
    c=0;
    d=1;
    e=1;
    f=1;
    g=1;
    }
void eight(){
    a=0;
    b=0;
    c=0;
    d=0;
    e=0;
    f=0;
    g=0;
  }
void nine(){ //the 7-segment led display 9
    a=0;
    b=0;
    c=0;
    d=0;
    e=1;
    f=0;
    g=0;
  }
void clearLEDs(){ //clear the 7-segment display screen
       a=1;
    b=1;
    c=1;
    d=1;
    e=1;
    f=1;
    g=1;
    }

void pickDigit(int x){ //light up a 7-segment display
  //The 7-segment LED display is a common-cathode one. So also use digitalWrite to  set d1 as high and the LED will go out
    d1=1;
    d2=1;
    d3=1;
    d4=1;
  switch(x)
  {
    case 0: 
    d1=0;//Light d1 up 
    break;
    case 1: 
    d2=0; //Light d2 up 
    break;
    case 2: 
    d3=0; //Light d3 up 
    break;
    default: 
    d4=0; //Light d4 up 
    break;
  }
}
void pickNumber(int x){
  switch(x)
  {
    default: 
    zero(); 
    break;
    case 1: 
    one(); 
    break;
    case 2: 
    two(); 
    break;
    case 3: 
    three(); 
    break;
    case 4: 
    four(); 
    break;
    case 5: 
    five(); 
    break;
    case 6: 
    six(); 
    break;
    case 7: 
    seven(); 
    break;
    case 8: 
    eight(); 
    break;
    case 9: 
    nine(); 
    break;
  }
}

void ADC_Initialize(){
  ADCON0 = 0b10000001; //ADC ON and Fosc/16 is selected
  ADCON1 = 0b11000000; // Internal reference voltage is selected
    }

unsigned int ADC_Read(unsigned char channel)
{
  ADCON0 &= 0x11000101; //Clearing the Channel Selection Bits
  ADCON0 |= channel<<3; //Setting the required Bits
  //__delay_ms(2); //Acquisition time to charge hold capacitor
  ADCON0bits.GO_DONE = 1; //Initializes A/D Conversion
  while(ADCON0bits.GO_DONE); //Wait for A/D Conversion to complete
  return ((ADRESH<<8)+ADRESL); //Returns Result
}

void __interrupt() my_ISR(){
    
    if(INTCONbits.TMR0IF==1){
    count++;
    TMR0=11;//11
     T_SUM0=0;
    INTCONbits.TMR0IF=0;
    }   
  if(count==5959){count=0;s++;}//        PORTD=~PORTD;5959
  if(s==2){s=0;
  // INTCONbits.TMR0IE=0;
  for(i=0;i<3;i++){ 
  VRT0 = (ADC_Read(0));              //Acquisition analog value of VRT
  VRT0 = ((VCC / 1023.00) * VRT0);      //Conversion to voltage
  
  VR0 = VCC - VRT0;
  RT01 = VRT0 / (VR0 / R);//Resistance of RT
  ln0 = log(RT01 / RT0);
  TX0 = (1 / ((ln0/B) + (1 / T0)) ); //Temperature from thermistor
  TX0 = TX0 - 273.15;//Conversion to Celsius
  T_SUM0+= TX0;
    }
   T_SUM0 =T_SUM0*10;
   T_Approx = T_SUM0/(3);
 //INTCONbits.TMR0IE=1; 
  }
      
}
//}

void main(void) {
   TRISDbits.TRISD1 = 1;
    TRISDbits.TRISD0 = 1;
    TRISCbits.TRISC3 = 1;
    TRISCbits.TRISC2 = 1;
    
    TRISAbits.TRISA0 = 1; // defining analog pins
   TRISAbits.TRISA1 = 1;

   TRISBbits.TRISB0 = 0;// defining digital pins
    TRISBbits.TRISB3 = 0;
    TRISBbits.TRISB1 = 0;
    TRISBbits.TRISB2 = 0;
    
    TRISDbits.TRISD6 = 0;
    TRISDbits.TRISD5 = 0;
    TRISDbits.TRISD7 = 0;
    TRISDbits.TRISD4 = 0;
    TRISCbits.TRISC7 = 0;
    TRISBbits.TRISB5 = 0;
    TRISBbits.TRISB4 = 0;
    TRISCbits.TRISC6 = 0;
    ADC_Initialize();
    T0 =25+ 273.15;
        
    INTCONbits.GIE=1;
    INTCONbits.PEIE=1;
    INTCONbits.TMR0IE=1;
    INTCONbits.TMR0IF=0;
    ei();
    OPTION_REGbits.T0CS=0;
    OPTION_REGbits.T0SE=0;
    OPTION_REGbits.PSA=0;
    
    OPTION_REGbits.PS2=0;
    OPTION_REGbits.PS1=0;
    OPTION_REGbits.PS0=1;
  //TMR0=11;        

  j = T_Approx/10;
    k=j/10;
    l=k/10;   
    while(1){
            clearLEDs();//clear the 7-segment display screen
            pickDigit(0);//Light up 7-segment display d4
            pickNumber((T_Approx/1000)%10);//Get the value of single digit
            __delay_ms(0.5);//delay 5ms
    
            clearLEDs();//clear the 7-segment display screen
            pickDigit(1);//Light up 7-segment display d3
            pickNumber((T_Approx/100)%10);//get the value of ten
            __delay_ms(0.5);//delay 5ms
    
            clearLEDs();//clear the 7-segment display screen
            pickDigit(2);//Light up 7-segment display d2
            pickNumber((T_Approx/10)%10);// get the value of hundred
            __delay_ms(0.5);//delay 5ms
        
            clearLEDs();//clear the 7-segment display screen
            pickDigit(3);//Light up 7-segment display d1
            pickNumber( T_Approx%10);// get the value of thousand
            __delay_ms(0.5);//delay 5ms
  /*          
  if(!set){
  __delay_ms(200);
  if(set){m=1;
    //  T_Approx = ((1.8*T_Approx) + 32);
    //j = T_Approx/10;
    //k=j/10;
   // l=k/10;
  
  
   }
   }*/
    }
}
Mod edit: code tags - JohnInTX
 

Attachments

Last edited by a moderator:

MrSalts

Joined Apr 2, 2020
2,767
Floating point math is processor intensive and slow. This will cause your display update routing to be delayed. You have to think of ways to simplify the calculations and the real number of significant digits you need.


Although it seems to be only one line of instruction, libraries must be accessed to really execute this with the 35 instructions that a PIC has. Division is not in the standard instructions and log and ln are not standard instructions.

This statement could be simplified to a fast, non floating point instruction
VRT0 = ((VCC / 1023.00) * VRT0); //Conversion to voltage
To
VTR = (VCC >> 10) * VRT0; //right shift an integer by 10 places (same as divide by 1024.00).

you can also use lookup tables instead of doing the exact calculations.

finally, if you want to do work to nearest 0.01°C, you can do your math in hundredths of a degree and use 27315 as the offset instead of 273.15 to avoid floating point math. Then you can just set the decimal point where you need it on your 4-digit display. You don't even have to divide by 100. Just fake it with proper illumination of the right decimal point.
 

John P

Joined Oct 14, 2008
2,026
Wait a minute here. If you take a 16-bit integer and shift it 10 places right, you're left with 6 bits and you can only have 64 different states. If the original number was the output of a PIC processor's 10-bit A/D, then 10 bits was all you had in the first place! So that idea may need a little more thought.

But those floating-point operations just aren't going to work well on a little 8-bit processor which doesn't even have a multiply operation in its instruction set. You'd do better to consider lookup tables or fitting the curve to a simpler formula. How accurate can a thermistor ever be, anyway?
 

JohnInTX

Joined Jun 26, 2012
4,787
Consider multiplexing the display digits with a timer-generated interrupt. However you calculate, store the resulting segment patterns one byte per digit then output them in sequence, one per interrupt. The other good advice notwithstanding, at least the display won't blink when you are bogged down in calculations.
 

MrSalts

Joined Apr 2, 2020
2,767
Consider multiplexing the display digits with a timer-generated interrupt. However you calculate, store the resulting segment patterns one byte per digit then output them in sequence, one per interrupt. The other good advice notwithstanding, at least the display won't blink when you are bogged down in calculations.
I think he is doing the multiplexing but the natural log calculations and long division are leaving three digits off so long they persistence of vision on the three off digits becomes a noticeable.
 

JohnInTX

Joined Jun 26, 2012
4,787
I think he is doing the multiplexing but the natural log calculations and long division are leaving three digits off so long they persistence of vision on the three off digits becomes a noticeable.
Agreed and sorry I wasn't more clear on that.

The display multiplexing (scanning) is being interrupted by the long calculations as @MrSalts points out. That plus waiting on the conversion to complete and the (now commented out) 2ms delay, is going to cause the display problems described.

If it were me, I'd have 2 interrupts and use TIMER 1 to start the conversions:
  • Run the ADC using TIMER1/CCP2/ in CCP mode 1011. When timer 1 hits the CCP value, it automatically resets the timer and starts the ADC conversion with the preset parameters. You don't need an interrupt here.

  • TMR2/PR2/TMR2IF to refresh the display. Fetch the pre-calculated digits and put them on the display once per interrupt.
  • Read the ADC using ADIF - on interrupt, read it, post the data for main with a flag that says "New Data". Select the next channel. It will settle while waiting for the next start of conversion from TIMER1/CCP2 Timer 1 / CCP2 period has to be > than the conversion time but that should not be a problem. The ADC readings could be summed for an average reading - don't do any division, just accumulate the sum and post that as the ADC output when appropriate.

In 'main', poll the ADC flag and posted data from the ADC. Do all of the calculations and post the digits for the display mux. There will be plenty of time to do other things if necessary without affecting the ADC readings or display mux.

That's about all I can add.
Good luck!
 

Thread Starter

Missio2468

Joined Mar 18, 2022
69
Wi
Agreed and sorry I wasn't more clear on that.

The display multiplexing (scanning) is being interrupted by the long calculations as @MrSalts points out. That plus waiting on the conversion to complete and the (now commented out) 2ms delay, is going to cause the display problems described.

If it were me, I'd have 2 interrupts and use TIMER 1 to start the conversions:
  • Run the ADC using TIMER1/CCP2/ in CCP mode 1011. When timer 1 hits the CCP value, it automatically resets the timer and starts the ADC conversion with the preset parameters. You don't need an interrupt here.

  • TMR2/PR2/TMR2IF to refresh the display. Fetch the pre-calculated digits and put them on the display once per interrupt.
  • Read the ADC using ADIF - on interrupt, read it, post the data for main with a flag that says "New Data". Select the next channel. It will settle while waiting for the next start of conversion from TIMER1/CCP2 Timer 1 / CCP2 period has to be > than the conversion time but that should not be a problem. The ADC readings could be summed for an average reading - don't do any division, just accumulate the sum and post that as the ADC output when appropriate.

In 'main', poll the ADC flag and posted data from the ADC. Do all of the calculations and post the digits for the display mux. There will be plenty of time to do other things if necessary without affecting the ADC readings or display mux.

That's about all I can add.
Good luck!
Thank You, Will try that And revert Back.
 
Top