Medição de Distância com Sensor Ultrassônico e RL78

Olá pessoal!

Após a correria de lançamento do meu último livro, volto a postar um artigo técnico aproveitando um dos exemplos do livro. Neste artigo mostro como utilizar um sensor ultrassônico HC-SR04 para efetuar a medição de distâncias (até cerca de quatro metros) de forma simples e barata.

O sensor propriamente dito consiste numa pequena placa de circuito impresso contendo dois transdutores de ultrassom (um transmissor e um receptor), além de circuitos para geração e recepção do sinal de US. A figura abaixo mostra o sensor e a pinagem do mesmo.

HC-SR04

Este sensor pode ser adquirido na DealExtreme, eBay, WayEngineer, Goodluckbuy, etc.

 A utilização do mesmo é bastante simples: alimenta-se o mesmo (pinos VCC e GND) com uma tensão de 5V e, a cada pulso no pino de disparo (Trig) o sensor dispara um trem de 5 pulsos ultrassônicos. A largura do sinal de disparo deve ser de no mínimo 10µs. Ao receber o eco do sinal, o sensor gera no seu pino Echo um pulso com largura proporcional ao tempo decorrido entre o envio dos pulsos e a recepção do eco.

O cálculo da distância pode ser facilmente realizado através da seguinte fórmula:

 

distance_f1

 

Assim, se o pulso tiver uma largura de 1000µs, o alvo estará a 0,17m do sensor. Para obter a distância em cm:

distance_f2

Note que, para o cálculo, o pulso deverá sempre ser medido em µs. A seguir apresento a listagem do exemplo completo. O pino TRIG do módulo HC-SR04 deve ser conectado ao pino P4.1 do RL78 e o pino ECHO deve ser conectado ao pino P4.2 (entrada TI04) do RL78 (foi utilizada a placa do Starter Kit do RL78/G13).

 

distance

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

#define TRIGGER       P4_bit.no1

volatile unsigned int interval;
unsigned long distance;
volatile __saddr struct
{
  unsigned char display_update  : 1;
  unsigned char echo_received   : 1;
  unsigned char trig_mode       : 1;
  unsigned char aux             : 1;
} bits;

#pragma vector = INTTM00_vect
__interrupt void trata_TAU0_canal0(void)
{
  if (bits.trig_mode)
  {
    TRIGGER = 1;
    TDR00 = 9; // intervalo de 10us para o disparo
    TS0L = TAU_CH0;
    bits.echo_received = 0; 
  } else
  {
    TRIGGER = 0;
    TDR00 = 0xFFFF;
    TS0L = TAU_CH0;
    if (!bits.echo_received) interval=0xFFFF;    
  }  
  bits.trig_mode = !bits.trig_mode;
}
#pragma vector = INTTM04_vect
__interrupt void trata_TAU0_canal4(void)
{
  interval = TDR04;         // salva o valor capturado
  bits.echo_received = 1;   // sinaliza que recebeu um pulso de eco
  bits.aux = !bits.aux;
  if (bits.aux) bits.display_update = 1;  // atualiza o display
}

/*
  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 TAU_init(void)
{
  TAU0EN = 1;         // habilita a TAU0
  TPS0 = TAU_CK0_DIV32; // CK0=1MHz, CK1=32MHz, CK2=16MHz e CK3=125kHz
  // configura o canal 0 da TAU0 no modo temporizador
  TMR00 = TAU_CK0 | TAU_TRIG_SOFT | TAU_MD_ONECOUNT;
  TDR00 = 9;          // intervalo inicial de 10us
  // configura o canal 4 da TAU0 no modo de captura de ciclo ativo
  TMR04 = TAU_CK0|TAU_EDGE_RISE_FALL|TAU_TRIG_BOTH_EDGE|TAU_MD_CAPTURE_LEVEL;  
  TS0L = TAU_CH4 | TAU_CH0; // Arma os canais 0 e 4
  TMMK00 = 0;         // habilita a interrupção do canal 0 da TAU0
  TMMK04 = 0;         // habilita a interrupção do canal 4 da TAU0
}

void SYS_init(void)
{
  PM4_bit.no1 = 0;        // P41 como saída (TRIGGER)  
  TAU_init();             // inicializa a TAU0
  LCD_init(DISPLAY_8x5|_2LINES,DISPLAY_ON|CURSOR_OFF|CURSOR_FIXED);
  LCD_write_char('\f');   // apaga o display
  __enable_interrupt();   // habilita as interrupções do RL78
}

void main(void)
{
  SYS_init();  
  LCD_pos_xy(0,0);              // cursor na coluna 0 da linha 0
  LCD_write_string("Distanc");
  TRIGGER = 1;	// inicia um pulso de disparo para o módulo de ultrassom
  bits.trig_mode = 0;           
  TS0L = TAU_CH0;               // Dispara o canal 0 
  while(1)
  {
    if (bits.display_update)
    {
      bits.display_update = 0;  // apaga o bit de atualização
      LCD_pos_xy(0,1);          // cursor na coluna 0 da linha 1
      // calcula a distância em cm
      distance = ((long)interval*100)/58;
      if (distance<=40000)      // se a distância for <= 400cm
      {
        LCD_write_frac_int(distance);
        LCD_write_string("cm   ");
      } else LCD_write_string("---.--cm");
    }
  }
}

Leave a Reply