Sistemas Embarcados: Programação do LPC2478 usando o gcc

| Laboratório de Processadores Programação do LPC2478 usando o arm-elf-gcc Parte 2: Análise dos programas |

Agora vamos ver mais atentamente os arquivos que formam o projeto. Cada projeto de software para ARM deve ter o seu próprio diretório. Neste caso temos o diretório `a02_piscaled` que foi criado quando expandimos o arquivo a02_piscaled.tgz. Vimos que neste diretório temos os seguintes arquivos:

`Makefile`
  Arquivo de configuração do programa `make`, descrevendo os procedimentos necessários para automatizar a compilação do projeto.
`crt.S`
  Procedimento de inicialização (startup) escrito em linguagem assembly do ARM7. Este procedimento contem o ponto de entrada principal do programa e as entradas das interrupções. Ele tambem incializa a memória e o sistema de clock e chama a rotina `main()` da parte escrita em linguagem C.
`lpc2478_flash.ld`
  Script de configuração de linker/loader. Este arquivo descreve como o `arm-elf-ld` deve alocar memória no LPC2478 para os segmentos do programa.
`lpc24xx.h`
  Arquivo de cabeçalho para `#include` contendo a descrição dos registradores, e portas de I/O dos periféricos do LPC2478. Nos programas atuais este arquivo está junto com o compilador no subdiretório
  `arch/nxp`
`main.c`
  Programa principal. Neste exemplo procurou se fazer a coisa mais simples possível que tenha resultados práticos: Fazer piscar um LED.

A seguir vai uma explicação sobre cada um destes arquivos.

### Makefile

Para executar automaticamente todas as etapas da compilação do projeto abre-se um shell, entra-se o diretório do projeto e usa-se o comando:
`make`
A forma geral do `make` é:
`make alvo`
Onde o `alvo` é o nome de um arquivo ou procedimento definido como alvo no `Makefile`. O comando `make` sem parâmetros equivale ao alvo default chamado `all`. Neste projeto os sequintes alvos podem ser úteis:

make all | Equivale a simplesmente `make`. Compila todos os módulos gerando o arquivo `arm02.hex`.
make arm02.hex | Compila todos os módulos gerando o arquivo `arm02.hex`.
make isp | Compila tudo e chama o `lpc21isp` para gravar o programa na Flash do microcontrolador
make ispu | Compila tudo e chama o `lpc21isp` para gravar o programa na Flash do microcontrolador [usa a interface /dev/ttyUSB0]
make tser | Compila uma versão para ser executada na RAM e chama o terminal `ltser` que envia o programa para a RAM. O monitor `mon24` deve estar instalado na Flash e sendo executado. Para executar o programa feito com o `make tser` usa-se o comando "P" do monitor.
make clean | Apaga os arquivos intermediários gerados na compilação.

O `Makefile` é organizado como um conjunto de alvos (targets) que são receitas de como obter determinado tipo de arquivo. Antes dos targets temos as macros ou definições de variáveis de ambiente, que são uma forma de dar nomes para strings usadas na configuração dos targets. Linhas que começam com # são comentários;

Perto do início do `Makefile` temos uma definição de macro na linha:

    SERIALDEV = /dev/ttyS0

Isto significa que doravante `$(SERIALDEV)` será substituído por `/dev/ttyS0`. Neste caso específico estamos definindo o nome do dispositivo a ser usado como interface serial para programar o Microcontrolador. Observe que este nome é típico da primeira porta serial no Linux. Para outras situações devemos alterar esta linha de acordo. Por exemplo:

SERIALDEV = com1 | Porta serial 1 no Windows
SERIALDEV = com2 | Porta serial 2 no Windows
SERIALDEV = /dev/ttyUSB0 | Conversor USB-RS232 no Linux

Nesta parte inicial também temos outras macros podem requerer modificações para usar o `Makefile` em outros contextos:

CLOCKFREQ = 12000 | Frequência em kHz do clock principal
TARGET = arm02 | Nome do programa a ser gerado na compilação
MODULOS = main.o crt.o | Lista dos módulos (arquivos fonte) que compõe o projeto
HEADERS = lpc23xx.h | Lista dos arquivos de cabeçalho para #includes
BAUDRATE = 19200 | Velocidade da porta serial.

A parte final do `Makefile` define os comandos para obter os targets. A sintaxe é algo assim:

    nome_do_alvo: dependencias
    --tab-->comando

O `nome_do_alvo` especifica o arquivo a ser gerado. As `dependencias` são os arquivos necessários como pré requisitos. Se as dependências não forem satisfeitas, o `make` irá procurar resolve-las executando outros alvos. Antes do comando deve haver um caractere de tabulação. Observe que, para editar o Makefile, não se deve usar editores de texto que substituem tabulação por espaços (como, por exemplo, o notepad do Windows).

O `Makefile` também pode ter alvos genéricos, convertendo um tipo de arquivo em outro. Por exemplo, o seguinte comando compila programas `*.c` em linguagem C, gerando o arquivo objeto `*.o` corresponednte.

    #Compila os modulos em linguagem C
    %.o: %.c
    	$(CC) -c $(CFLAGS) -o $@ $<

Esta diretiva é usada para compilar o programa principal `main.c`, gerando o arquivo objeto `main.o`.
A macro `$(CC)` é expandida como `arm-elf-gcc`; a macro `$(CFLAGS)` é substituida por
`-Wall -O2 -mcpu=arm7tdmi-s`.
O nome especial `$<` é substituído pelo nome do arquivo de entrada `main.c` e a palavra especial `$@` é o nome do arquivo de saída `main.o`. Assim o make vai gerar a seguinte linha de comando:

    arm-elf-gcc -c -Wall -O2 -mcpu=arm7tdmi-s -o main.o main.c

### crt.S

O arquivo crt.S é o programa de partida, em linguagem assembly, que contém o ponto de entrada inicial do reset e os pontos de entrada das rotinas de interrupções. Este programa inicializa as áreas de memória e de pilha e então chama a rotina `main()` definida na parte em linguagem C. Por enquanto não é necessário saber detalhes do funcionamento deste módulo.

### lpc2478_flash.ld

Este é o arquivo de configuração do linker ld, que descreve como o linker deve alocar memória para os diversos segmentos do programa.

O arquivo lpc2478_ram.ld é uma variante deste arquivo de configuração do linker que serve para gerar um programa para ser executado em memória RAM a pratir do endereço 0x40000000. Para usar este modelo de memória o kit deve ter o programa `mon24` intalado na flash. Usa-se o comando
`make tser`
para compilar o programa para rodar na RAM e envia-lo usando o terminal `ltser`. Para executar o programa usa-se então o comando "P" do monitor `mon24`.

O modelo de memória mais comunmente usado pelo gcc usa os seguintes segmentos:

.text | Usado para armazenar as instuções do programa
.data | Usado para armazenar dados variáveis inicializados: Variáveis locais e variáveis estáticas.
.bss | Usado para dados não inicializados
.stack | Pilha do microprocessador e variáveis locais

Estes segmentos devem ser mapeados para a memória do LPC2478.

Mapa de memória do LPC2478

No mapa de memória alguns endereços são típicos, mas podem variar de acordo com o tamanho do programa aplicativo.

Flash 0x00000000 0x0007FFFF (512K) | 0x00000000 0x0000003F | Vetores de Interrupção (64 Bytes)
0x00000040 | Rotinas de partida `crt.S`
0x00000400 | Programas em Linguagem C `main()`
0x00001000 | Valores iniciais do segmento `.data`
0x00001100 0x0007FFFF | Memória Flash livre
0x00080000 0x3FFF7FFF |  | Livre (não definido)
0x3FFF8000 0x3FFFBFFF |  | Registradores de configuração
0x3FFFC000 0x3FFFFFFF |  | Controle das portas FAST GPIO
RAM 0x40000000 0x4000FFFF (64K) | 0x40000000 0x4000003F | Vetores de interrupção (remapeados em RAM)
0x40000040 0x4000011F | Livre
0x40000120 0x400001FF | Usado pelo programador ISP
0x40000200 0x4000020F | .data
0x40000210 0x4000023F | .bss
0x40000240 0x4000FEC7 | Livre (pilha)
0x4000FECC | Pilha SVC
0x4000FED0 | Pilha IRQ
0x4000FED4 | Pilha FIQ
0x4000FED8 | Pilha ABT
0x4000FEDC | Pilha UDF
0x4000FEE0 | Variáveis do ISP
0x40010000 0x7FCFFFFF |  | Reservado
0x7FD00000 0x7FD01FFF |  | USB RAM (16k)
0x7FD02000 0x7FDFFFF |  | Reservado
0x7FE00000 0x7FE01FFF |  | Ethernet RAM (16k)
0x7FE02000 0xDFFFFFFF |  | Livre (não definido)
0xA000000 0xA3FFFFFF |  | SDRAM externa (64 MBytes)
0xE0084000 0xE00847FF |  | Battery RAM: 2kBytes mantidos pela bateria do RTC.
0xE0000000 0xFFFFFFFF |  | Periféricos mapeados em memória.

O firmware do gravador da memória flash (ISP boot loader) usa certas áreas de memória. Os programas aplicativos devem evitar usar estas áreas. O arquivo `lpc2478_flash.ld` é vinculado ao comando que chama o loader com a opção `-T` como no comando:

    ld -Tlpc2478_flash.ld -o arm02.elf main.o crt.o

### main.c

O programa `main.c` contém o programa principal da parte em linguagem C. Este módulo pode ser modificado para alterar a funcionalidade do programa. Como exemplo inicial vamos fazer uma operação muito simples: Fazer piscar um LED. Para isto é necessário aprender como usar as portas de E/S do LPC2478, chamadas de GPIO. O acesso aos dispositivos periféricos do LPC2478 é mapeado em memória. O arquivo de cabeçalho `lpc23xx.h`, incluído no início do `main.c`, contém diretivas para dar nomes aos endereços usados para acessar periféricos de I/O. O LPC2478 tem 4 portas de E/S, com 32 bits cada. Vários pinos também tem outras funções, mas todos eles podem ser configurados para serem portas genéricas de E/S (GPIO).

Os LEDs e os botões estão conectados à porta P4:

Porta | Dispositivo | Máscara hex
--- | --- | ---
P4_15 | LED1 | 0x00008000
P4_16 | LED2 | 0x00010000
P4_17 | LED3 | 0x00020000
P4_18 | LED4 | 0x00040000
P4_19 | LED5 | 0x00080000
P4_20 | LED6 | 0x00010000
P4_21 | LED7 | 0x00020000
P4_22 | LED8 | 0x00040000
P4_26 | Botão SW0 | 0x04000000
P4_27 | Botão SW1 | 0x08000000
P4_30 | Botão SW2 | 0x40000000
P4_31 | Botão SW3 | 0x80000000
P0_17 | PS2_CP Sinal de clock do conector PS/2 | 0x00020000
P0_18 | PS2_DATA Sinal de dados do conector PS/2 | 0x00040000
P1_30 | Sinal RS do display LCD | 0x40000000
P1_31 | Sinal E do display LCD | 0x80000000
P4_23 | Sinal R/W do display LCD | 0x00800000
P2_0 a P2_7 | Dados D0 a D7 do LCD | 0x000000ff

### Registradores do GPIO

Usam-se os seguites registradores para configurar e usar as portas GPIO

FIO4DIR 0x3FFFC080 | Escreve-se neste registrador um padrão de bits que indica se os pinos são de entrada (bit=0) ou saída (bit=1).
FIO4PIN 0x3FFFC094 | O valor escrito neste registrador será escrito nos bits da porta P4. Este registrador pode ser lido para obter o estado dos bits de entrada da porta P4
FIO4SET 0x3FFFC098 | Escreve-se neste registrador para ligar bits da porta P4. Escevendo 1 faz ligar o bit correspondente da porta. Onde o bit for zero, a porta permanecerá com o valor inalterado. Isto equivale a fazer uma operação OU do padrão de bits escrito com o valor anterior da porta.
FIO4CLR 0x3FFFC09C | Escreve-se neste registrador para desligar bits da porta P4. Escevendo 1 faz desligar o bit correspondente da porta. Onde o bit for zero, a porta permanecerá com o valor inalterado. Isto equivale a fazer uma operação E do complemento do padrão de bits escrito com o valor anterior da porta.

### Descrição do programa

Para acessar os pinos como GPIO é necessário inicialmente fazer algumas configurações. Um conjunto de registradores chamado PINSEL0 a PINSEL8, com 2 bits para cada pino, seleciona a função que o pino vai ter. O valor inicial destes registradores PINSEL é geralmente 0, selecionando a função GPIO para os pinos.

Para configurar os pinos dos LEDs como saídas usam-se as configurações

    FIO4DIR = (1<<16) | (1<<17); /* LED2 (P4.16) e LED3 (P4.17) como saida */

O Registrador de 32 bits `FIO4DIR` determina se os bits da porta P3 são entradas ou saídas. Configura-se escrevendo neste registrador um pardrão de bits onde 0 significa entrada e um significa saída. No caso, o valor `(1<<16)` tem apenas o bit 16 ligado (valor numérico = 0x10000).

Para ligar bits da porta P4 escreve-se no `FIO4SET` um padão de bits onde 1 faz ligar o bit e 0 deixa o bit como está. Para desligar um bit escreve-se no `FIO4CLR` um padrão onde 1 (um) faz desligar o bit e 0 (zero) deixa como está. Para controlar o LED2 ligado no P4.17 usa-se o valor `0x00020000` ou (1<<17).

    FIO4SET= (1<<17);	/* Liga bit 17 da porta P4 (Desliga LED2) */
    FIO4CLR= (1<<17);	/* Desliga bit 17 da porta P4 (liga LED2) */

As portas P0 e P1 podem ser controladas por dois métodos diferentes selecionados por um registrador de configuração: o `SCS`. O LPC2478 inicia em um modo legado, compatível com processadores antigos da família LPC20xx, que usa comandos tipo `IOPIN0, IODIR0, IOSET0` e `IOCLR0` para controlar as portas P0 e P1.

Ligando bit 0 do registrador `SCS` seleciona-se o método "fast" para as portas GPIO P0 e P1. Neste modo usam-se comandos tipo `FIO0PIN, FIO0DIR, FIO0SET` ou `FIO0CLR` para controlar os pinos das portas P0 ou P1. Nas portas P2, P3 e P4 existe apenas o método "fast".

Para ler o estado dos botões ligados na porta P4 usa-se o registrador `FIO4PIN`. Este registrador da acesso a todos os 32 bits da porta P1. Para testar um bit individual da porta P4 pode-se usar uma operação e bit a bit, como no trecho de código a seguir:

    /* Se sw1 (P4.26) for apertado, liga LED1 */
    if(!(FIO4PIN & (1<<26))) FIO4CLR = (1<<15);
    /* Se sw2 (P4.27) for apertado, desliga LED1 */
    if(!(FIO4PIN & (1<<27))) FIO4SET = (1<<15);

Os bits da porta P4 podem também ser acessados como 4 grupos de 8 bits:
`FIO4PIN0` bits 0 a 7
`FIO4PIN1` bits 8 a 15
`FIO4PIN2` bits 16 a 23
`FIO4PIN3` bits 24 a 31

O loop principal deste programa faz piscar sequencialmente os LEDs.

Depois da rotina `main()` tem a seguinte série de funções que por enquanto não fazem nada:

    /* Estas rotinas são chamados pelo crt.S.
    Devem existir, mas ainda não estão sendo usadas */
    void UNDEF_Routine(){}
    void SWI_Routine(){}
    void timer_routine(){}

Estas funções são chamadas pela rotina de partida `crt.S` quando ocorrem interrupções. Veremos para que elas servem quando estudarmos o sistema de interrupções.