Termômetro Digital com o RL78

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:

RL78_temp_sensor1

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:

RL78_temp_sensor2

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

Leave a Reply