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)
{
}