![]() |
Programação do LPC2378 usando o arm-elf-gcc Parte 2: Análise dos programas |
![]() |
Anterior | Indice | Próximo |
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_pisca_led que foi criado quando expandimos o arquivo a02_pisca_led.tgz. Vimos que neste diretório temos os seguintes arquivos:
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 tser | Compila uma versão para ser executada na RAM e chama o terminal ltser que envia o programa para a RAM. O monitor mon23 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/ttyS0Isto 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 |
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-->comandoO 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.
arm-elf-gcc -c -Wall -O2 -mcpu=arm7tdmi-s -o main.o main.c
O arquivo lpc2378_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
mon23 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 mon23.
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 |
Mapa de memória do LPC2378
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 0x40007FFF (32K) |
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 0x40007EC7 |
Livre (pilha) | |
0x40007ECC | Pilha SVC | |
0x40007ED0 | Pilha IRQ | |
0x40007ED4 | Pilha FIQ | |
0x40007ED8 | Pilha ABT | |
0x40007EDC | Pilha UDF | |
0x40007EE0 | Variáveis do ISP | |
0x40008000 0x7FCFFFFF | Reservado | |
0x7FD00000 0x7FD01FFF |
USB RAM (16k) | |
0x7FD02000 0x7FDFFFF | Reservado | |
0x7FE00000 0x7FE01FFF |
Ethernet RAM (16k) | |
0x7FE02000 0xDFFFFFFF | Livre (não definido) | |
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 lpc2378_flash.ld é vinculado ao comando que chama o loader com a opção -T como no comando:
ld -Tlpc2378_flash.ld -o arm02.elf main.o crt.o
No kit de 2009-1 os LEDs e os botões estão conectados à porta P4:
Pino | Porta | Dispositivo | Máscara hex |
---|---|---|---|
52 | P4_0 | LED1 | 0x00000001 |
55 | P4_1 | LED2 | 0x00000002 |
58 | P4_2 | LED3 | 0x00000004 |
68 | P4_3 | LED4 | 0x00000008 |
72 | P4_4 | LED5 | 0x00000010 |
74 | P4_5 | LED6 | 0x00000020 |
78 | P4_6 | LED7 | 0x00000040 |
84 | P4_7 | LED8 | 0x00000080 |
86 | P4_8 | Botão SW0 | 0x00000100 |
91 | P4_9 | Botão SW1 | 0x00000200 |
94 | P4_10 | Botão SW2 | 0x00000400 |
101 | P4_11 | Botão SW3 | 0x00000800 |
104 | P4_12 | Botão SW4 | 0x00001000 |
127 | P4_24 | Sinal E do display LCD | 0x01000000 |
124 | P4_25 | Sinal RS do display LCD | 0x02000000 |
130 | P4_30 | PS2_CP Sinal de clock do conector PS/2 | 0x40000000 |
134 | P4_31 | PS2_DATA Sinal de dados do conector PS/2 | 0x80000000 |
P3_0 a P3_7 | Dados D0 a D7 do LCD | 0x000000ff |
Na versão 2008/2 do kit LPC2378 da PUCRS temos os seguintes componentes ligados a portas de I/O:
Pino | Porta | Dispositivo | Máscara hex |
---|---|---|---|
45 | P3_23 | LED1 | 0x00800000 |
40 | P3_24 | LED2 | 0x01000000 |
39 | P3_25 | LED3 | 0x02000000 |
38 | P3_26 | LED4 | 0x04000000 |
72 | P2_6 | LED5 | 0x00000040 |
74 | P2_9 | LED6 | 0x00000200 |
118 | P4.28 | LED7 | 0x10000000 |
122 | P4.29 | LED8 | 0x20000000 |
47 | P1_19 | Botão SW1 | 0x00080000 |
51 | P1_22 | Botão SW2 | 0x00400000 |
56 | P1_25 | Botão SW3 | 0x02000000 |
57 | P1_26 | Botão SW4 | 0x04000000 |
127 | P4_24 | Sinal E do display LCD | 0x01000000 |
124 | P4_25 | Sinal RS do display LCD | 0x02000000 |
P3_0 a P3_7 | Dados D0 a D7 do LCD | 0x000000ff |
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. |
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<<2) | (1<<3); /* LED3 (P4.2) e LED4 (P4.3) 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<<2) tem apenas o bit 2 ligado (valor numérico = 4).
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 LED4 ligado no P4.3 usa-se o valor 0x00000008 ou simplesmente 8. (Observe que 8 corresponde ao valor binário 1000 bin: com 1 no bit 3)
FIO4SET= 8; /* Liga bit 3 da porta P4 (Desliga LED4) */ FIO4CLR= 8; /* Desliga bit 3 da porta P4 (liga LED4) */
As portas P0 e P1 podem ser controladas por dois métodos diferentes selecionados por um registrador de configuração: o SCS. O LPC2378 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.9) for apertado, liga LED3 */ if(!(FIO4PIN & (1<<9))) FIO4CLR = 4; /* Se sw2 (P4.10) for apertado, desliga LED3 */ if(!(FIO4PIN & (1<<10))) FIO4SET = 4;O loop principal deste programa faz piscar um LED e controla o outro usando os botões.
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.
Anterior | Indice | Próximo |