Temperature measurement with the RL78

Hello, today I will show you how to make use of the internal temperature sensor, internal voltage reference and the Analog-to-Digital Converter on the RL78/G13 devices. The application monitors the voltage output by the internal temperature sensor and by the internal voltage reference. By reading both voltages we can compensate the temperature sensor output according to the ADC power supply (which, in this case, is the same for the whole MCU).

The ADC module is set to operate in continuous mode, alternating between two inputs: the internal voltage reference (1.45V) and the internal temperature sensor (the ISR switches the input channel).

Supply voltage is calculated by reading the internal voltage output (1.45V) and considering the read value is proportional to the ADC power supply (further details are shown on my free portuguese book Microcontroladores RL78: Guia Básico). Measuring the supply voltage is necessary as the temperature sensor’s voltage output is a function of the system power supply voltage.

The temperature sensor’s voltage output is calculated by using the following equation (it is written in brazilian portuguese):

RL78_temp_sensor1

Current temperature can be calculated with the following equation which uses the ADCR (ADC binary result) reading and supply voltage VDD (again the equation is written in portuguese):

RL78_temp_sensor2

After some math, the application presents the supply voltage and ambient temperature on the RL78/G13 RSK’s LCD display.

This example also illustrates a technic of data formatting for display presentation (or whatever other media) by using integer variables to represent fractional data. The LCD_wrie_frac_int function receives an integer value as a parameter and prints out data using the following format: X.YY where X is the higher than 100 part and YY is the less than 100 part, which is considered the fractional part. That way, 100 is printed as 1.00 and 9999 is printed as 99.99. Values lower than 100 are considered less than 1.00.

Notice that our application also makes use of a fixed temperature offset value (“toff” variable). This is due to the needed calibration for the internal temperature sensor. We came to the value by experimentation. On a professional environment it would be needed some kind of production calibration, storing the calibration data onto the flash during manufacturing.

The following code was taken from my portuguese Microcontroladres RL78: Guia Básico book:

#include "ior5f100le.h"
#include "ior5f100le_ext.h"
#include "intrinsics.h"
#include "myRL78.h"
#include "lcd_8x2.c"

// Watchdog config
#pragma location = "OPTBYTE"
__root __far const char opbyte0 = WDT_OFF;
// Low voltage detector config
#pragma location = "OPTBYTE"
__root __far const char opbyte1 = LVD_OFF;
// Selects internal 32MHz high speed oscillator
#pragma location = "OPTBYTE"
__root __far const char opbyte2 = FLASH_HS | CLK_32MHZ;
// Debug enabled, erases memory on password failure
#pragma location = "OPTBYTE"
__root __far const char opbyte3 = DEBUG_ON_ERASE;

/* Set the security ID */
#pragma location = "SECUID"
__root __far const char password[10] = {0,0,0,0,0,0,0,0,0,0};

volatile __saddr struct
{
  unsigned char display_update  : 1;
  unsigned char adc_channel     : 1;
} bits;

unsigned int acc_vref, vref, acc_vtemp, vtemp, vdd;
int toff;
unsigned long long_data;

#pragma vector = INTAD_vect
__interrupt void ADC_ISR(void)
{
  unsigned int result;
  result = ADCR >> 6;   // reads ADC result
  // adc_channel controls which channel will be selected
  if (bits.adc_channel)
  { // adc_channel==1 => selects the temperature sensor (voltage reference was converted)
    acc_vref += result - vref;  // accumulate the result, subtracs the average
    vref = acc_vref >> 6;       // calculates the average from 64 results
    ADS = ADC_CH_TEMP;          // selects the temperature sensor
    bits.adc_channel = 0;
  } else
  {
    acc_vtemp += result - vtemp;  // accumulate the result, subtracts the average
    vtemp = acc_vtemp >> 6;       // calculates the average from 64 results
    ADS = ADC_CH_REF;             // selects the internal reference
    bits.adc_channel = 1;
  }
}

#pragma vector = INTTM00_vect
__interrupt void TAU0_channel0_ISR(void)
{
  bits.display_update = 1;  // updates display data every 500ms
}

void init(void)
{
  ADPC = 0;         // no analog pin
  ADCEN = 1;        // enables ADC
  // Sets up ADC (multiple conversions, one channel, software triggered)
  ADM0 = ADCLK_DIV64 | ADC_LV0 | bADCE;
  ADM1 = ADC_TRIG_SOFT;
  ADS = ADC_CH_REF; // selects the internal reference as ADC input channel
  ADMK = 0;         // ADC interrupt enabled
  ADCS = 1;         // start an ADC conversion
  TAU0EN = 1;       // TAU0 enabled
  TPS0 = TAU_CK0_DIV1024; // CK0=31250Hz, CK1=32MHz, CK2=16MHz e CK3=125kHz
  // TAU0 channel 0 on timer mode
  TMR00 = TAU_CK0 | TAU_TRIG_SOFT | TAU_MD_TIMER;
  TDR00 = 15624;    // one interrupt every 500ms
  TMMK00 = 0;       // TAU0 channel 0 interrupt enabled
  TS0L = TAU_CH0;   // Start TAU0 channel 0
  LCD_init(DISPLAY_8x5|_2LINES,DISPLAY_ON|CURSOR_OFF|CURSOR_FIXED);
  acc_vref = vref = 0;
  acc_vtemp = vtemp = 0;
  bits.adc_channel = 1;   // ADC is converting the internal voltage reference
  __enable_interrupt(); // RL78 interrupts are enabled
}

/*
 This function writes an integer value on the LCD. It uses a special formatting
 in order to represent a fractional value with integers. The thousands and hundreds
 digits are printed followed by a decimal point and tens and units digits.
 Thus, an 1001 value is printed as 10.01
*/
void LCD_write_frac_int(unsigned int data)
{
  unsigned char aux, space;
  unsigned int sub = 10000;
  aux = 0;
  space = 1;
  while (sub)
  {
    aux = 0;
    while (data>=sub)
    {
      data -= sub;
      aux++;
      space = 0;
    }
    if (!space) LCD_write_char(aux+'0');
    sub /= 10;
    if (sub==10)
    {
      if (space) LCD_write_char('0');
      LCD_write_char('.');
      space = 0;
    }
  }
  if (space) LCD_write_char('0');
}

void main(void)
{
  init();  
  LCD_write_char('\f');         // clears display
  toff = -500;                  // temperature offset (-5 celsius degrees)
  while(1)
  {
    // if the display update is set
    if (bits.display_update)
    {
      bits.display_update = 0;    // clears display update flag
      LCD_pos_xy(0,0);            // cursor at line 0, column 0
      LCD_write_string("V=");     
      long_data = 148480 / vref;  // VDD calculation
      vdd = long_data;
      LCD_write_frac_int(vdd);    // prints out VDD (5V = 5.00)
      LCD_write_string("   ");    // erases remaining characters
      LCD_pos_xy(0,1);            // cursor at line 1, column 0
      LCD_write_string("T=");     
      // current temperature calculation
      long_data = 2500 - ((long)vtemp*(long)vdd*100 - 10752000)/368 + toff;
      LCD_write_frac_int(long_data);  // prints out the temperature
      LCD_write_string("   ");    // erases remaining characters
    }    
  }
}

Leave a Reply