Zdrojový kód v čistém C.
#include <stdint.h>
#include <stddef.h>
#ifndef _IO_H
#define _IO_H
#define __IO volatile
#ifdef ARM_CM0
} ;
uint16_t RESERVED0;
uint16_t RESERVED1;
uint16_t RESERVED2;
union {
struct {
};
};
uint16_t RESERVED3;
} ;
#define PERIPH_BASE ((uint32_t) 0x40000000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x00020000)
#define AHB2PERIPH_BASE (PERIPH_BASE + 0x08000000)
#define RCC_BASE (AHBPERIPH_BASE + 0x00001000)
#define GPIOC_BASE (AHB2PERIPH_BASE + 0x00000800)
#define RCC_AHBENR_GPIOCEN ((uint32_t)0x00080000)
// board specific
#define LEDPORT_BASE GPIOC_BASE
#define LEDCLK_GPIOEN RCC_AHBENR_GPIOCEN
#define LEDPIN 8
#define DELAY_TIME 0x40000
#define DELAY_SYST 4000000
typedef enum {
#define __CM0_REV 0
#define __MPU_PRESENT 0
#define __NVIC_PRIO_BITS 2
#define __Vendor_SysTickConfig 0
#include "core_cm0.h"
#endif // ARM_CM0
#ifdef ARM_CM4
uint32_t RESERVED0;
uint32_t RESERVED1[2];
uint32_t RESERVED2;
uint32_t RESERVED3[2];
uint32_t RESERVED4;
uint32_t RESERVED5[2];
uint32_t RESERVED6[2];
__IO uint32_t PLLI2SCFGR;
} ;
} ;
#define PERIPH_BASE ((uint32_t)0x40000000)
#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000)
#define RCC_BASE (AHB1PERIPH_BASE + 0x3800)
#define GPIOD_BASE (AHB1PERIPH_BASE + 0x0C00)
#define LEDPORT_BASE GPIOD_BASE
#define LEDPIN 15
#define LEDCLK_GPIOEN ((uint32_t)0x00000008)
#define DELAY_TIME 0x200000
#define DELAY_SYST 8000000
typedef enum IRQn {
MemoryManagement_IRQn = -12,
BusFault_IRQn = -11,
UsageFault_IRQn = -10,
SVCall_IRQn = -5,
DebugMonitor_IRQn = -4,
TAMP_STAMP_IRQn = 2,
RTC_WKUP_IRQn = 3,
EXTI0_IRQn = 6,
EXTI1_IRQn = 7,
EXTI2_IRQn = 8,
EXTI3_IRQn = 9,
EXTI4_IRQn = 10,
DMA1_Stream0_IRQn = 11,
DMA1_Stream1_IRQn = 12,
DMA1_Stream2_IRQn = 13,
DMA1_Stream3_IRQn = 14,
DMA1_Stream4_IRQn = 15,
DMA1_Stream5_IRQn = 16,
DMA1_Stream6_IRQn = 17,
ADC_IRQn = 18,
CAN1_TX_IRQn = 19,
CAN1_RX0_IRQn = 20,
CAN1_RX1_IRQn = 21,
CAN1_SCE_IRQn = 22,
EXTI9_5_IRQn = 23,
TIM1_BRK_TIM9_IRQn = 24,
TIM1_UP_TIM10_IRQn = 25,
TIM1_TRG_COM_TIM11_IRQn = 26,
TIM4_IRQn = 30,
I2C1_EV_IRQn = 31,
I2C1_ER_IRQn = 32,
I2C2_EV_IRQn = 33,
I2C2_ER_IRQn = 34,
USART3_IRQn = 39,
EXTI15_10_IRQn = 40,
RTC_Alarm_IRQn = 41,
OTG_FS_WKUP_IRQn = 42,
TIM8_BRK_TIM12_IRQn = 43,
TIM8_UP_TIM13_IRQn = 44,
TIM8_TRG_COM_TIM14_IRQn = 45,
TIM8_CC_IRQn = 46,
DMA1_Stream7_IRQn = 47,
FSMC_IRQn = 48,
SDIO_IRQn = 49,
TIM5_IRQn = 50,
SPI3_IRQn = 51,
UART4_IRQn = 52,
UART5_IRQn = 53,
TIM7_IRQn = 55,
DMA2_Stream0_IRQn = 56,
DMA2_Stream1_IRQn = 57,
DMA2_Stream2_IRQn = 58,
DMA2_Stream3_IRQn = 59,
DMA2_Stream4_IRQn = 60,
ETH_IRQn = 61,
ETH_WKUP_IRQn = 62,
CAN2_TX_IRQn = 63,
CAN2_RX0_IRQn = 64,
CAN2_RX1_IRQn = 65,
CAN2_SCE_IRQn = 66,
OTG_FS_IRQn = 67,
DMA2_Stream5_IRQn = 68,
DMA2_Stream6_IRQn = 69,
DMA2_Stream7_IRQn = 70,
USART6_IRQn = 71,
I2C3_EV_IRQn = 72,
I2C3_ER_IRQn = 73,
OTG_HS_EP1_OUT_IRQn = 74,
OTG_HS_EP1_IN_IRQn = 75,
OTG_HS_WKUP_IRQn = 76,
OTG_HS_IRQn = 77,
DCMI_IRQn = 78,
CRYP_IRQn = 79,
HASH_RNG_IRQn = 80,
FPU_IRQn = 81
#define __CM4_REV 0x0001
#define __MPU_PRESENT 1
#define __NVIC_PRIO_BITS 4
#define __Vendor_SysTickConfig 0
#include "core_cm4.h"
#endif // ARM_CM4
#define NAKED __attribute__((naked))
static inline void fillram (
void) {
__ASM volatile (
"ldr r0, =0x20000800\n\t"
"ldr r1, =0x20001800\n\t"
"ldr r2, =0xDEADBEEF\n"
"floop:\n\t"
"str r2, [r0, #0]\n\t"
"add r0, r0, #4\n\t"
"cmp r0, r1\n\t"
"bne floop\n\t"
);
}
#endif// _IO_H
Všimněte si, že popis instrukční sady (thumb, code 16) i typu procesoru je zabudován přímo do zdrojového textu.
.ifdef ARM_CM0
.cpu cortex-m0
.fpu softvfp
.set RCCREG, 0x40021000
.set RCCSET, 20
.set RCCENB, 19
.set GPIOREG, 0x48000800
.set LED, 8
.set DLED, 16
.set DROFS, 20
.set TIME, 18
.endif
.ifdef ARM_CM4
.cpu cortex-m4
.fpu softvfp
.set RCCREG, 0x40023800
.set RCCSET, 48
.set RCCENB, 3
.set GPIOREG, 0x40020C00
.set LED, 15
.set DLED, 30
.set DROFS, 20
.set TIME, 18
.endif
.section .text.Reset_Handler,"ax",%progbits
.align 1
.global Reset_Handler
.code 16
.thumb_func
.type Reset_Handler, %function
Reset_Handler:
mov r2, #1 @ obecna jednicka, zachovava se cely cas behu
ldr r3, RCC @ hodiny
lsl r0, r2, #RCCENB
str r0, [r3, #RCCSET]
ldr r3, GPIOC @ pin jako vystup
lsl r0, r2, #DLED
str r0, [r3]
lsl r1, r2, #LED @ r1 = pozice LED
InfiniteLoop:
ldrh r0, [r3, #DROFS] @ xor vystupu
eor r0, r1
strh r0, [r3, #DROFS]
@ ldr r0, Dly @ zpozdeni - setrime tedy ->
lsl r0, r2, #TIME @ alternativne takto, pokud staci nasobky 2
DelayLoop:
sub r0, r2
cmp r0, #0
bne DelayLoop
b InfiniteLoop
.align 2
RCC: .word RCCREG
GPIOC: .word GPIOREG
@Dly: .word 0x00040000
/* Tohle je obsah vectors.c přeložený do jazyka symbolických adres. */
.global Vectors
.extern _estack
.section .rodata.Vectors,"a",%progbits
.align 2
.type Vectors, %object
Vectors:
.word _estack
.word Reset_Handler
/* Použijeme to tak v jednom souboru aby bylo vidět, že i to jde */
Výsledek je vytvořen pomocí Makefile. Vypadá to složitě, ale je to poměrně univerzální kopyto, jež lze použít i pro dost složité projekty. Proto je tam i dost komentářů, z kterých (snad) lze pochopit, jak to funguje.
##########################################################################
# Uživatelské definice - pro dané nastavení zde lze vytvořit projekt
# jednoduše tak, že zadáme jméno výstupu a vstupní objekty.
##########################################################################
# Pro STM32F0 Discovery
# CPU_TYPE = cortex-m0
# Pro STM32F4 Discovery
CPU_TYPE = cortex-m4
# VPATH je cesta ke zdrojovým souborům, zde jen hlavní adresář
VPATH = .
# C files
COBJS = vectors.o cblink.o
# C/C++ files
BOBJS = vectors.o bblink.o
# ASM files
AOBJS = ablink.o
SOBJS = sysvect.o sblink.o
##########################################################################
# Následují pokročilejší nastavení - cesta k hlavičkám
##########################################################################
INCLUDE_PATHS = -I.
##########################################################################
# GNU GCC OR clang compiler - verze pro ARM - crosscompiler
##########################################################################
CROSS_COMPILE = arm-none-eabi-
# Můžeme zkusit použít i clang - pak COMPILER = clang
COMPILER =
ifeq ($(COMPILER),clang)
# Pro clang je lépe nastavit vyšší optimalizaci, -fshort-enums je kvůli kompatibititě s ld.
# Ta verze clang-3.5 jako příkaz je z Ubuntu - pozor různé verze mají odlišné přepínače cílové architektury (-target)
CC = clang-3.8 -target arm -mfloat-abi=soft -fshort-enums -O3
CXX = clang++-3.8 -target arm -mfloat-abi=soft -fshort-enums -O3
else
CC = $(CROSS_COMPILE)gcc -Os
CXX = $(CROSS_COMPILE)g++ -Os
endif
# Assembler (*.s) lze překládat i pomocí gcc (-x assembler-with-cpp)
AS = $(CROSS_COMPILE)as
# Stejně tak linkování se obvykle provádí pomocí gcc, zde pro názornost přímo
LD = $(CROSS_COMPILE)ld
SIZE = $(CROSS_COMPILE)size
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
##########################################################################
# Startup files - zde jen skript pro linker
##########################################################################
LD_SCRIPT = script.ld
##########################################################################
# Nastavení parametrů překladu
##########################################################################
# 1.Překlad pomocí gcc / g++
ifeq ($(CPU_TYPE),cortex-m0)
DEFINES = ARM_CM0
endif
ifeq ($(CPU_TYPE),cortex-m4)
DEFINES = ARM_CM4
endif
# Defines - podmineny preklad
CFLAGS = -D$(DEFINES) -I./cmsis
# Instrukční sada thumb, cpu Cortex-Mx
MFLAGS = -mthumb -mcpu=$(CPU_TYPE)
ifneq ($(COMPILER),clang)
# Vytvořit listing s příponou .lst (divné, ale - kouzlit se dá všelijak)
# clang tento přepínač neakceptuje, protože funguje trochu jinak
CFLAGS += -Wa,-adhlns=$(@:%.o=%.lst)
endif
CFLAGS += $(MFLAGS)
# Cesta k hlavičkám, optimalizace na velikost, všechna varování, debug info
CFLAGS += -c $(INCLUDE_PATHS) -Wall -g
# Umístit kód funkcí i proměnné do vzláštních sekcí
CFLAGS += -ffunction-sections -fdata-sections
# pro C++ nutno ještě vypnout obsluhu výjimek
CFLAGS += -fno-exceptions
# 2. Linker
# Vytvořit mapu rozvržení paměti, křížové reference
LDFLAGS = --cref
# bez default startupu (crt.o), odstranit nepotřebné sekce
LDFLAGS+= -nostartfiles
LDFLAGS+= --gc-sections
# Vlastní linker skript
LDLIBS = -T $(LD_SCRIPT)
# 3. Ostatní
# Assembler nepotřebuje žádné parametry, pokud je vše ve zdrojovém textu
ASFLAGS = --defsym $(DEFINES)=1
# Odřež na výstupu vše nepotřebné (objcopy)
OCFLAGS = --strip-unneeded
##########################################################################
# Rules - pravidla pro tvorbu výstupu.
##########################################################################
# Výsledek všeho (all) má být soubor *.elf - z něj se odvodí další
all: cblinking.elf bblinking.elf ablinking.elf sblinking.elf
# Ten je závislý na objektech $(XOBJS) : po jeho vytvoření lze udělat hex, bin atd...
cblinking.elf: $(COBJS)
$(LD) -Map=cblinking.map $(LDFLAGS) -o cblinking.elf $(COBJS) $(LDLIBS)
-@echo ""
$(SIZE) cblinking.elf
-@echo ""
$(OBJCOPY) $(OCFLAGS) -O binary cblinking.elf cblinking.bin
$(OBJCOPY) $(OCFLAGS) -O ihex cblinking.elf cblinking.hex
$(OBJDUMP) -h -S cblinking.elf > cblinking.lst
-@echo ""
# stejne C++
bblinking.elf: $(BOBJS)
$(LD) -Map=bblinking.map $(LDFLAGS) -o bblinking.elf $(BOBJS) $(LDLIBS)
-@echo ""
$(SIZE) bblinking.elf
-@echo ""
$(OBJCOPY) $(OCFLAGS) -O binary bblinking.elf bblinking.bin
$(OBJCOPY) $(OCFLAGS) -O ihex bblinking.elf bblinking.hex
$(OBJDUMP) -h -S bblinking.elf > bblinking.lst
-@echo ""
# stejne ASM
ablinking.elf: $(AOBJS)
$(LD) -Map=ablinking.map $(LDFLAGS) -o ablinking.elf $(AOBJS) $(LDLIBS)
-@echo ""
$(SIZE) ablinking.elf
-@echo ""
$(OBJCOPY) $(OCFLAGS) -O binary ablinking.elf ablinking.bin
$(OBJCOPY) $(OCFLAGS) -O ihex ablinking.elf ablinking.hex
$(OBJDUMP) -h -S ablinking.elf > ablinking.lst
-@echo ""
# blikani pomoci systick
sblinking.elf: $(SOBJS)
$(LD) -Map=sblinking.map $(LDFLAGS) -o sblinking.elf $(SOBJS) $(LDLIBS)
-@echo ""
$(SIZE) sblinking.elf
-@echo ""
$(OBJCOPY) $(OCFLAGS) -O binary sblinking.elf sblinking.bin
$(OBJCOPY) $(OCFLAGS) -O ihex sblinking.elf sblinking.hex
$(OBJDUMP) -h -S sblinking.elf > sblinking.lst
-@echo ""
# Divná makra $@ $< lze najít v dokumentaci make
# Každý objekt *.o je závislý na zdroji - zde *.c
%.o : %.c
$(CC) -std=c11 $(CFLAGS) -o $@ $<
# nebo c++
%.o : %.cpp
$(CXX) $(CFLAGS) -o $@ $<
# nebo *.s
%.o : %.s
$(AS) $(ASFLAGS) -o $@ $<
# Vyčištění adresáře
clean:
rm -f *.o *.map *~ *.lst *.elf *.hex *.bin
/* Entry Point - musí být definován */
ENTRY(Vectors)
/* Specifikace paměťových prostorů čipu */
MEMORY {
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 4K
ROM (xr) : ORIGIN = 0x08000000, LENGTH = 4K
}
/* Kam umístíme stack - navrch RAM */
_estack = ORIGIN(RAM) + LENGTH(RAM);
/* Definice výstupních sekcí */
SECTIONS {
.fixed : {
/* Takto se vkládají symboly, které se pak mohou použít jako konstanty v programu.
Tečka představuje aktuální hodnotu PC (jako v asm), tedy adresu symbolu. */
_vect_tab = .;
/* Obvykle se tato sekce nějak pojmenuje, zde víme, že gcc to pojmenuje
právě takto, není pak třeba používat __attribute__((section("nazev"))).
Linker by to mohl uklidit (gc-sections), ale ENTRY (Vectors) to zachová
takže není potřeba používat KEEP. Tohle jsou ale už dost divné finty */
*(.rodata.Vectors)
*(.text*) /* .text* sections (code) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
*(.eh_frame)
} >RAM
.reloc : {
*(.data*) /* .data* sections */
*(.bss*) /* .bss* sections */
*(COMMON)
} >RAM
.ARM.attributes 0 : { *(.ARM.attributes) }
}