Lab. Processadores: Programação do LPC2478 usando o gcc

| Laboratório de Processadores Programação do LPC2478 usando o arm-elf-gcc Parte 4: Timer e display LCD |

Neste exemplo vamos fazer o programa a04_timer_lcd.tgz para configurar o Kit como um relógio que mostra as horas, minutos e segundos no display LCD e na porta serial e pisca os LEDs de forma cadenciada usando o timer T0.

### Timers

Os microcontroladores tipo LPC24xx tem 4 timers (T0, T1, T2 e T3) de 32 bits, que diferem apenas no endereço de acesso. Estes timers tem recursos poderosos, mas na configuração básica são fáceis de usar. Com eles pode-se medir tempos, gerar interrupções cadenciadas ou contar eventos externos.

Aqui vamos configurar o timer para contar com base no clock externo de 12MHz, incrementando a contagem 1000 vezes por segundo. Isto será usado na função `void espera(int t)` para esperar t milisegundos e no programa principal para fazer piscar o LED1 ficando apagado durante 600ms e aceso durante 400ms. Os registradores usados na configuração do timer são os seguintes:

T0TCR 0xE0004004 | Timer Control Register. Habilita ou Inicializa o Timer bit 0: Habilita o Timer. bit 1: Inicializa o timer com zero.
T0TC 0xE0004008 | T0 Timer Count. Neste registrador está o valor da contagem do timer.
T0PR 0xE000400C | T0 Prescale Register: Configura o divisor da frequência de incremento do timer. O registrador T0PC é incrementado até ficar igual ao T0PR. Neste momento a contagem T0TC é incrementada e T0PC é zerado. Portanto configura-se este registrador com o divisor de freqência menos um.

Assim a configuração do timer para incrementar T0TC 1000 vezes por segundo é:

    T0TCR = 0;		/* Desabilita T0 */
    T0PR = PCLK/1000 - 1; /* Incrementa o contador T0TC 1000 vezes por segundo */
    T0TCR = 2;	/* Inicializa T0 */
    T0TCR = 1;	/* Habilita T0	*/

Nesta configuração a palavra PCLK é a frequência em Hertz do clock usado no timer. Inicialmente o LPC2478 usa um clock interno de 4MHz com um divisor por 4, resultando que PCLK=1000000. Fazendo a configuração feita na função `int PLL_Init(void)`, temos que PCLK=12000000. Esta função tambem tem outro efito: Antes de usar `int PLL_Init(void)`, o acesso aos pinos GPIO das portas P0 e P1 é feito da forma tradicional, com IOPIN1. Depois deve-se usar as portas com acessos tipo FIO1PIN.

Existem vários outros registradores relacionados com os timers que não vou mencionar porque não foram usados neste programa.

A rotina de espera usa o valor da contagem do timer `T0TC` para esperar durante um tempo determinado de milisegundos:

    /* Espera t milisegundos */
    void espera(unsigned int t)
    {
    unsigned int tf;
    tf = T0TC + t;		/* tf = Valor futuro do T0TC */
    while(tf != T0TC);	/* espera ate que tf==T0TC */
    }

### Display LCD

O Kit de Lab. Processadores tem um display LCD de 2 linhas por 16 colunas conectado aos pinos GPIO do LPC2478 de acordo com a tabela a seguir:

Nome do sinal | Pino do LPC2478 | Máscara | Descrição
--- | --- | --- | ---
LCD_E | P1.31 | 0x80000000 | Habilitação do Latch de entrada do LCD. Normalmente `LCD_E = 0` Admite dado quando `LCD_E = 1`
LCD_RS | P1.30 | 0x40000000 | Seleciona Dado/Comando. `LCD_RS = 0` Comando. `LCD_RS = 1` Escreve caractere.
LCD_RW | P4.23 | 0x00800000 | Seleciona Leitura/Escrita. Normalmente usa-se o LCD em modo de escrita. `LCD_RW = 0` Escreve. `LCD_RW = 1` Le.
LCD_D | P2.0 a P2.7 | 0x000000ff | Barramento de dados do LCD (8 bits).

Para facilitar o entendimento do programa vamos dar nomes para as máscaras de bits:

    #define LCD_E  0x80000000
    #define LCD_RS 0x40000000
    #define LCD_RW 0x00800000
    #define LCD_D  0x000000ff

O display é controlado escrevendo comandos com RS=0 ou escrevendo caracteres ASCII com RS=1. A tabela a seguir apresenta alguns comandos do LCD.

Comando | Explicação
--- | ---
0x38 | Configura o display para 2 linhas com interface de 8 bits
0x01 | Limpa o display
0x0e | Liga o cursor como um traço em baixo _
0x0f | Liga o cursor como um bloco sólido
0x0c | Esconde o cursor
0x80+x | Posiciona o cursor na primeira linha, coluna x
0xc0+x | Posiciona o cursor na segunda linha, coluna x
0x10 | Move o cursor 1 charactere para a esquerda
0x14 | Move o cursor 1 charactere para a direita
0x18 | Scroll horizontal para a esquerda
0x1E | Scroll horizontal para a direita

Conforme foi visto no capítulo 2, podemos controlar os bits do display usando os registradores:

FIO3DIR 0x3fffc060 | Especifica a direção dos bits da GPIO P3: 0: Entrada 1: Saida O processador inicia com todos os bits configurados como entrada.
FIO3MASK 0x3fffc070 | Habilita a escrita nos pinos da P3: Escrevendo uma máscara de bits em FIO3MASK pode-se inibir a escrita nos bits onde a máscara é 1 (um). O processador inicia com FIO3MASK=0, deixando todos os bits liberados para escrita.
FIO3PIN 0x3fffc074 | Na escrita escreve um valor nos bits de GPIO Na leitura le o estado dos bits.
FIO3SET 0x3fffc078 | Liga bits da P3. Os bits onde se escreve 1 são ligados e os bits onde se escreve 0 ficam inalterados.
FIO3CLR 0x3fffc07c | Desiga bits da P3. Os bits onde se escreve 1 são desligados e os bits onde se escreve 0 ficam inalterados.

Definem-se constantes para as máscaras dos bits de controle do LCD:

    #define LCD_E	(1<<31)
    #define LCD_RS	(1<<30)
    #define LCD_RW	(1<<23)

O procedimento para escrever um comando no LCD fica assim:

    /* Escreve um comando para o LCD */
    void LCDcomando(int c)
    {
    FIO2PIN0 = c;		/* Escreve dados do LCD nos bits 0 a 7 do FIO3	*/
    FIO1CLR = LCD_RS;	/* Zera o sinal LCD_RS para indicar comando	*/
    FIO1SET = LCD_E;	/* Liga a habilitacao do latch de dados do LCD	*/
    FIO1CLR = LCD_E;	/* E=0 */
    espera(20);		/* espera 20ms */
    }

O procedimento para escrever caracteres no display é similar. A ´nica diferença é que o sinal RS deve ser ligado.

    /* Escreve um caractere no LCD */
    void LCDputchar(int c)
    {
    FIO2PIN0 = c;		/* Escreve dados do LCD nos bits 0 a 7 do FIO3	*/
    FIO1SET = LCD_RS;	/* Liga o sinal LCD_RS para escrever caractere	*/
    FIO1SET = LCD_E;	/* Liga a habilitacao do latch de dados do LCD	*/
    FIO1CLR = LCD_E;	/* E=0 */
    espera(8);		/* espera 8ms */
    }

A inicialização do display é feita configurando as portas de GPIO e escrevendo os comandos de configuração do LCD:

    /* Inicializa o display LCD */
    void LCDinit(void)
    {
    FIO2DIR |= 0xff;	/* Dados do LCD como saidas */
    FIO1DIR |= (LCD_RS | LCD_E);	/* LCD_RS e LCD_E como saídas */
    FIO1CLR = LCD_E;	/* LCD_E deve ficar normalmente em 0 */
    FIO4DIR |= LCD_RW;	/* LCD_RW deve ficar em 0 para poder escrever no LCD */
    FIO4CLR = LCD_RW;
    espera(40);
    LCDcomando(0x38);	/* Configura LCD */
    LCDcomando(1);		/* Limpa */
    LCDcomando(0x0c);	/* Cursor oculto */
    }

Bibliografia

- LCD - Info Dados sobre o display LCD. Inclui uma lista completa dos comandos do LCD.