quinta-feira, abril 21, 2011

Pequenos Imãs e Micro Controladores - Parte 7

Vamos completar o nosso contador de giros vendo o software para o circuito apresentado no post anterior.

O software começa com as definições necessários para o compilador utilizado (PIC C) e as definições dos pinos aos quais estão conectados os sinais que nos interessam:
  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    10    // delay p/ shift register  
  8. #define TAXA_DISP 3     // taxa de varredura dos displays  
  9. #define TAXA_ATL  100   // taxa de atualização dos valores nos displays  
  10.   
  11. #define TEMPO_LED 600   // tempo que o LED fica aceso  
  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  
Vamos manter o LED usado anteriormente. Para isto precisamos do contador que temporiza o seu apagamento. Vamos acrescentar um variável para contar as detecções de imã:
  1. // Controle do LED  
  2. volatile int16 cntLed;  
  3.   
  4. // Contador de passagens do imã  
  5. volatile unsigned int16 cntIma = 0;  
Um vetor de quatro posições (segto) armazena a situação dos segmentos nos quatro dígitos do display. Na interrupção de timer usaremos este vetor para refrescar continuamente o display. Na hora de apresentar um novo valor, os segmentos serão preparados inicialmente em um segundo vetor (newsegto).

Uma variável passo controlará a atualização do display:
  • 0 indica que o display deve ser refrescado com o valor em segto
  • 1 indica que um novo valor deve ser montado em newsegto
  • 2 indica que newsegto tem um novo valor que deve ser copiado para segto
O vetor 'digitos' determina quais segmentos devem ser acesos para apresentar os dígitos de 0 a 9.
  1. // Controle dos segmentos do display  
  2. // Um byte para cada segmento  
  3. // Bit 7 = segto G, Bit 6 = segto F, .. Bit 1 = segto A  
  4. unsigned int8 segto    [4];  
  5. unsigned int8 newsegto [4];  
  6. unsigned int8 passo = 0;  
  7.   
  8. // Segmentos a serem acesos para cada dígito  
  9. const unsigned int8 digitos[10] =  
  10. {  
  11. 0b01111110, 0b00001100, 0b10110110, 0b10011110, 0b11001100,  
  12. 0b11011010, 0b11111010, 0b00001110, 0b11111110, 0b11011110  
  13. };  
No programa inicial temos a iniciação das entradas e saídas e do timer, seguido de um laço infinito onde um novo conteúdo para o display é calculado periodicamente.
  1. // P R O G R A M A   P R I N C I P A L  
  2.   
  3. void main()  
  4. {  
  5.   unsigned int16 aux;  
  6.    
  7.   // Iniciações do hardware  
  8.   set_tris_a (0xEB);   // A4 e A2 output  
  9.   #use fast_io(A)  
  10.   set_tris_c (0xC0);   // C0 a C5 output  
  11.   #use fast_io(C)  
  12.    
  13.   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);  
  14.   setup_timer_1(T1_DISABLED);  
  15.   setup_comparator(NC_NC);  
  16.   setup_vref(FALSE);  
  17.   setup_adc(ADC_OFF);  
  18.   
  19.   output_low (LED);  
  20.   output_low (SEL_1);  
  21.   output_low (SEL_2);  
  22.   output_low (SEL_3);  
  23.   output_low (SEL_4);  
  24.   
  25.   enable_interrupts(INT_RA5);  
  26.   enable_interrupts(INT_RTCC);  
  27.   enable_interrupts(GLOBAL);  
  28.   
  29.   
  30.   for (;;)  
  31.   {  
  32.      if (passo == 1)  
  33.      {  
  34.         disable_interrupts(GLOBAL);  
  35.         aux = cntIma;  
  36.         enable_interrupts(GLOBAL);  
  37.   
  38.         newsegto[0] = digitos [aux / (unsigned int16) 1000];  
  39.         newsegto[1] = digitos [(aux/100) % 10];  
  40.         newsegto[2] = digitos [(aux/10) % 10];  
  41.         newsegto[3] = digitos [aux % 10];  
  42.         passo = 2;  
  43.      }  
  44.   }  
  45. }  
O tratamento do sensor é igual ao que já vimos antes, acrescido do incremento do contador de detecções:
  1. // S E N S O R  
  2.   
  3. //////////////////////////////////////////////  
  4. // Interrupcao de mudança do sinal  
  5. // Ocorre qdo sensor detecta imã  
  6. //////////////////////////////////////////////  
  7. #int_ra  
  8. void Sensor_ISR ()  
  9. {  
  10.   output_high (LED);  
  11.   input_a();  
  12.   clear_interrupt (INT_RA5);  
  13.   cntLed = TEMPO_LED;  
  14.   cntIma++;  
  15.   if (cntIma > 9999)  
  16.      cntIma = 0;  
  17. }  
Na interrupção de tempo real temos as temporizações para o LED e o display:
  1. // T E M P O  
  2.   
  3. //////////////////////////////////////////////  
  4. // Interrupcao de tempo real  
  5. // Ocorre a cada 256 uSeg  
  6. //////////////////////////////////////////////  
  7. #int_RTCC  
  8. void RTCC_isr()  
  9. {  
  10.   static unsigned int8 cont_disp = TAXA_DISP;  
  11.   static unsigned int8 cont_atl  = TAXA_ATL;  
  12.   
  13.   // Tratamento do LED  
  14.   if (cntLed)  
  15.   {  
  16.      if (--cntLed == 0)  
  17.         output_low (LED);  
  18.   }  
  19.    
  20.   // Atualização do Display  
  21.   if (--cont_atl == 0)  
  22.   {  
  23.      passo = 1;  
  24.      cont_atl = TAXA_ATL;  
  25.   }  
  26.   if (passo == 2)  
  27.      AtlDisp ();  
  28.   
  29.   if (--cont_disp == 0)  
  30.   {  
  31.      VarreDisp ();  
  32.      cont_disp = TAXA_DISP;  
  33.   }  
  34.    
  35. }  
Por último, temos as rotinas de atualização do display:
  1. // D I S P L A Y  
  2.   
  3. static void AtlDisp (void)  
  4. {  
  5.   segto[0] = newsegto[0];  
  6.   segto[1] = newsegto[1];  
  7.   segto[2] = newsegto[2];  
  8.   segto[3] = newsegto[3];  
  9.   passo = 0;  
  10. }    
  11.   
  12. static void VarreDisp (void)  
  13. {  
  14.   static unsigned int8 disp = 0;  
  15.   unsigned int8 i, mask;  
  16.   
  17.   // coloca os valores dos segmentos no registrador  
  18.   for (i = 0, mask = 0x80; i < 8; i++)  
  19.   {  
  20.      if (segto[disp] & mask)  
  21.         output_low (SHIFT_DS);  
  22.      else  
  23.         output_high (SHIFT_DS);  
  24.      delay_us (DLY_SR);  
  25.      output_high (SHIFT_SH);  
  26.      delay_us (DLY_SR);  
  27.      output_low (SHIFT_SH);  
  28.      delay_us (DLY_SR);  
  29.      mask = mask >> 1;  
  30.   }  
  31.   
  32.   // seleciona o display atual  
  33.   if (disp == 0)  
  34.   {  
  35.      output_low (SEL_4);  
  36.      output_high (SEL_1);  
  37.   }  
  38.   else if (disp == 1)  
  39.   {  
  40.      output_low (SEL_1);  
  41.      output_high (SEL_2);  
  42.   }  
  43.   else if (disp == 2)  
  44.   {  
  45.      output_low (SEL_2);  
  46.      output_high (SEL_3);  
  47.   }  
  48.   else  
  49.   {  
  50.      output_low (SEL_3);  
  51.      output_high (SEL_4);  
  52.   }  
  53.   
  54.   // acende os segmentos  
  55.   output_high (SHIFT_ST);  
  56.   delay_us (DLY_SR);  
  57.   output_low (SHIFT_ST);  
  58.       
  59.   // passa para o dígito seguinte  
  60.   disp = (disp + 1) & 3;  
  61. }  
O vídeo abaixo mostra hardware e software funcionando. Os mais atentos perceberão que estamos contando as detecções de imã e portanto a contagem é incrementada de dois a cada volta.



Vamos fechar esta série no próximo post, adaptando o código para apresentar no display o número de rotações por minuto e usar os outros dois tipos de sensores.

Nenhum comentário: