ARM_minimal.
Funkce | Proměnné
Dokumentace souboru cblink.c

Zdrojový kód v čistém C. ...

#include "io.h"

Funkce

void Reset_Handler (void)
 Místo main() přímo tt. funkce. ...
 
static void pinGpioCoutInit (const unsigned int pin)
 
static void pinGpioCtoggle (const unsigned int pin)
 
static void Delay (volatile unsigned int tick)
 

Proměnné

static RCC_TypeDef *const RCC = (RCC_TypeDef *) RCC_BASE
 
static GPIO_TypeDef *const GPIOX = (GPIO_TypeDef *) LEDPORT_BASE
 Stejně pro GPIO bránu.
 
const unsigned int LED = LEDPIN
 Modrá LED na kitu.
 

Detailní popis

Zdrojový kód v čistém C.

Celkový kód v C nebo C++.

Nic na tom není. Vektory jsou v extra souboru vectors.c, je to společná část pro C i C++. Jsou použity jen první dva - nastavení SP a reset.

typedef void (*pHandler) (void);
extern void _estack (void);
extern void Reset_Handler (void);
/*
* S tímto zápisem se setkáme u složitějších projektů psaných pro GCC.
* ALIAS v podstatě znamená, že pokud je vektor např. HardFault_Handler někde dále
* v programu definován, použije se právě tato definice. Pokud definován není,
* použije se pro obsluhu právě ta ALIAS funkce, která definována být musí.
* Jiné překladače to budou mít udělané podobně, ne však úplně stejně. Někdy je
* (zřejmě ze zvyku, jiný důvod pro to není) tato část psána v jazyce symbolických adres.
* */
#define ALIAS(f) __attribute__ ((weak, alias (#f)))
void HardFault_Handler (void) ALIAS(Default_Handler);
void SVC_Handler (void) ALIAS(Default_Handler);
void PendSV_Handler (void) ALIAS(Default_Handler);
void SysTick_Handler (void) ALIAS(Default_Handler);
pHandler const Vectors[] = {
Reset_Handler, // Reset handler
#if 0
/* ostatní nepotřebujeme */
NMI_Handler, // The NMI handler
HardFault_Handler, // The hard fault handler
0, // Reserved
0, // Reserved
0, // Reserved
0, // Reserved
0, // Reserved
0, // Reserved
0, // Reserved
SVC_Handler, // SVCall handler
0, // Reserved
0, // Reserved
PendSV_Handler, // The PendSV handler
SysTick_Handler // The SysTick handler
// ... atd.
#endif // 0
};
void Default_Handler (void) {
for (;;);
}

Vlastní zdroj pro čisté C (cblink.c)

#include "io.h"
/* Konstanty RCC, GPIOX (ve složitějších případech mohou být i další) jsou ukazatelé
* na pevné místo do paměti, kde se nalézají příslušné registry. Protože tyto buňky
* paměti jsou deklarovány jako volatilní (__IO), zápis nebo čtení se provádí vždy,
* bez ohledu na optimalizaci. Je to ustálený způsob přístupu k registrům procesorů
* v jazyce C, C++. Oproti běžným zvyklostem z 8-bitových procesorů je to něco trochu
* odlišného, ale je to jen formalizmus, jednoduchý a možná i efektivnější.
* Za povšimnutí stojí, že registry jsou 32. i 16.bitové, překladač si s tím poradí.
* */
static RCC_TypeDef * const RCC = (RCC_TypeDef *) RCC_BASE;
extern void Reset_Handler (void);
static inline void pinGpioCoutInit (const unsigned int pin) {
// mělo by být nastavení pomocí |=, ale zde jen = nevadí, kratší o 2 instr.
RCC ->AHBENR = LEDCLK_GPIOEN; // zavést hodiny
GPIOX->MODER = (1U << 2*pin); // a nastavit jako výstup
}
static inline void pinGpioCtoggle (const unsigned int pin) {
GPIOX->ODR ^= (1U << pin); // změnit výstup
}
static inline void Delay (volatile unsigned int tick) {
while (tick--);
}
const unsigned int LED = LEDPIN;
/*NAKED*/ void Reset_Handler (void) {
// fillram ();
for (;;) {
}
}

Nebo pro C++ (bblink.cpp)

#include "io.h"
extern "C" { // Tato část je vždy v čistém C - propojujeme kód v C a C++
extern void Reset_Handler (void);
}
/* Základní IO použité v programu. Je to podobné jako v čistém C, jen je použita
* jiná syntaxe, typická pro C++. Je to stejně potenciálně nebezpečný kus kódu,
* tato syntaxe jen umožňuje ji lépe vyhledávat ve zdrojácích. Pokud takové přetypování
* napíšeme, dáváme tím najevo, že víme přesně co děláme a byl by problém udělat to jinak.
*/
static RCC_Type * const RCC = reinterpret_cast<RCC_Type * const> RCC_BASE;
static GPIO_Type * const GPIOX = reinterpret_cast<GPIO_Type * const> LEDPORT_BASE;
/* I když to napíšeme jako třídu v C++, nic se nezmění.
* Po optimalizaci kód nijak nenaroste. Dá se dokonce říct,
* (alespoň u mé verze GCC to tak je), že děláme úplně stejnou
* věc a výsledná binárka je tedy úplně stejná jako v případě
* C-kódu. Protože jsou všechny metody jednoduché můžeme je nechat
* přímo v deklaraci třídy (deklarace je zároveň i definicí), takže
* budou implicitně inline - stejně jako v C.
*/
class GPIOCC {
public:
GPIOCC (const unsigned pin) : bitpos (1U << pin) {
// mělo by být nastavení pomocí |=, ale zde jen = nevadí, kratší o 2 instr.
RCC ->AHBENR = LEDCLK_GPIOEN; // zavést hodiny
GPIOX->MODER = (1U << 2*pin); // a nastavit jako výstup
}
void toggle (void) const {
GPIOX->ODR ^= bitpos; // změnit výstup
}
void set (const bool state = true) const { // nepoužito
if (state) GPIOX->BSRRL = bitpos;
else GPIOX->BSRRH = bitpos;
}
private:
const unsigned bitpos;
};
static inline void Delay (volatile unsigned int tick) {
while (tick--);
}
// main() není potřeba, voláme přímo toto
/*NAKED*/ void Reset_Handler (void) {
/* Instance GPIOCC je v tomto případě vytvořena na zásobníku z těchto důvodů :
* 1. při vyšší optimalizaci se data třídy stejně nepoužijí,
* 2. pokud by byla třída statická, bylo by nutné řešit volání statického konstruktoru,
* což není triviální a zbytečně by to komplikovalo tento jednoduchý příklad.
* (3. možností je vytvořit objekt na haldě, ale to je zcela mimo toto pojednání.)
*/
GPIOCC led (LEDPIN);
for (;;) {
led.toggle ();
}
}

a potřebná hlavička

#include <stdint.h>
#include <stddef.h>
#ifndef _IO_H
#define _IO_H
#define __IO volatile
#ifdef ARM_CM0
// Vyzobano z STM stm32f0xx.h
struct RCC_Type {
__IO uint32_t CR;
__IO uint32_t CFGR;
__IO uint32_t CIR;
__IO uint32_t APB2RSTR;
__IO uint32_t APB1RSTR;
__IO uint32_t AHBENR;
__IO uint32_t APB2ENR;
__IO uint32_t APB1ENR;
__IO uint32_t BDCR;
__IO uint32_t CSR;
__IO uint32_t AHBRSTR;
__IO uint32_t CFGR2;
__IO uint32_t CFGR3;
__IO uint32_t CR2;
} ;
struct GPIO_Type {
__IO uint32_t MODER;
__IO uint16_t OTYPER;
uint16_t RESERVED0;
__IO uint32_t OSPEEDR;
__IO uint32_t PUPDR;
__IO uint16_t IDR;
uint16_t RESERVED1;
__IO uint16_t ODR;
uint16_t RESERVED2;
union { // Kompatibilita s F4 zavede trochu chaos, ale funkční - původně jeden 32.bit registr
__IO uint32_t BSRR;
struct { // Jde takto rozdělit (jako v F4) na dva 16. bitové registry
__IO uint16_t BSRRL;
__IO uint16_t BSRRH;
};
};
__IO uint32_t LCKR;
__IO uint32_t AFR[2];
__IO uint16_t BRR;
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 {
/****** Cortex-M0 Processor Exceptions Numbers **************************************************************/
SVC_IRQn = -5,
PendSV_IRQn = -2,
SysTick_IRQn = -1,
/****** STM32F051x4/STM32F051x6/STM32F051x8 specific Interrupt Numbers **************************************/
WWDG_IRQn = 0,
PVD_IRQn = 1,
RTC_IRQn = 2,
FLASH_IRQn = 3,
RCC_IRQn = 4,
TSC_IRQn = 8,
TIM1_CC_IRQn = 14,
TIM2_IRQn = 15,
TIM3_IRQn = 16,
TIM14_IRQn = 19,
TIM15_IRQn = 20,
TIM16_IRQn = 21,
TIM17_IRQn = 22,
I2C1_IRQn = 23,
I2C2_IRQn = 24,
SPI1_IRQn = 25,
SPI2_IRQn = 26,
USART1_IRQn = 27,
USART2_IRQn = 28,
#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
// Vyzobano z STM stm32f4xx.h
// RCC
struct RCC_Type {
__IO uint32_t CR;
__IO uint32_t PLLCFGR;
__IO uint32_t CFGR;
__IO uint32_t CIR;
__IO uint32_t AHB1RSTR;
__IO uint32_t AHB2RSTR;
__IO uint32_t AHB3RSTR;
uint32_t RESERVED0;
__IO uint32_t APB1RSTR;
__IO uint32_t APB2RSTR;
uint32_t RESERVED1[2];
__IO uint32_t AHBENR;
__IO uint32_t AHB2ENR;
__IO uint32_t AHB3ENR;
uint32_t RESERVED2;
__IO uint32_t APB1ENR;
__IO uint32_t APB2ENR;
uint32_t RESERVED3[2];
__IO uint32_t AHB1LPENR;
__IO uint32_t AHB2LPENR;
__IO uint32_t AHB3LPENR;
uint32_t RESERVED4;
__IO uint32_t APB1LPENR;
__IO uint32_t APB2LPENR;
uint32_t RESERVED5[2];
__IO uint32_t BDCR;
__IO uint32_t CSR;
uint32_t RESERVED6[2];
__IO uint32_t SSCGR;
__IO uint32_t PLLI2SCFGR;
} ;
// GPIO
struct GPIO_Type {
__IO uint32_t MODER;
__IO uint32_t OTYPER;
__IO uint32_t OSPEEDR;
__IO uint32_t PUPDR;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint16_t BSRRL;
__IO uint16_t BSRRH;
__IO uint32_t LCKR;
__IO uint32_t AFR[2];
} ;
#define PERIPH_BASE ((uint32_t)0x40000000)
#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000)
#define RCC_BASE (AHB1PERIPH_BASE + 0x3800)
#define GPIOD_BASE (AHB1PERIPH_BASE + 0x0C00)
// board specific
#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 {
/****** Cortex-M4 Processor Exceptions Numbers ****************************************************************/
MemoryManagement_IRQn = -12,
BusFault_IRQn = -11,
UsageFault_IRQn = -10,
SVCall_IRQn = -5,
DebugMonitor_IRQn = -4,
PendSV_IRQn = -2,
SysTick_IRQn = -1,
/****** STM32 specific Interrupt Numbers **********************************************************************/
WWDG_IRQn = 0,
PVD_IRQn = 1,
TAMP_STAMP_IRQn = 2,
RTC_WKUP_IRQn = 3,
FLASH_IRQn = 4,
RCC_IRQn = 5,
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,
TIM1_CC_IRQn = 27,
TIM2_IRQn = 28,
TIM3_IRQn = 29,
TIM4_IRQn = 30,
I2C1_EV_IRQn = 31,
I2C1_ER_IRQn = 32,
I2C2_EV_IRQn = 33,
I2C2_ER_IRQn = 34,
SPI1_IRQn = 35,
SPI2_IRQn = 36,
USART1_IRQn = 37,
USART2_IRQn = 38,
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
typedef struct RCC_Type RCC_TypeDef;
typedef struct GPIO_Type GPIO_TypeDef;
// Funkce obsluhující vektor reset je bez návratu, nemusí tedy nic uklízet na zásobník.
// Proto ji můžeme definovat jako naked, pro případné zkrácení kódu. Není to však nutné (alespoň pro gcc).
#define NAKED __attribute__((naked))
// Naplň část ram definovaným obsahem - debug stacku.
static inline void fillram (void) {
__ASM volatile (
"ldr r0, =0x20000800\n\t" // od
"ldr r1, =0x20001800\n\t" // do
"ldr r2, =0xDEADBEEF\n" // obsah
"floop:\n\t"
"str r2, [r0, #0]\n\t"
"add r0, r0, #4\n\t"
"cmp r0, r1\n\t"
"bne floop\n\t"
// "bkpt 0\n"
);
}
#endif// _IO_H

Celkový kód v Assembleru.

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 */

C-čkový kód pro blikání pomocí systémového časovače.

#include "io.h"
#include "dbgmcu.h"
extern uint32_t _vect_tab;
static RCC_TypeDef * const RCC = (RCC_TypeDef *) RCC_BASE;
static GPIO_TypeDef * const GPIOX = (GPIO_TypeDef *) LEDPORT_BASE;
extern void Reset_Handler (void);
static inline void pinGpioCoutInit (const unsigned int pin) {
RCC ->AHBENR |= LEDCLK_GPIOEN; // zavést hodiny
GPIOX->MODER |= (1U << 2*pin); // a nastavit jako výstup
}
static inline void pinGpioCtoggle (const unsigned int pin) {
GPIOX->ODR ^= (1U << pin); // změnit výstup
}
const unsigned int LED = LEDPIN;
// Obsluha přerušení od systémového časovače SysTick
void SysTick_Handler (void) {
pinGpioCtoggle (LED); // Jen změní stav LED, blikáme tedy s frekvencí 1 Hz
}
/*NAKED*/ void Reset_Handler (void) {
// fillram();
#ifdef ARM_CM4
EnableDebugOnSleep (); // Pokud použiji na F4 wfi, pak je dobré povolit debug v tt. módu
SCB->VTOR = (uint32_t) &_vect_tab; // Možná zbytečné, ale funkční - umístění vektorů pro F4
#endif // ARM_CM4
SysTick_Config (DELAY_SYST);
for (;;) { // Pro zmenšení spotřeby můžeme uP uspat,
__WFI (); // probudí se při přerušení od SysTick.
}
}

Makefile.

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

Linker skript.

/* 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) }
}

Dokumentace funkcí

static void Delay ( volatile unsigned int  tick)
inlinestatic

Zpoždění - poněkud špatně definované, ale stačí

Parametry
tick- počet průchodů (délka průchodu je právě ne příliš dobře definována)
static void pinGpioCoutInit ( const unsigned int  pin)
inlinestatic

Inicializace pinu na portu C jako výstupu

Parametry
pinPin na portu C
static void pinGpioCtoggle ( const unsigned int  pin)
inlinestatic

Manipulace s výstupem - změna log. hodnoty na pinu.

Parametry
pinPin na portu C
void Reset_Handler ( void  )

Místo main() přímo tt. funkce.

Obsluha vektoru Reset.

main() není potřeba

Dokumentace proměnných

RCC_TypeDef* const RCC = (RCC_TypeDef *) RCC_BASE
static

Základní IO použité v programu. Nejsou DEFINE, const je čitší, při optimalizaci stejně místo v paměti nezaberou.