Of course, it's possible to start in the embedded world with a real hello world application, but it's easier to do something more simple. We will start with a LED blinking application.
For this example I have chosen TMS470 ARM7TDMI microcontroller, but it's pretty easy to modify it for almost any ARM7TDMI microcontroller. Anyway, if you want to use this microcontroller, you might find useful example schematics at http://www.arm-development.com/tms470. You will have to attach a LED diode through 330 resistor to any of HET ports. The schematics is verified to be working.
Other files (like TMS470 header files) can be downloaded from http://www.arm-development.com/.
The following makefile is used to compile sources to a binary file that can be flashed directly to the microcontroller.
ARM7AS = /usr/local/arm7/bin/arm-elf-as ARM7LD = /usr/local/arm7/bin/arm-elf-ld ARM7CC = /usr/local/arm7/bin/arm-elf-gcc OBJCOPY = /usr/local/arm7/bin/arm-elf-objcopy CFLAGS = -c -O2 -Wall -DDEBUG=1 -mbig-endian CC = $(ARM7CC) COMMON_SRC = hw.c main.c FIXADDR = -Ttext 0x0 -Tbss 0x30000 all: code.bin depend: gcc -E -MM startup.S main.c > .depend -include .depend clean: rm -f *.o code*.bin code*.elf core .depend *~ code.bin: code.elf @$(OBJCOPY) -v -O binary --remove-section ".comment" \ code.elf code.bin code.elf: main.o startup.o rtos.o $(ARM7LD) -EB -static $(FIXADDR) -o code.elf \ startup.o main.o rtos.o \ libgcc.a startup.o: startup.S $(ARM7CC) $(CFLAGS) -c startup.S -mbig-endian
This is my own startup code for ARM7 architecture. It has to be modified for each microcontroller, because it uses interrupt controller, which is different for each microcontroller type.
/* ARM7 startup code Written by Martin Hinner <hinner(at)s e c o n s(dot)com> */ #define RAM_BASE 0x300000 #define RAM_SIZE 12*1024 #define USR_STACK_SIZE 1024 #define USR_STACK_POINTER (RAM_BASE + RAM_SIZE) #define Mode_USR 0x10 #define Mode_FIQ 0x11 #define Mode_IRQ 0x12 #define Mode_SVC 0x13 #define Mode_ABT 0x17 #define Mode_UNDEF 0x1B #define Mode_SYS 0x1F #define I_Bit 0x80 #define F_Bit 0x40 #define EIC_Base_Addr 0xFFFFF800 #define ICR_Off_Addr 0x00 /* Interrupt ctrl. reg. */ #define CICR_Off_Addr 0x04 #define CIPR_Off_Addr 0x08 /* Interrupt ctrl. reg. */ #define IVR_Off_Addr 0x18 /* Interrupt ctrl. reg. */ #define FIR_Off_Addr 0x1C /* Interrupt ctrl. reg. */ #define IER_Off_Addr 0x20 /* Interrupt ctrl. reg. */ #define IPR_Off_Addr 0x40 /* Interrupt ctrl. reg. */ #define SIR0_Off_Addr 0x60 /* Interrupt ctrl. reg. */ .text .global __vectors __vectors: ldr pc, reset_addr ldr pc, undef_addr ldr pc, swi_addr ldr pc, prefetch_addr ldr pc, abort_addr nop @ reserved ldr pc, irq_addr ldr pc, fiq_addr reset_addr: .word startup_routine undef_addr: .word undef_routine swi_addr: .word 0 prefetch_addr: .word abort_routine abort_addr: .word abort_routine irq_addr: .word irq_routine fiq_addr: .word abort_routine .global _start .extern main .extern lowlevelinit .global startup_routine .global undef_routine .global swi_routine .global abort_routine .global irq_routine irq_routine: sub lr, lr, #4 stmfd sp!, {lr} @- Save and r0 in IRQ stack stmfd sp!, {r0-r11} mvn r8, #0xDF @ IRQIVEC = FFFF FF20 ldr r8,[r8] and r8, r8, #0xFF mov r0, #(irq_vectors-4) mov r14,pc ldr pc,[r0, r8, lsl #2] ldmia sp!, {r0-r11} @- Restore adjusted LR_irq from IRQ stack directly in the PC ldmia sp!, {pc}^ irq_vectors: .word IRQ0Handler .word IRQ1Handler .word IRQ2Handler .word IRQ3Handler .word IRQ4Handler .word IRQ5Handler .word IRQ6Handler .word IRQ7Handler .word IRQ8Handler .word IRQ9Handler .word IRQ10Handler .word IRQ11Handler .word IRQ12Handler .word IRQ13Handler .word IRQ14Handler .word IRQ15Handler .word IRQ16Handler .word IRQ17Handler .word IRQ18Handler .word IRQ19Handler .word IRQ20Handler .word IRQ21Handler .word IRQ22Handler .word IRQ23Handler .word IRQ24Handler .word IRQ25Handler .word IRQ26Handler .word IRQ27Handler .word IRQ28Handler .word IRQ29Handler .word IRQ30Handler .word IRQ31Handler .extern IRQ0Handler .extern IRQ1Handler .extern IRQ2Handler .extern IRQ3Handler .extern IRQ4Handler .extern IRQ5Handler .extern IRQ6Handler .extern IRQ7Handler .extern IRQ8Handler .extern IRQ9Handler .extern IRQ10Handler .extern IRQ11Handler .extern IRQ12Handler .extern IRQ13Handler .extern IRQ14Handler .extern IRQ15Handler .extern IRQ16Handler .extern IRQ17Handler .extern IRQ18Handler .extern IRQ19Handler .extern IRQ20Handler .extern IRQ21Handler .extern IRQ22Handler .extern IRQ23Handler .extern IRQ24Handler .extern IRQ25Handler .extern IRQ26Handler .extern IRQ27Handler .extern IRQ28Handler .extern IRQ29Handler .extern IRQ30Handler .extern IRQ31Handler _start: startup_routine: msr cpsr_c, #Mode_SYS | I_Bit | F_Bit ldr SP,=USR_STACK_POINTER bl lowlevelinit msr cpsr_c, #Mode_IRQ | F_Bit | I_Bit ldr SP,=(USR_STACK_POINTER-USR_STACK_SIZE-4) msr cpsr_c, #Mode_UNDEF | F_Bit | I_Bit ldr SP,=(USR_STACK_POINTER-USR_STACK_SIZE-USR_STACK_SIZE-4) msr cpsr_c, #Mode_FIQ | F_Bit | I_Bit ldr SP,=(USR_STACK_POINTER-USR_STACK_SIZE-USR_STACK_SIZE-USR_STACK_SIZE-4) msr cpsr_c, #Mode_ABT | F_Bit | I_Bit ldr SP,=(USR_STACK_POINTER-USR_STACK_SIZE-USR_STACK_SIZE-USR_STACK_SIZE-USR_STACK_SIZE-4) msr cpsr_c, #Mode_SVC | F_Bit | I_Bit ldr SP,=(USR_STACK_POINTER-USR_STACK_SIZE-USR_STACK_SIZE-USR_STACK_SIZE-USR_STACK_SIZE-USR_STACK_SIZE-4) msr cpsr_c, #Mode_SYS ldr SP,=USR_STACK_POINTER b main undef_routine: b undef_routine abort_routine: b abort_routine disable: stmfd sp!,{r0} mrs r0,cpsr orr r0, r0, #(I_Bit|F_Bit) msr cpsr,r0 ldmfd sp!,{r0} bx lr enable: stmfd sp!,{r0} mrs r0,cpsr and r0, r0, #~(I_Bit|F_Bit) msr cpsr,r0 ldmfd sp!,{r0} bx lr .global enable .global disable
The following code (main.c) contains three important parts:
#include "iotms470r1a128.h" #include "tms470r1a256_bit_definitions.h" void lowlevelinit () { SYSECR = 0x4007; MFBAHR0 = 0x0; // memsel0 MFBALR0 = 0xa0; MFBAHR2 = 0x30; // memsel1 MFBALR2 = 0x50; MFBALR0 = 0x1a0; } void main(void) { int i; int j; int canid; PCR = CLKDIV_1; // ICLK = SYSCLK PCR |= PENABLE; // enable peripherals HETDIR = 0xFFFFFFFF; // HETx Output direction HETDOUT = 0x00000000; // HET31 reset, else set #if SERIAL_TEST SCI2CTL3 &= ~SW_NRESET; // Reset SCI state machine SCI2CCR = TIMING_MODE_ASYNC + CHAR_8; // Async, 8-bit Char SCI2CTL1 |= RXENA; // RX enabled SCI2CTL2 |= TXENA; // TX enabled SCI2CTL3 = CLOCK ;//+ RX_ACTION_ENA; // Internal clock. RX interrrupt SCI2LBAUD = (6000000/(8*19200))-1; // clock/(8*baud) SCI2PC2 |= RX_FUNC; // SCIRX is the SCI receive pin SCI2PC3 |= TX_FUNC; // SCITX is the SCI transmit pin SCI2CTL3 |= SW_NRESET; // Configure SCI2 state machine #endif j = 0; for (;;) { #if SERIAL_TEST j++ ; SCI2TXBUF = '0' + (j%10); #endif for (i=0;i<100000;i++); HETDOUT = 0xFFFFFFFF; // HET31/0 Toggle for (i=0;i<100000;i++); HETDOUT = 0x0; // HET31/0 Toggle #if SERIAL_TEST if ( (SCI2CTL1 & RXRDY) ) { SCI2TXBUF = SCI2RXBUF; SCI2CTL1 = RXENA; SCI2CTL1 = RXENA | RXRDY; } #endif } } void IRQ0Handler (void) { } void IRQ1Handler (void) { } /* PIOA */ void IRQ2Handler (void) { } void IRQ3Handler (void) { } void IRQ4Handler (void) { } void IRQ5Handler (void) { } void IRQ6Handler (void) { } void IRQ7Handler (void) { } void IRQ8Handler (void) { } void IRQ9Handler (void) { } void IRQ10Handler (void) { } void IRQ11Handler (void) { } void IRQ12Handler (void) { } void IRQ13Handler (void) { } void IRQ14Handler (void) { } void IRQ15Handler (void) { } void IRQ16Handler (void) { } void IRQ17Handler (void) { } void IRQ18Handler (void) { } void IRQ19Handler (void) { } void IRQ20Handler (void) { } void IRQ21Handler (void) { } void IRQ22Handler (void) { } void IRQ23Handler (void) { } void IRQ24Handler (void) { } void IRQ25Handler (void) { } void IRQ26Handler (void) { } void IRQ27Handler (void) { } void IRQ28Handler (void) { } void IRQ29Handler (void) { } void IRQ30Handler (void) { } void IRQ31Handler (void) { }