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

| Laboratório de Processadores Programação do LPC2478 usando o arm-elf-gcc Parte 3: Configuração da porta serial |

Agora vamos analisar o programa `a03_uart/`, que configura a porta serial, escreve uma mensagem e faz eco da porta serial.

No início do módulo `main.c` temos a função `U0init()` que habilita e configura a porta serial. Então pode-se usar as funções `void U0putchar(int c), int U0getchar()` e `void U0puts` para comunicar-se com um terminal através da porta serial.

### Instruções para compilar e usar

Iniciar expandindo o arquivo `a03_uart.tgz` e abrindo um shell e entrando no diretório `a03_uart`. Prepara-se o microprocessador para programar a Flash, conectando o cabo serial e colocando em modo de programação. Então pode-se compilar o programa e gravar na Flash com o comando

    make isp

Para testar o programa recomenda-se usar um teminal serial, como o ltser.exe. O programa escreve uma mensagem e fica pronto para fazer eco do que é digitado.

Tendo o programa `mon24` instalado na FLASH e sendo executado, pode-se testar este programa rodando na RAM. Para isto usa-se o comando

    make tser

Este comando compila o programa e envia-o para a RAM usando o terminal `ltser`. Para executar o programa usa-se o comando "P" do monitor `mon24`.

### Configuração da porta serial (UART)

O LPC2478 tem quatro portas seriais (UART0, UART1, UART2 e UART3), que podem ser configuradas de modo semelhante à UART tipo 16550. Inicialmente devemos selecionar as funções TxD0 e RxD0 nos pinos do microprocessador. Os registradores PINSEL0 até PINSEL9 servem para selecionar uma das 4 possíveis funções de cada pino do microcontrolador. A cada pino correspondem 2 bits dos registradores PINSEL. Os sinais TXD0 e RXD0 são selecionados colocando o valor 01 nos bits 5:4 e 6:7 do PINSEL0. Para evitar o efeito colateral de alterar a configuração de outros pinos, recomenda-se usar uma operação ou com o valor anterior do PINSEL0:

    PINSEL0 |= 0x50;	/* Habilita TxD0 e RxD0 */

Aa portas seriais podem usar buffers tipo FIFO de 16 níveis para evitar perdas de dados. Para habilita-las usa-se o comando:

    U0FCR   = 0x7;		/* Habilita as FIFOs e reset */

Especificamente os bits do registrador U0FCR são os seguintes:

U0FCR 0xE000C008 | FIFO Control Register: Configura a flia tipo FIFO da UART. bit 7 e 6: Estes bits controlam o nivel de enchimento da FIFO que causa o disparo de uma interrupção: 00 Dispara com 1 caractere. 01 Dispara com 4 caracteres. 10 Dispara com 8 caracteres. 11 Dispara com 14 caracteres. bit 5,4,3: Não usados; sempre zero bit 2: Inicializa a FIFO de transmissão bit 1: Inicializa a FIFO de recebimento bit 0: Habilita as FIFOs (deve ser 1 para que a FIFO funcione)

A configuração do baud-rate, numero de bits de dados e paridade é feita nos registradores:

U0LCR 0xE000C00C | Line Control Register: Configura a UART. bit 7: DLAB Habilita o acesso ao divisor de Baud Rate bit 6: Set Break bit 5: Paridade fixa bit 4: Paridade par bit 3: Habilita a paridade bit 2: Dois stop bits bit 1 e 0: Número de bits de dados-5
U0DLL 0xE000C000 | Divisor de Baud-Rate menos significativo
U0DLM 0xE000C004 | Divisor de Baud-Rate mais significativo

O divisor de baud-rate DLM:DLL usa os mesmos endereços que o habilitador de interrupções IER e o buffer de transmissão/recebimento de dados THR/RBR. Ele torna-se acessível quando o bit 7 (DLAB) do U0LCR é ligado. Depois que o baud-rate é configurado volta-se ao modo normal de operação com o DLAB desligado. O valor do divisor deve ser:
DLM:DLL = (Freq do clock)/ (16* Baud_rate)
Esta configuração é feita com o seguinte trecho de código:

    #define PCLK 12000000
    #define BAUDRATE 19200
    /* Configuracao da Porta Serial 0 */
    void U0init(void)
    {
    /* 01 nos bits 7,6 do PCLKSEL0 Para selecionar divisor por 1 no PCLK da UART0 */
    PCLKSEL0 = (PCLKSEL0 & (~0xc0)) | 0x40;
    /* 01 e 01 nos bits 7,6 e 5,4 do PINSEL0: Seleciona pinos P0.2 e P0.3 como TxD0 e RxD0 */
    PINSEL0 = (PINSEL0 & (~0xf0)) | 0x50;
    U0FCR   = 0x7;		/* Habilita as FIFOs e reset */
    U0LCR   = 0x83;		/* Habilita acesso ao divisor de baud-rate (DLAB) */
    U0DLL   = ((PCLK/BAUDRATE+8) >> 4) & 0xff;
    U0DLM   = ((PCLK/BAUDRATE) >> 12) & 0xff;
    U0LCR    = 0x03;	/* Configura UART0 como 8N1 */
    }

Existe também um registrador preescalonador do clock que pode servir para obter valores exatos ou melhores aproximações do baud rate desejado. Trata-se do U0FDR (Fractional Divider Register):

U0FDR 0xE000C028 | Fractional Divider Register bit 7 a 4: MULVAL bit 3 a 0: DIVADDVAL

Este registrador preescalona o PCLK da UART pelo fator `MULVAL/(MULVAL+DIVADDVAL)`. Desta forma o divisor de Baud-Rate `U0DLM:U0DLL` passa a ser calculado pela fórmula:

    U0FDR = (MULVAL << 4) + DIVADDVAL;
    Divisor = PCLK * MULVAL / ((MULVAL + DIVADDVAL)*16*BaudRate)
    U0DLM = Divisor >> 8;		; Byte mais significativo
    U0DLL = Divisor & 0xff;	; Byte menos significativo

Por exemplo, se PCLK = 72 MHz, pode-se configurar a UART para exatamente 19200 Baud com MULVAL = 8, DIVADDVAL=7, U0DLM=0 e U0DLL=125.

Depois de feita a configuração, pode-se enviar ou receber dados pela porta serial usando os registradores:

U0RBR 0xE000C000 | Receiver Buffer Register, para leitura. Este registrador tem o byte de dado recebido.
U0THR 0xE000C000 | Mesmo endereço que o U0RBR, mas para escrita. Aqui vai o dado a ser transmitido.
U0LSR 0xE000C014 | Line Status Register: Só leitura; Informa o estado da UART. bit 7: Erro na fila de recebinmento. bit 6: TEMT Toda a fila de transmissão está vazia. bit 5: THRE Indica que tem lugar livre no transmissor. bit 4: Break Interrupt: Acontece quando RX fica em 0 durante mais tempo que um caractere inteiro. bit 3: Framing error: Erro de enquadramento. Foi lido zero quando o stop-bit (sempre 1) era esperado. bit 2: Houve um erro de paridade bit 1: OE Overrun error. Um byte de dados foi perdido porque foi sobreescrito sem ter sido lido. bit 0: RDR Indica que tem dado recebido.

Usando estes registradores pode-se escrever funções para ler e para escrever bytes na UART.

    /* Mascaras para selecionar bits do U0LSR */
    #define THREMPTY 0x20
    #define RXDREADY 0x01

    /* Le um caractere recebido na UART0 */
    char UART0getchar(void)
    {
    while((U0LSR & RXDREADY) == 0);	/* Espera ter dado recebido */
    return U0RBR;				/* Le e retorna o dado recebido */
    }

    /* Envia um caractere pela UART0 */
    void UART0putchar(char c)
    {
    while((U0LSR & THREMPTY) == 0);	/* Espera o transmissor ficar disponivel */
    U0THR = c;				/* Transmite */
    }