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

  1. #include <16F676.h>  
  2. #device adc=8  
  3. #use delay(clock=4000000)  
  4. #fuses NOWDT, NOCPD, NOPROTECT  
  5. #fuses INTRC_IO, PUT, BROWNOUT, MCLR  
  6.   
  7. #define DLY_SR    2     // delay p/ shift register  
  8. #define TAXA_DISP 3     // taxa de varredura dos displays  
  9.   
  10. #define TEMPO_LED 300   // tempo que o LED fica aceso  
  11. #define TIMEOUT   19531 // timeout p/ sensor  
  12.   
  13. // Pinos de E/S  
  14. #define SENSOR PIN_A5  
  15. #define LED    PIN_A4  
  16.   
  17. #define SHIFT_DS  PIN_A2  
  18. #define SHIFT_ST  PIN_C0  
  19. #define SHIFT_SH  PIN_C1  
  20.   
  21. #define SEL_1     PIN_C5  
  22. #define SEL_2     PIN_C4  
  23. #define SEL_3     PIN_C3  
  24. #define SEL_4     PIN_C2  
  25.   
  26.   
  27. // Controle do LED  
  28. volatile int16 cntLed;  
  29.   
  30. // Tempo entre passagens do imã  
  31. volatile unsigned int16 cnta = 0;  
  32. volatile unsigned int16 cntb = 0;  
  33.   
  34. // Controle dos segmentos do display  
  35. // Um byte para cada segmento  
  36. // Bit 7 = segto G, Bit 6 = segto F, .. Bit 1 = segto A  
  37. unsigned int8 segto    [4];  
  38. unsigned int8 newsegto [4];  
  39. unsigned int8 passo = 0;  
  40.   
  41. // Segmentos a serem acesos para cada dígito  
  42. const unsigned int8 digitos[10] =  
  43. {  
  44.  0b01111110, 0b00001100, 0b10110110, 0b10011110, 0b11001100,  
  45.  0b11011010, 0b11111010, 0b00001110, 0b11111110, 0b11011110  
  46. };  
  47.   
  48. // Rotinas locais  
  49. static void AtlDisp   (void);  
  50. static void VarreDisp (void);  
  51.   
  52.   
  53. /*********************************************************************/  
  54.   
  55. // P R O G R A M A   P R I N C I P A L  
  56.   
  57. void main()  
  58. {  
  59.   unsigned int16 aux;  
  60.    
  61.   // Iniciações do hardware  
  62.   set_tris_a (0xEB);   // A4 e A2 output  
  63.   #use fast_io(A)  
  64.   set_tris_c (0xC0);   // C0 a C5 output  
  65.   #use fast_io(C)  
  66.    
  67.   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_2);  
  68.   setup_timer_1(T1_DISABLED);  
  69.   setup_comparator(NC_NC);  
  70.   setup_vref(FALSE);  
  71.   setup_adc(ADC_OFF);  
  72.   
  73.   output_low (LED);  
  74.   output_low (SEL_1);  
  75.   output_low (SEL_2);  
  76.   output_low (SEL_3);  
  77.   output_low (SEL_4);  
  78.   
  79.   enable_interrupts(INT_RA5);  
  80.   enable_interrupts(INT_RTCC);  
  81.   enable_interrupts(GLOBAL);  
  82.   
  83.   
  84.   for (;;)  
  85.   {  
  86.      if (passo == 1)  
  87.      {  
  88.         if (cntb >= TIMEOUT)  
  89.            aux = 0; // considerar parado  
  90.         else if (cntb > 5)  
  91.            aux = 58594/cntb;  
  92.         else  
  93.            aux = 9999;  
  94.          
  95.         newsegto[0] = digitos [aux / (unsigned int16) 1000];  
  96.         newsegto[1] = digitos [(aux/100) % 10];  
  97.         newsegto[2] = digitos [(aux/10) % 10];  
  98.         newsegto[3] = digitos [aux % 10];  
  99.         passo = 2;  
  100.      }  
  101.   }  
  102. }  
  103.   
  104. /*********************************************************************/  
  105.   
  106. // S E N S O R  
  107.   
  108. //////////////////////////////////////////////  
  109. // Interrupcao de mudança do sinal  
  110. // Ocorre qdo sensor detecta imã  
  111. //////////////////////////////////////////////  
  112. #int_ra  
  113. void Sensor_ISR ()  
  114. {  
  115.   output_high (LED);  
  116.   input_a();  
  117.   clear_interrupt (INT_RA5);  
  118.   cntLed = TEMPO_LED;  
  119.   if (passo == 0)  
  120.   {  
  121.      cntb = cnta;  
  122.      cnta = 0;  
  123.      passo = 1;  
  124.   }  
  125.   else if (passo == 3)  
  126.   {  
  127.      cnta = 0;  
  128.      passo = 0;  
  129.   }  
  130. }  
  131.   
  132. /*********************************************************************/  
  133.   
  134. // T E M P O  
  135.   
  136. //////////////////////////////////////////////  
  137. // Interrupcao de tempo real  
  138. // Ocorre a cada 512 uSeg  
  139. //////////////////////////////////////////////  
  140. #int_RTCC  
  141. void RTCC_isr()  
  142. {  
  143.   static unsigned int8 cont_disp = TAXA_DISP;  
  144.   
  145.   // Tratamento do LED  
  146.   if (cntLed)  
  147.   {  
  148.      if (--cntLed == 0)  
  149.         output_low (LED);  
  150.   }  
  151.    
  152.   // Atualização do Display  
  153.   if (passo == 2)  
  154.   {  
  155.      AtlDisp ();  
  156.      passo = 3;  
  157.   }  
  158.   
  159.   if (--cont_disp == 0)  
  160.   {  
  161.      VarreDisp ();  
  162.      cont_disp = TAXA_DISP;  
  163.   }  
  164.   
  165.   // Medida do tempo  
  166.   if ((passo == 0) || (passo == 3))  
  167.   {  
  168.      if (cnta < TIMEOUT)  
  169.         cnta++;  
  170.      else  
  171.      {  
  172.         // não detectou  
  173.         cntb = cnta;  
  174.         cnta = 0;  
  175.         passo = 1;  
  176.      }  
  177.   }  
  178. }  
  179.   
  180.   
  181. /*********************************************************************/  
  182.   
  183. // D I S P L A Y  
  184.   
  185. static void AtlDisp (void)  
  186. {  
  187.   segto[0] = newsegto[0];  
  188.   segto[1] = newsegto[1];  
  189.   segto[2] = newsegto[2];  
  190.   segto[3] = newsegto[3];  
  191. }    
  192.   
  193. static void VarreDisp (void)  
  194. {  
  195.   static unsigned int8 disp = 0;  
  196.   unsigned int8 i, mask;  
  197.   
  198.   // coloca os valores dos segmentos no registrador  
  199.   for (i = 0, mask = 0x80; i < 8; i++)  
  200.   {  
  201.      if (segto[disp] & mask)  
  202.         output_low (SHIFT_DS);  
  203.      else  
  204.         output_high (SHIFT_DS);  
  205.      delay_us (DLY_SR);  
  206.      output_high (SHIFT_SH);  
  207.      delay_us (DLY_SR);  
  208.      output_low (SHIFT_SH);  
  209.      delay_us (DLY_SR);  
  210.      mask = mask >> 1;  
  211.   }  
  212.   
  213.   // seleciona o display atual  
  214.   if (disp == 0)  
  215.   {  
  216.      output_low (SEL_4);  
  217.      output_high (SEL_1);  
  218.   }  
  219.   else if (disp == 1)  
  220.   {  
  221.      output_low (SEL_1);  
  222.      output_high (SEL_2);  
  223.   }  
  224.   else if (disp == 2)  
  225.   {  
  226.      output_low (SEL_2);  
  227.      output_high (SEL_3);  
  228.   }  
  229.   else  
  230.   {  
  231.      output_low (SEL_3);  
  232.      output_high (SEL_4);  
  233.   }  
  234.   
  235.   // acende os segmentos  
  236.   output_high (SHIFT_ST);  
  237.   delay_us (DLY_SR);  
  238.   output_low (SHIFT_ST);  
  239.       
  240.   // passa para o dígito seguinte  
  241.   disp = (disp + 1) & 3;  
  242. }  

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.´.