quarta-feira, dezembro 25, 2013

Um Display de Mensagens com o JY-MCU 3208

Retomando as experiências com o display JY-MCU 3208 (lembram dele?), apresento aqui um firmware que permite selecionar entre várias mensagens gravadas na EEProm do ATmega.


O Projeto
A origem deste projeto foi o desejo do meu amigo Tony colocar um display na bicicleta e poder selecionar a mensagem a apresentar através de teclas no guidão. Este firmware é apenas a parte inicial deste projeto, à medida em que ele evoluir eu vou documentado no blog.

Nós já vimos como apresentar uma mensagem no display, rolando o texto da direita para a esquerda. Para este novo projeto vamos precisar guardar várias mensagens, e a EEProm do ATmega é o lugar óbvio. Como colocar e editar estas mensagens? Em princípio daria para fazer pelo gravador, mas é mais versátil fazer isto pela serial do ATmega, que está disponível no conector de programação.

Usar a serial simplifica uma outra parte do projeto: como selecionar a mensagem a apresentar. Poderíamos, é claro, ligar diretamente um botão em uma porta digital do ATmega para cada mensagem, mas poucas entradas digitais estão disponíveis no conector de programação e ficaria um monte de fios. Com a serial, podemos ligar os botões a um outro microcontrolador no guidão (um ATtinyx5 ou x4 dependendo do número de botões) e fazer a conexão entre os dois por três fios (Vcc, Gnd e comunicação, supondo que baste comunicar em um sentido).

O firmware (que você pode baixar dos arquivos do blog como JYMCU3208_Banner2.zip) ilustra algumas técnicas interessantes de programação AVR.

Acesso à EEprom

A memória EEprom requer alguns cuidados no acesso. Felizmente, a avr-libc possui rotinas prontas para isto. O primeiro passo é marcar com EEMEN as estruturas que estarão na EEprom:
  1. // Mapeamento das mensagens na EEProm  
  2. // O ATmega8 tem 512 bytes de EEProm  
  3. #define T_MSG   40  // tamanho máximo de cada mensagem  
  4. #define N_MSG   8   // quantidade de mensagens  
  5. uint8_t eepMsgs[N_MSG][T_MSG+1] EEMEM;  
Esta indicação é usada pelo linker, que mapeia estas estruturas no espaço de endereçamento da EEprom (que é separado da Ram e Flash).

Para ler e escrever usamos as rotinas eeprom_read_byte() e eeprom_update_byte(). Existe também a rotina eeprom_write_byte; a update verifica primeiro o que já está na eeprom e só grava se for diferente, evitando gastar a eeprom desnecessariamente (pois o número de gravações é limitado).
  1. // Carrega da EEProm a mensagem atual  
  2. static void load_msg (uint8_t *pMsg)  
  3. {  
  4.     int i;  
  5.     for (i = 0; i < T_MSG; i++)  
  6.         pMsg[i] = eeprom_read_byte (&eepMsgs[msg_atual][i]);  
  7.     pMsg[i] = 0;    // garante fim no final  
  8. }  
  9.   
  10.     // Salva a mensagem editada  
  11.     for (j = 0; j <= i; j++)  
  12.         eeprom_update_byte (&eepMsgs[msgEd][j], msg[2+j]);  
Comunicação Serial

O ATmega8 possui uma USART para comunicação serial síncrona e assíncrona. A programação é um pouco chatinha, mas basta suar um pouco sobre o manual para obter uma sequência de iniciação apropriada:
  1. // Inicia a comunicação serial  
  2. static void init_serial ()  
  3. {  
  4.     // Inicia a fila  
  5.     poe = tira = 0;  
  6.       
  7.     // Programa a USART para 9600 8N1  
  8.     UCSRA = _BV(U2X);              // para maior resolução do baud rate  
  9.     UCSRB = _BV(RXEN) | _BV(TXEN) | _BV(RXCIE);   // liga recepção e transmissão  
  10.                                                   //   com interrupção de Rx  
  11.     UCSRC = _BV(URSEL) | _BV(UCSZ1) | _BV(UCSZ0) ;// 8bit, 1 stop, sem paridade  
  12.     UCSRC = 0;                                    // UBRRH = 0  
  13.     UBRRL = (CLK_CPU / (8 * 9600UL)) - 1;         // 9600 bps  
  14. }  
Uma coisa estranha é o registrador de controle UCSRC e a parte alta do registrador de programação do clock UBRR possuem o mesmo endereço. A seleção entre os dois é através de um dos bits escritos (URSEL).

Optei por trabalhar com interrupção na recepção, os caracteres recebidos são guardados em uma fila circular para serem processados depois pelo programa principal:
  1. // Fila de recepção da serial  
  2. #define T_FILA 16  
  3. static volatile uint8_t filaRx[T_FILA];  
  4. static volatile uint8_t poe, tira;  
  5.   
  6. // Pega o próximo caracter da fila de recepção da serial  
  7. // Retorna -1 se fila vazia  
  8. static int le_serial ()  
  9. {  
  10.     int c;  
  11.     cli ();  
  12.     if (poe == tira)  
  13.         c = -1;   // fila vazia  
  14.     else  
  15.     {  
  16.         c = filaRx[tira];  
  17.         if (++tira == T_FILA)  
  18.             tira = 0;  
  19.     }  
  20.     sei ();  
  21.     return c;  
  22. }  
  23.   
  24. // Interrupção de recepção da USART  
  25. ISR(USART_RXC_vect)  
  26. {  
  27.     uint8_t prox;  
  28.       
  29.     filaRx[poe] = UDR;      // pega o caracter  
  30.     prox = poe + 1;         // avança o ponteiro de entrada  
  31.     if (prox == T_FILA)  
  32.         prox = 0;  
  33.     if (prox != tira)       // não atualizar se fila cheia  
  34.         poe = prox;  
  35. }  

Uso do Firmware

O firmware é razoavelmente fácil de usar.

Para gravar as mensagens, conecte a serial a um PC (usando um conversor TTL USB e um programa de comunicação  como o PuTTY); a velocidade é 9600bps e o formato 8N1. Para alterar a mensagem "n" (onde n varia de 1 a 8) digite * n texto Enter. Se você digitar algo errado, use o backspace para apagar. ESC cancela a edição.

Para apresentar a mensagem n, basta digitar n. Você pode navegar também entre as mensagens usando as teclas 1 e 2 do display,

A pegadinha é que teclas e serial só são verificados ao final da apresentação da mensagem atual.

Nenhum comentário: