sexta-feira, outubro 30, 2009

Abóbora Assassina - Parte 3

Neste post vamos ver o firmware da "Abóbora Assassina". Como o hardware, ele é baseado no meu artigo sobre controle de servomotor e foi desenvolvido em C.
O comportamento gerado pelo firmware é o seguinte:
  • Durante um período "longo" (2 minutos no código abaixo) a abóbora está "dormindo" (LEDs apagados e servomotor na posição de boca fechada).
  • Ao "acordar" os LEDs dos olhos são acesos e o servomotor muda de posição para "abrir a boca da abóbora".
  • Completada a abertura, é aceso o LED dentro da boca. A boca permanece aberta por alguns segundos.
  • O servo motor retorna rapidamente para a posição de boca fechada, ao final do fechamento os LEDs são apagados.
O firmware utiliza uma variável para guardar o "estado da abóbora":
  1. enum { DORMINDO, ABRINDO, ABERTA, FECHANDO } estAbobora;  
A interrupção de timer do PIC, que é programada para ocorrer a cada 64 microsegundos, é utilizada para gerar o sinal PWM de controle do servomotor e para decrementar um contador que indica a passagem de um décimo de segundo.
  1. unsigned int16 cntDSeg;  
  2. unsigned int16 cnt_periodo;  
  3. unsigned int8  cnt_duty, duty;  
  4.   
  5. //////////////////////////////////////////////  
  6. // Interrupcao de tempo real  
  7. // Ocorre a cada 64 uSeg  
  8. //////////////////////////////////////////////  
  9. #int_RTCC  
  10. void RTCC_isr()   
  11. {  
  12.    SET_TIMER0 (192);    // conta de 192 a 255  
  13.      
  14.    // Trata Servo  
  15.    if ((estAbobora != DORMINDO) && (estAbobora != ABERTA)  
  16.    {  
  17.       if (--cnt_periodo == 0)  
  18.       {  
  19.          // fim de um ciclo  
  20.          cnt_periodo = TMPUS_PERIODO/TMPUS_OVF;  
  21.          cnt_duty = duty;  
  22.          output_high (MOTOR);  
  23.       }  
  24.       else if (cnt_duty != 0)  
  25.       {  
  26.          cnt_duty--;  
  27.          if (cnt_duty == 0)  
  28.             output_low (MOTOR);  
  29.       }  
  30.    }  
  31.      
  32.    if (cntDSeg != 0)  
  33.       cntDSeg--;  
  34. }  
O programa principal contém a iniciação do hardware e a lógica de mudança de estado a cada décimo de segundo:
  1. #define MOTOR     PIN_A2  
  2. #define LED_OLHO  PIN_A4  
  3. #define LED_BOCA  PIN_A5  
  4.   
  5. #define  TMPUS_OVF     64l      // tempo uSeg entre interrupções do timer  
  6. #define  TMPUS_PERIODO 20000l   // periodo do PWM em uSeg  
  7. #define  TMPUS_DSEG    100000l  
  8.   
  9. #define  TMPDSEG_DORME   1000    
  10. #define  TMPDSEG_ABRINDO    3    
  11. #define  TMPDSEG_FECHANDO   1  
  12. #define  TMPDSEG_ABERTA    30  
  13.   
  14. #define  DUTY_0      400      // tempo para 0 graus  
  15. #define  DUTY_90     1000     // tempo para 90 graus  
  16. #define  DUTY_180    1600     // tempo para 180 graus  
  17.   
  18. #define  DUTY_ABERTA    768  
  19. #define  DUTY_FECHADA   576  
  20.   
  21. unsigned int16 cntEstado;  
  22.   
  23. void main()  
  24. {  
  25.    // configura os periféricos  
  26.    #use fast_io(A)  
  27.    set_tris_A(0xCB);  
  28.    setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);  
  29.    SET_TIMER0 (192);    // conta de 192 a 255  
  30.    setup_timer_1(T1_DISABLED);  
  31.    setup_comparator(NC_NC);  
  32.    setup_vref(FALSE);  
  33.   
  34.    output_high (LED_BOCA);  
  35.    output_high (LED_OLHO);  
  36.    output_low (MOTOR);  
  37.   
  38.    // inicia as variaveis  
  39.    estAbobora = FECHANDO;  
  40.    cntDSeg = TMPUS_DSEG/TMPUS_OVF;  
  41.    cnt_periodo = TMPUS_PERIODO/TMPUS_OVF;  
  42.    cnt_duty = duty = DUTY_FECHADA / TMPUS_OVF;  
  43.    cntEstado = 3;  
  44.      
  45.    enable_interrupts(INT_RTCC);  
  46.    enable_interrupts(GLOBAL);  
  47.   
  48.    for (;;)  
  49.    {  
  50.       // Trata atualização do estado a cada décimo de segundo  
  51.       if (cntDSeg == 0)  
  52.       {  
  53.          disable_interrupts(GLOBAL);  
  54.          cntDSeg = TMPUS_DSEG/TMPUS_OVF;  
  55.          if (--cntEstado == 0)  
  56.          {  
  57.             switch (estAbobora)  
  58.             {  
  59.                case DORMINDO:  
  60.                   estAbobora = ABRINDO;  
  61.                   duty = DUTY_FECHADA / TMPUS_OVF;  
  62.                   output_high (LED_OLHO);  
  63.                   cntEstado = TMPDSEG_ABRINDO;  
  64.                   break;  
  65.                case ABRINDO:  
  66.                   if (duty == (DUTY_ABERTA / TMPUS_OVF))  
  67.                   {  
  68.                      estAbobora = ABERTA;  
  69.                      output_high (LED_BOCA);  
  70.                      cntEstado = TMPDSEG_ABERTA;  
  71.                   }  
  72.                   else  
  73.                   {  
  74.                      duty++;  
  75.                      cntEstado = TMPDSEG_ABRINDO;  
  76.                   }  
  77.                   break;  
  78.                case ABERTA:  
  79.                   estAbobora = FECHANDO;  
  80.                   cntEstado = TMPDSEG_FECHANDO;  
  81.                   break;  
  82.                case FECHANDO:  
  83.                   if (duty == (DUTY_FECHADA / TMPUS_OVF))  
  84.                   {  
  85.                      estAbobora = DORMINDO;  
  86.                      output_low (LED_BOCA);  
  87.                      output_low (LED_OLHO);  
  88.                      output_low (MOTOR);  
  89.                      cntEstado = TMPDSEG_DORME;  
  90.                   }  
  91.                   else  
  92.                   {  
  93.                      duty--;  
  94.                      cntEstado = TMPDSEG_FECHANDO;  
  95.                   }  
  96.                   break;  
  97.             }  
  98.          }  
  99.          enable_interrupts(GLOBAL);  
  100.       }  
  101.    }  
  102. }  
No próximo post da série vamos ver a montagem da minha "abóbora" (que acabou sendo uma esfera de isopor).

Nenhum comentário: