quinta-feira, agosto 15, 2013

Leitor RFID YET-125K: Construindo uma Fechadura Eletrônica - Parte 4

Fechando este série de posts, vamos ver o software do microcontrolador que implementa a fechadura eletrônica. O software foi desenvolvido usando o avr-gcc toolchain e gravado na placa com o bootloadHID.


Vamos começar pela iniciação do hardware: entradas e saídas digitais, USART (para receber os dados do leitor de RFID) e o timer:
  1. // LEDs  
  2. #define LED_TOVIVO      LED8  
  3. #define LED_LEU         LED7  
  4. #define LED_TRANCA      LED6  
  5. #define LED_CADASTRA    LED2  
  6. #define LED_APAGA       LED3  
  7.   
  8. // Servo  
  9. #define SERVO_PORT      PORTB  
  10. #define SERVO_DDR       DDRB  
  11. #define SERVO           _BV(PB7)  
  12.   
  13. // Teclas  
  14. #define TEC_CADASTRA    TEC1  
  15. #define TEC_APAGA       TEC4  
  16.   
  17.     // Inicia as direçoes dos pinos de conexão dos LEDs  
  18.     LED_DDR |= 0xFF;  
  19.       
  20.     // LEDs Apagados  
  21.     LED_PORT = 0x00;  
  22.   
  23.     // Inicia conexão ao servo  
  24.     SERVO_DDR |= SERVO;  
  25.     SERVO_PORT &= ~SERVO;  
  26.       
  27.     // Liga pullup das teclas  
  28.     TEC_DDR &= ~(TEC1|TEC2|TEC3|TEC4);  
  29.     TEC_PORT |= TEC1|TEC2|TEC3|TEC4;  
  30.   
  31.     // Programa a USART para 9600 8N1  
  32.     UCSRA = _BV(U2X);              // para maior resolução do baud rate  
  33.     UCSRB = _BV(RXEN) | _BV(RXCIE);     // liga recepção, com interrupção  
  34.     UCSRC = _BV(URSEL) | _BV(UCSZ1) | _BV(UCSZ0) ;  // 8bit, 1 stop, sem paridade  
  35.     UBRRL = (CLK_CPU / (8 * 9600UL)) - 1;           // 9600 bps  
  36.   
  37.     // Preparar o timer 1 operar no modo CTC com 16MHz / 8  
  38.     // gerando interrupcao contando até 100 (0,05 ms)  
  39.     OCR1A = 100;  
  40.     TCCR1B = _BV(WGM12) | _BV(CS11);  
  41.     TIMSK |= (1<<OCIE1A);  
Em seguida vamos ler a lista de tags da EEProm. Definimos dois vetores para isto, um na Ram e o outro na EEProm. A indicação EEMEM faz com que o linker coloque eepTags na sessão eeprom.
  1. // Lista de tags conhecidos  
  2. #define NMAX_TAGS   8  
  3. static uint32_t tags[NMAX_TAGS];  
  4.   
  5. // Tags salvos na EEPROM  
  6. uint32_t eepTags[NMAX_TAGS] EEMEM;  
  7.   
  8.   
  9.     int i;  
  10.   
  11.     // Carrega os tags conhecidos da EEPROM  
  12.     for (i = 0; i < NMAX_TAGS; i++)  
  13.         tags[i] = eeprom_read_dword (&eepTags[i]);  
A interrupção da USART é usada para receber o código do tag, usando uma pequena máquina de estados. O código do tag lido é colocado em tagLido.
  1. // Último tag detectado  
  2. volatile uint32_t tagLido;  
  3.   
  4. // Controle do LED de leitura  
  5. volatile uint8_t cntLedLeu = 0;  
  6.   
  7. // Interrupção de recepção da USART  
  8. ISR(USART_RXC_vect)  
  9. {  
  10.     static uint8_t estado = 0;  
  11.     static uint32_t idTag;  
  12.     uint8_t c;  
  13.   
  14.     c = UDR;  
  15.     if (bit_is_clear(UCSRA, FE))  
  16.     {  
  17.         // recebeu caracter sem erro  
  18.         if (estado == 0)  
  19.         {  
  20.             // Aguardando o STX  
  21.             if (c == 0x02)  
  22.             {  
  23.                 estado = 1;  
  24.                 idTag = 0;  
  25.             }  
  26.         }  
  27.         else if (estado == 9)  
  28.         {  
  29.             // Aguardando o ETX  
  30.             if (c == 0x03)  
  31.             {  
  32.                 // Recebeu ID com sucesso  
  33.                 tagLido = idTag;  
  34.                 cntLedLeu = 100;  
  35.                 LED_PORT |= LED_LEU;  
  36.             }  
  37.             estado = 0;  
  38.         }  
  39.         else  
  40.         {  
  41.             // recebeu um dígito, vamos tratar como hexadecimal  
  42.             if ((c >= '0') && (c <= '9'))  
  43.                 idTag = (idTag << 4) | (c & 0xF);  
  44.             else if ((c >= 'A') && (c <= 'F'))  
  45.                 idTag = (idTag << 4) | (c - 'A' + 10);  
  46.             else if ((c >= 'a') && (c <= 'f'))  
  47.                 idTag = (idTag << 4) | (c - 'a' + 10);  
  48.             else  
  49.                 estado = 0xFF;  
  50.             estado++;  
  51.         }  
  52.     }  
  53.     else  
  54.         estado = 0;  
  55. }  
O nosso loop principal também é uma máquina de estados:
  1. // Modo de Operação  
  2. typedef enum { MODO_NORMAL, MODO_CADASTRA, MODO_APAGA1, MODO_APAGA2 } MODOOPER;  
  3. static MODOOPER modo = MODO_NORMAL;  
  4.   
  5.     for (;;)  
  6.     {  
  7.         if (modo == MODO_NORMAL)  
  8.         {  
  9.             if (tagLido != 0)  
  10.             {  
  11.                 // Procura nos conhecidos  
  12.                 for (i = 0; (i < NMAX_TAGS) && (tags[i] != 0xFFFFFFFF); i++)  
  13.                 {  
  14.                     if (tags[i] == tagLido)  
  15.                     {  
  16.                         // achou, move a tranca  
  17.                         if (tranca == ABERTA)  
  18.                         {  
  19.                             Fecha();  
  20.                             tranca = FECHADA;  
  21.                         }  
  22.                         else  
  23.                         {  
  24.                             Abre();  
  25.                             tranca = ABERTA;  
  26.                         }  
  27.                         tagLido = 0;  
  28.                         break;  
  29.                     }  
  30.                 }  
  31.             }  
  32.         }  
  33.         else if (modo == MODO_CADASTRA)  
  34.         {  
  35.             if (tagLido != 0)  
  36.             {  
  37.                 // Procura nos conhecidos  
  38.                 for (i = 0; (i < NMAX_TAGS) && (tags[i] != 0xFFFFFFFF); i++)  
  39.                 {  
  40.                     if (tags[i] == tagLido)  
  41.                     {  
  42.                         // achou, ignora  
  43.                         break;  
  44.                     }  
  45.                 }  
  46.                 if ((i < NMAX_TAGS) && (tags[i] == 0xFFFFFFFF))  
  47.                 {  
  48.                     // Não encontrou e tem espaço, coloca na lista  
  49.                     tags[i] = tagLido;  
  50.                     eeprom_update_dword (&eepTags[i], tagLido);  
  51.                 }  
  52.                 tagLido = 0;  
  53.             }  
  54.         }          
  55.         else if (modo == MODO_APAGA2)  
  56.         {  
  57.             // Apaga a lista de Tags  
  58.             for (i = 0; i < NMAX_TAGS; i++)  
  59.             {  
  60.                 tags[i] = 0xFFFFFFFF;  
  61.                 eeprom_update_dword (&eepTags[i], 0xFFFFFFFF);  
  62.             }  
  63.             modo = MODO_NORMAL;  
  64.         }  
  65.     }  
A abertura e fechamento da fechadura é feito enviando pulsos ao servo. A largura destes pulsos é que define a posição do eixo do servo. Para garantir o posicionamento correto são enviados vários pulsos iguais.
  1. // Estado da Fechadura  
  2. typedef enum { ABERTA, FECHADA } FECHADURA;  
  3. static FECHADURA tranca = FECHADA;  
  4.   
  5. // Controle do servo  
  6. volatile uint8_t nCiclos = 0;  
  7. volatile uint16_t posServo = 0;  
  8.   
  9. // Abre a fechadura  
  10. static void Abre(void)  
  11. {  
  12.     posServo = 10;  // 0,5 ms  
  13.     nCiclos = 50;  
  14.     LED_PORT &= ~LED_TRANCA;  
  15. }  
  16.   
  17. // Fecha a fechadura  
  18. static void Fecha(void)  
  19. {  
  20.     posServo = 30;  // 1,5 ms  
  21.     nCiclos = 50;  
  22.     LED_PORT |= LED_TRANCA;  
  23. }  
A interrupção do timer é usada para testar as teclas e gerar todas as temporizações, inclusive os pulsos de controle do servo:
  1. // Interrupção do Timer1  
  2. // Ocorre a cada 0,05 ms  
  3. ISR(TIMER1_COMPA_vect)   
  4. {  
  5.     static uint16_t cntCiclo = 0;       // número de pulsos para o servo  
  6.     static uint8_t cntServo = 0;        // largura do pulso  
  7.     static uint8_t cnt10ms = 200;       // 200*0,05ms = 10ms  
  8.       
  9.     // Tempos a seguir usam a base de 10ms  
  10.     static uint16_t cntCadastra = 500;  // 5 seg  
  11.     static uint16_t cntApaga = 1000;    // 10 seg  
  12.     static uint8_t cntLed = 20;         // 0,2 seg  
  13.     static uint8_t cntToVivo = 50;      // 0,5 seg  
  14.       
  15.     if (nCiclos != 0)  
  16.     {  
  17.         if (cntCiclo == 0)  
  18.         {  
  19.             // Fim do ciclo  
  20.             if (--nCiclos != 0)  
  21.             {  
  22.                 // Novo ciclo  
  23.                 SERVO_PORT |= SERVO;  
  24.                 cntServo = posServo;  
  25.                 cntCiclo = 400; // 20 ms  
  26.             }  
  27.         }  
  28.         if (nCiclos != 0)  
  29.         {  
  30.             if ((cntServo != 0) && (--cntServo == 0))  
  31.                 SERVO_PORT &= ~SERVO;  
  32.             cntCiclo--;  
  33.         }  
  34.     }  
  35.       
  36.     if (--cnt10ms == 0)  
  37.     {  
  38.         cnt10ms = 200;  
  39.         if (cntLedLeu != 0)  
  40.         {  
  41.             if (--cntLedLeu == 0)  
  42.                 LED_PORT &= ~LED_LEU;  
  43.         }  
  44.         if (--cntToVivo == 0)  
  45.         {  
  46.             LED_PORT ^= LED_TOVIVO;  
  47.             cntToVivo = 50;  
  48.         }  
  49.         if (modo == MODO_NORMAL)  
  50.         {  
  51.             if ((TEC_PIN & TEC_CADASTRA) == 0)  
  52.             {  
  53.                 if (--cntCadastra == 0)  
  54.                 {  
  55.                     LED_PORT |= LED_CADASTRA;  
  56.                     modo = MODO_CADASTRA;  
  57.                 }  
  58.             }  
  59.             else  
  60.                 cntCadastra = 500;  
  61.             if ((TEC_PIN & TEC_APAGA) == 0)  
  62.             {  
  63.                 if (--cntApaga == 0)  
  64.                 {  
  65.                     modo = MODO_APAGA1;  
  66.                     cntApaga = 1000;  
  67.                     cntLed = 20;  
  68.                 }  
  69.             }  
  70.             else  
  71.                 cntApaga = 1000;  
  72.         }  
  73.         else if (modo == MODO_CADASTRA)  
  74.         {  
  75.             if ((TEC_PIN & TEC_CADASTRA) == 0)  
  76.             {  
  77.                 if (--cntCadastra == 0)  
  78.                 {  
  79.                     LED_PORT &= ~LED_CADASTRA;  
  80.                     modo = MODO_NORMAL;  
  81.                 }  
  82.             }  
  83.             else  
  84.                 cntCadastra = 500;  
  85.         }  
  86.         else if (modo == MODO_APAGA1)  
  87.         {  
  88.             if (--cntLed == 0)  
  89.             {  
  90.                 LED_PORT ^= LED_APAGA;  
  91.                 cntLed = 20;  
  92.             }  
  93.             if ((TEC_PIN & TEC_APAGA) == 0)  
  94.             {  
  95.                 if (--cntApaga == 0)  
  96.                 {  
  97.                     modo = MODO_APAGA2;  
  98.                     cntApaga = 1000;  
  99.                     LED_PORT &= ~LED_APAGA;  
  100.                 }  
  101.             }  
  102.             else  
  103.             {  
  104.                 modo = MODO_NORMAL;  
  105.                 cntApaga = 1000;  
  106.                 LED_PORT &= ~LED_APAGA;  
  107.             }  
  108.         }  
  109.     }  
  110. }  
Como de costume, o projeto completo está nos arquivos do blog (link lá no alto à direita), neste caso no arquivo bau.zip.

Nenhum comentário: