Neste artigo eu mostro como utilizar o sensor interno de temperatura, a referência interna de tensão e o conversor A/D do RL78/G13. A aplicação faz a leitura da tensão de saída do sensor interno de temperatura e a tensão da referência interna. Este procedimento é necessário para corrigir a saída do sensor conforme a tensão de alimentação do conversor A/D (que no caso é a mesma tensão de alimentação do restante do microcontrolador).
O ADC é configurado para operar no modo contínuo, convertendo alternadamente a tensão proveniente da referência interna de tensão (1,45V) e do sensor de temperatura integrado (esta alternância é controlada pela ISR do ADC).
A tensão de alimentação é determinada através da medição da tensão da referência interna de 1,45V, baseando-se no princípio de que o valor convertido será proporcional a tensão de alimentação do chip (mais detalhes sobre isso são mostrados no livro Microcontroladores RL78: Guia Básico). A medição da tensão de alimentação é necessária pois a tensão de saída do sensor interno de temperatura é uma função da tensão de alimentação do sistema.
A tensão de saída do sensor, para uma dada temperatura, pode ser obtida pela seguinte fórmula:
Já a temperatura pode ser calculada através do resultado da conversão em ADCR e considerando-se a tensão de alimentação VDD, através da seguinte fórmula:
Após os cálculos, o valor da tensão de alimentação e da temperatura ambiente são apresentados no display LCD da placa RSK do RL78/G13.
Este exemplo também ilustra uma técnica de formatação de dados para apresentação em display (ou outro meio) utilizando variáveis inteiras para representar valores fracionários. A função LCD_write_frac_int recebe um valor inteiro como parâmetro e o imprime no display utilizando o seguinte formato: X.YY onde a X corresponde à parte superior a 100 e YY à parte inferior a 100, que é tratada como a parte fracional do valor. Isto significa que o valor 100 será impresso como 1.00 e o valor 9999 será impresso como 99.99.
Note que a aplicação utiliza um valor de offset fixo para a temperatura (variável “toff”). Este valor foi determinado através de ensaio, utilizando como referência um termômetro medindo a temperatura ambiente. Em aplicações profissionais cada placa deveria ser calibrada na linha de produção e o valor de calibração salvo na memória flash.
O código da aplicação, conforme apresentado no livro Microcontroladores RL78: Guia Básico é mostrado abaixo:
#include "ior5f100le.h"
#include "ior5f100le_ext.h"
#include "intrinsics.h"
#include "myRL78.h"
#include "lcd_8x2.c"
// Configura watchdog
#pragma location = "OPTBYTE"
__root __far const char opbyte0 = WDT_OFF;
// Configura detector de baixa tensão
#pragma location = "OPTBYTE"
__root __far const char opbyte1 = LVD_OFF;
// oscilador 32MHz flash high speed
#pragma location = "OPTBYTE"
__root __far const char opbyte2 = FLASH_HS | CLK_32MHZ;
// debug ativado, com apagamento em caso de falha de autenticação
#pragma location = "OPTBYTE"
__root __far const char opbyte3 = DEBUG_ON_ERASE;
/* Configura security ID */
#pragma location = "SECUID"
__root __far const char senha[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 trata_ADC(void)
{
unsigned int result;
result = ADCR >> 6; // lê o resultado da conversão
// o bit adc_channel controla qual o canal do ADC sendo convertido
if (bits.adc_channel)
{ // conversão da referência interna de tensão
acc_vref += result - vref; // acumula o novo valor, subtrai a média
vref = acc_vref >> 6; // calcula a média dos 64 últimos valores
ADS = ADC_CH_TEMP; // seleciona o sensor de temperatura
bits.adc_channel = 0;
} else
{
acc_vtemp += result - vtemp; // acumula o novo valor, subtrai a média
vtemp = acc_vtemp >> 6; // calcula a média dos 64 últimos valores
ADS = ADC_CH_REF; // seleciona a referência interna
bits.adc_channel = 1;
}
}
#pragma vector = INTTM00_vect
__interrupt void trata_TAU0_canal0(void)
{
bits.display_update = 1; // atualiza o display a cada 500ms
}
void init(void)
{
ADPC = 0; // nenhum pino em modo analógico
ADCEN = 1; // habilita o ADC
// Configura o ADC (conversões múltiplas, um canal, disparo por software)
ADM0 = ADCLK_DIV64 | ADC_LV0 | bADCE;
ADM1 = ADC_TRIG_SOFT;
ADS = ADC_CH_REF; // seleciona a referência interna como entrada do ADC
ADMK = 0; // habilita interrupção do ADC
ADCS = 1; // inicia uma conversão
TAU0EN = 1; // habilita a TAU0
TPS0 = TAU_CK0_DIV1024; // CK0=31250Hz, CK1=32MHz, CK2=16MHz e CK3=125kHz
// configura o canal 0 da TAU0 no modo temporizador
TMR00 = TAU_CK0 | TAU_TRIG_SOFT | TAU_MD_TIMER;
TDR00 = 15624; // uma interrupção a cada 500ms no canal 0
TMMK00 = 0; // habilita a interrupção do canal 0 da TAU0
TS0L = TAU_CH0; // Dispara o canal 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 convertendo tensão de referência
__enable_interrupt(); // habilita as interrupções do RL78
}
/*
Esta função escreve um valor inteiro no LCD. Ela utiliza uma formatação
especial para representar um valor fracionário através de inteiros.
Os dígitos de dezenas de milhares, milhares e centenas são impressos,
seguidos do ponto decimal e dos dígitos de dezenas e unidades.
Assim, o valor 1001 é apresentado como 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'); // apaga o display
toff = -500; // offset de temperatura (-5 graus)
while(1)
{
// caso exista uma solicitação de atualização do display
if (bits.display_update)
{
bits.display_update = 0; // apaga o bit de atualização de display
LCD_pos_xy(0,0); // cursor na linha 0, coluna 0
LCD_write_string("V=");
long_data = 148480 / vref; // calcula o VDD
vdd = long_data;
LCD_write_frac_int(vdd); // imprime o VDD (5V = 5.00)
LCD_write_string(" "); // apaga caracteres remanescentes
LCD_pos_xy(0,1); // cursor na linha 1, coluna 0
LCD_write_string("T=");
// calcula a temperatura atual
long_data = 2500 - ((long)vtemp*(long)vdd*100 - 10752000)/368 + toff;
LCD_write_frac_int(long_data); // imprime a temperatura
LCD_write_string(" "); // apaga caracteres remanescentes
}
}
}

