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):
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):
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 } } }