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:
// Mapeamento das mensagens na EEProm
// O ATmega8 tem 512 bytes de EEProm
#define T_MSG   40  // tamanho máximo de cada mensagem
#define N_MSG   8   // quantidade de mensagens
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).
// Carrega da EEProm a mensagem atual
static void load_msg (uint8_t *pMsg)
{
    int i;
    for (i = 0; i < T_MSG; i++)
        pMsg[i] = eeprom_read_byte (&eepMsgs[msg_atual][i]);
    pMsg[i] = 0;    // garante fim no final
}

    // Salva a mensagem editada
    for (j = 0; j <= i; j++)
        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:
// Inicia a comunicação serial
static void init_serial ()
{
    // Inicia a fila
    poe = tira = 0;
    
    // Programa a USART para 9600 8N1
    UCSRA = _BV(U2X);              // para maior resolução do baud rate
    UCSRB = _BV(RXEN) | _BV(TXEN) | _BV(RXCIE);   // liga recepção e transmissão
                                                  //   com interrupção de Rx
    UCSRC = _BV(URSEL) | _BV(UCSZ1) | _BV(UCSZ0) ;// 8bit, 1 stop, sem paridade
    UCSRC = 0;                                    // UBRRH = 0
    UBRRL = (CLK_CPU / (8 * 9600UL)) - 1;         // 9600 bps
}
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:
// Fila de recepção da serial
#define T_FILA 16
static volatile uint8_t filaRx[T_FILA];
static volatile uint8_t poe, tira;

// Pega o próximo caracter da fila de recepção da serial
// Retorna -1 se fila vazia
static int le_serial ()
{
    int c;
    cli ();
    if (poe == tira)
        c = -1;   // fila vazia
    else
    {
        c = filaRx[tira];
        if (++tira == T_FILA)
            tira = 0;
    }
    sei ();
    return c;
}

// Interrupção de recepção da USART
ISR(USART_RXC_vect)
{
    uint8_t prox;
    
    filaRx[poe] = UDR;      // pega o caracter
    prox = poe + 1;         // avança o ponteiro de entrada
    if (prox == T_FILA)
        prox = 0;
    if (prox != tira)       // não atualizar se fila cheia
        poe = prox;
}

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: