segunda-feira, abril 25, 2011

Pequenos Imãs e Micro Controladores - Parte 8

Neste post vamos ver a adaptação do software do nosso circuito de teste para apresentar a velocidade em RPM (rotações por minuto).


A medição da velocidade em RPM é, do ponto de vista teórico, muito simples. Basta usar a interrupção de timer para medir o tempo entre as detecções dos imãs. Sabendo que em cada volta teremos duas detecções, se o tempo entre elas é t segundos, a velocidade em RPM será 30/t.

Na prática temos algumas dificuldades. A taxa de interrupção que eu estava usando (256 uSeg) se mostrou inadequada. O meu processamento dentro da interrupção estava demorado demais, tornando a contagem de tempo não confiável. Uma solução seria colocar um cristal para aumentar o clock do processador, mas acabei optando pela saída mais simples de dividir por dois o clock do timer, obtendo uma interrupção a cada 512 uSeg.

Um outro cuidado a tomar é com os extremos da operação. O display permite mostrar um valor de até 9999 RPM, o que corresponde a uma contagem de 5 interrupções. Valores a cima disto serão apresentados como 9999. Para apresentar 0000 rapidamente quando estiver parado, coloquei um timeout de 10 segundos na detecção do imã.

O código final, abaixo, é bem semelhante ao que vimos na parte anterior. O tempo é contado através do contador cnta. Quando um imã é detectado, o valor em cnta é copiado para cntb que será usado na apresentação. Como na parte anterior, tempos uma pequena máquina de estados, desta vez com 4 estados:
  • 0: estamos contando o tempo até a próxima detecção para calcular a velocidade.
  • 1: cntb contém o tempo contado, é necessário calcular a velocidade
  • 2: a velocidade foi calculada e a nova apresentação do display está em newsegto
  • 3: segto foi atualizado, aguardar a próxima detecção para abrir uma nova contagem de tempo

#include <16F676.h>
#device adc=8
#use delay(clock=4000000)
#fuses NOWDT, NOCPD, NOPROTECT
#fuses INTRC_IO, PUT, BROWNOUT, MCLR

#define DLY_SR 2 // delay p/ shift register
#define TAXA_DISP 3 // taxa de varredura dos displays

#define TEMPO_LED 300 // tempo que o LED fica aceso
#define TIMEOUT 19531 // timeout p/ sensor

// Pinos de E/S
#define SENSOR PIN_A5
#define LED PIN_A4

#define SHIFT_DS PIN_A2
#define SHIFT_ST PIN_C0
#define SHIFT_SH PIN_C1

#define SEL_1 PIN_C5
#define SEL_2 PIN_C4
#define SEL_3 PIN_C3
#define SEL_4 PIN_C2


// Controle do LED
volatile int16 cntLed;

// Tempo entre passagens do imã
volatile unsigned int16 cnta = 0;
volatile unsigned int16 cntb = 0;

// Controle dos segmentos do display
// Um byte para cada segmento
// Bit 7 = segto G, Bit 6 = segto F, .. Bit 1 = segto A
unsigned int8 segto [4];
unsigned int8 newsegto [4];
unsigned int8 passo = 0;

// Segmentos a serem acesos para cada dígito
const unsigned int8 digitos[10] =
{
0b01111110, 0b00001100, 0b10110110, 0b10011110, 0b11001100,
0b11011010, 0b11111010, 0b00001110, 0b11111110, 0b11011110
};

// Rotinas locais
static void AtlDisp (void);
static void VarreDisp (void);


/*********************************************************************/

// P R O G R A M A P R I N C I P A L

void main()
{
unsigned int16 aux;

// Iniciações do hardware
set_tris_a (0xEB); // A4 e A2 output
#use fast_io(A)
set_tris_c (0xC0); // C0 a C5 output
#use fast_io(C)

setup_timer_0(RTCC_INTERNAL|RTCC_DIV_2);
setup_timer_1(T1_DISABLED);
setup_comparator(NC_NC);
setup_vref(FALSE);
setup_adc(ADC_OFF);

output_low (LED);
output_low (SEL_1);
output_low (SEL_2);
output_low (SEL_3);
output_low (SEL_4);

enable_interrupts(INT_RA5);
enable_interrupts(INT_RTCC);
enable_interrupts(GLOBAL);


for (;;)
{
if (passo == 1)
{
if (cntb >= TIMEOUT)
aux = 0; // considerar parado
else if (cntb > 5)
aux = 58594/cntb;
else
aux = 9999;

newsegto[0] = digitos [aux / (unsigned int16) 1000];
newsegto[1] = digitos [(aux/100) % 10];
newsegto[2] = digitos [(aux/10) % 10];
newsegto[3] = digitos [aux % 10];
passo = 2;
}
}
}

/*********************************************************************/

// S E N S O R

//////////////////////////////////////////////
// Interrupcao de mudança do sinal
// Ocorre qdo sensor detecta imã
//////////////////////////////////////////////
#int_ra
void Sensor_ISR ()
{
output_high (LED);
input_a();
clear_interrupt (INT_RA5);
cntLed = TEMPO_LED;
if (passo == 0)
{
cntb = cnta;
cnta = 0;
passo = 1;
}
else if (passo == 3)
{
cnta = 0;
passo = 0;
}
}

/*********************************************************************/

// T E M P O

//////////////////////////////////////////////
// Interrupcao de tempo real
// Ocorre a cada 512 uSeg
//////////////////////////////////////////////
#int_RTCC
void RTCC_isr()
{
static unsigned int8 cont_disp = TAXA_DISP;

// Tratamento do LED
if (cntLed)
{
if (--cntLed == 0)
output_low (LED);
}

// Atualização do Display
if (passo == 2)
{
AtlDisp ();
passo = 3;
}

if (--cont_disp == 0)
{
VarreDisp ();
cont_disp = TAXA_DISP;
}

// Medida do tempo
if ((passo == 0) || (passo == 3))
{
if (cnta < TIMEOUT)
cnta++;
else
{
// não detectou
cntb = cnta;
cnta = 0;
passo = 1;
}
}
}


/*********************************************************************/

// D I S P L A Y

static void AtlDisp (void)
{
segto[0] = newsegto[0];
segto[1] = newsegto[1];
segto[2] = newsegto[2];
segto[3] = newsegto[3];
}

static void VarreDisp (void)
{
static unsigned int8 disp = 0;
unsigned int8 i, mask;

// coloca os valores dos segmentos no registrador
for (i = 0, mask = 0x80; i < 8; i++)
{
if (segto[disp] & mask)
output_low (SHIFT_DS);
else
output_high (SHIFT_DS);
delay_us (DLY_SR);
output_high (SHIFT_SH);
delay_us (DLY_SR);
output_low (SHIFT_SH);
delay_us (DLY_SR);
mask = mask >> 1;
}

// seleciona o display atual
if (disp == 0)
{
output_low (SEL_4);
output_high (SEL_1);
}
else if (disp == 1)
{
output_low (SEL_1);
output_high (SEL_2);
}
else if (disp == 2)
{
output_low (SEL_2);
output_high (SEL_3);
}
else
{
output_low (SEL_3);
output_high (SEL_4);
}

// acende os segmentos
output_high (SHIFT_ST);
delay_us (DLY_SR);
output_low (SHIFT_ST);

// passa para o dígito seguinte
disp = (disp + 1) & 3;
}

O vídeo abaixo mostra um teste do software. Para conferir o cálculo, o sinal do sensor é monitorado em um osciloscópio. A frequência indicada no osciloscópio é 19.57Hz, o que corresponde a 1174 RPM, um valor bastante próximo do apresentado no display.



A adaptação do software para os outros sensores fica para o próximo post.

7 comentários:

Leandro disse...

Nossa muito bom mesmo seu projeto adorei, gostaria de saber qual o ci que ultilizou para controlar o display (BCD?), se possivel me passe o esquema elétrico.
Obrigado
tecnico_eletronico@hotmail.com

Daniel Quadros disse...

O circuito e uma descrição detalhada está em um post anterior: http://dqsoft.blogspot.com/2011/04/pequenos-imas-e-micro-controladores_19.html

Anônimo disse...

Tem a lista de componentes que é preciso?

Daniel Quadros disse...

Os principais componentes são um PIC 16F676, um 74HC595, um display sete segmentos quatro dígitos anodo comum (YSD-439AK2B-35) e o sensor efeito hall US1881. O display e o sensor foram comprados da Sparkfun através do Lab de Garagem. Além deles o meu circuito usa vários resistores e um LED comum. Além dos componentes, você vai precisar gravar o firmware no PIC, no meu circuito tem a conexão de um programador.

Anônimo disse...

quais os valores dos resistors? e qual o seu programa? dá para fazer com microgenios?

Daniel Quadros disse...

Os valores dos resistores estão no esquema (veja os links nas respostas anteriores). O programa está listado e explicado neste post. Não sei a que "microgenios" você se refere.

Elisilva disse...

Boa tarde a todos , a muito tempo que não via uma pagina excelente como essa , e excelentes projetos , como sempre digo o mundo sera melhor só com o compartilhamento de conhecimentos.´.