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

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 mon23 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 mon23.

Configuração da porta serial (UART)

O LPC2378 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 */
}