STM32 Primer - Use the Standard Peripheral Library

As I mentioned in the last section we need to do a bit more work to the infrastructure to make it more suitable for more realistic application development. While still making the same LED flashing, in this section I'll:

The files involved in this section are:

This time we look at the linker script first:

/***************************************
 * stm32 timer led example timer_led.ld
 ***************************************/

/* Memory layout for an STM32F103RB */
MEMORY
{
   FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K
   RAM  (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
}

STACK_SIZE = 1024;
_estack = ORIGIN(RAM) + LENGTH(RAM);
_sstack = _estack - STACK_SIZE;

/* Output sections */
SECTIONS
{
	.text :
	{
		/* All unused bytes set to 0xFF. */
		FILL (0xff)

		/* ISR Vector Table first */
		_isr_vectors_offs = . - 0x08000000;
		KEEP(*(.isr_vector))

		. = ALIGN(4);
        *(.text)		/* Program code */
        *(.text.*)
		*(.rodata)		/* Constants */
		*(.fini)	
		*(.rodata*)	
		*(.glue_7)	
		*(.glue_7t)	
		. = ALIGN(4);
		_etext = .;

		_sidata = _etext;
		
	} > FLASH

	/* Initialized global and static variables */
	.data :
	{
		FILL (0xff)
		
		. = ALIGN(4);
		_sdata = .;		
		*(.data)
		*(.data.*)
		. = ALIGN(4);
		_edata = .;
		
	} > RAM AT> FLASH

	/* Uninitialized global and static variables */
	.bss :
	{
		. = ALIGN(4);
		_sbss = .;
		*(.bss)
		*(.bss.*)
		*(COMMON)
		. = ALIGN(4);

		_ebss = .;
		
	} > RAM

	/* Stack */
	. = _sstack;
	.stack :
	{
		. = . + STACK_SIZE;
	} > RAM
}

Compared to last section, this linker script has more stuff in it. It now specifies the stack to be 1 Kbytes. It also has separate sections for initialized and uninitialized global and static variables. Before application starts execution, the initialized data in section '.data' will be copied from Flash to RAM, wherease RAM required by the the uninitialized data in section '.bss' will be allocated and zeroed - in the startup file:

/************************************
 * stm32 timer led example startup.c
 ************************************/

#include "stm32f10x.h"

typedef void( *const intfunc )( void );

#define WEAK __attribute__ ((weak))

extern unsigned long _etext;
extern unsigned long _sidata;
extern unsigned long _sdata;
extern unsigned long _edata;
extern unsigned long _sbss;
extern unsigned long _ebss;
extern unsigned long _estack;
 

void Reset_Handler(void) __attribute__((__interrupt__));
void __Init_Data(void);
void SystemInit (void);
void Default_Handler(void);

extern int main(void);

/* Init Data
 * Loads data from addresses defined in linker file into RAM
 * Zero bss (statically allocated uninitialized variables)
 */
void __Init_Data(void) {
	unsigned long *src, *dst;
	/* copy the data segment into ram */
	src = &_sidata;
	dst = &_sdata;
	if (src != dst)
		while(dst < &_edata)
			*(dst++) = *(src++);

	/* zero the bss segment */
	dst = &_sbss;
	while(dst < &_ebss)
		*(dst++) = 0;
}

/* This function is straight from the system_stm32f10x.c library file and
 * is called within the startup file:
 * 1. After each device reset the HSI is used as System clock source.
 * 2. This function assumes that an external 8MHz crystal is used to drive the System clock.
 */
void SystemInit (void)
{
	/* Reset the RCC clock configuration to the default reset state */
	/* Set HSION bit */
	RCC->CR |= (uint32_t)0x00000001;
	/* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
	RCC->CFGR &= (uint32_t)0xF8FF0000;
	/* Reset HSEON, CSSON and PLLON bits */
	RCC->CR &= (uint32_t)0xFEF6FFFF;
	/* Reset HSEBYP bit */
	RCC->CR &= (uint32_t)0xFFFBFFFF;
	/* Disable all interrupts and clear pending bits  */
	RCC->CIR = 0x009F0000;
	/* Enable HSE */
	RCC->CR |= ((uint32_t)RCC_CR_HSEON);
	/* Wait till HSE is ready */
	do
	{
	} while ((RCC->CR & RCC_CR_HSERDY) == RESET);
	/* Configure the Flash Latency cycles and enable prefetch buffer */
	FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY_2;
	/* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
	/* HCLK = SYSCLK, PCLK2 = HCLK, PCLK1 = HCLK / 2
	 * If crystal is 16MHz, add in PLLXTPRE flag to prescale by 2
     */
    RCC->CFGR = (uint32_t)( RCC_CFGR_HPRE_DIV1  |
                            RCC_CFGR_PPRE2_DIV1 |
                            RCC_CFGR_PPRE1_DIV2 |
                            RCC_CFGR_PLLSRC_HSE |
                            RCC_CFGR_PLLMULL9 );
	/* Enable PLL */
	RCC->CR |= RCC_CR_PLLON;
    /* Wait till PLL is ready */
    while ((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }
	/* Select PLL as system clock source */
	RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
    /* Wait till PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
    {
    }
}

void Reset_Handler(void) {
	/* Initialize data and bss */
	__Init_Data();
	extern uint32_t _isr_vectors_offs;
	SCB->VTOR = 0x08000000 | ((uint32_t)&_isr_vectors_offs & (uint32_t)0x1FFFFF80);
	SystemInit();
	while(1)
	{
	  __Init_Data();
	  main();
	}
	while(1) {}
}

void WEAK NMI_Handler(void);
void WEAK HardFault_Handler(void);
void WEAK MemManage_Handler(void);
void WEAK BusFault_Handler(void);
void WEAK UsageFault_Handler(void);
void WEAK MemManage_Handler(void);
void WEAK SVC_Handler(void);
void WEAK DebugMon_Handler(void);
void WEAK PendSV_Handler(void);
void WEAK SysTick_Handler(void);

void WEAK WWDG_IRQHandler(void);
void WEAK PVD_IRQHandler(void);
void WEAK TAMPER_IRQHandler(void);
void WEAK RTC_IRQHandler(void);
void WEAK FLASH_IRQHandler(void);
void WEAK RCC_IRQHandler(void);
void WEAK EXTI0_IRQHandler(void);
void WEAK EXTI1_IRQHandler(void);
void WEAK EXTI2_IRQHandler(void);
void WEAK EXTI3_IRQHandler(void);
void WEAK EXTI4_IRQHandler(void);
void WEAK DMA1_Channel1_IRQHandler(void);
void WEAK DMA1_Channel2_IRQHandler(void);
void WEAK DMA1_Channel3_IRQHandler(void);
void WEAK DMA1_Channel4_IRQHandler(void);
void WEAK DMA1_Channel5_IRQHandler(void);
void WEAK DMA1_Channel6_IRQHandler(void);
void WEAK DMA1_Channel7_IRQHandler(void);
void WEAK ADC1_2_IRQHandler(void);
void WEAK USB_HP_CAN1_TX_IRQHandler(void);
void WEAK USB_LP_CAN1_RX0_IRQHandler(void);
void WEAK CAN1_RX1_IRQHandler(void);
void WEAK CAN1_SCE_IRQHandler(void);
void WEAK EXTI9_5_IRQHandler(void);
void WEAK TIM1_BRK_IRQHandler(void);
void WEAK TIM1_UP_IRQHandler(void);
void WEAK TIM1_TRG_COM_IRQHandler(void);
void WEAK TIM1_CC_IRQHandler(void);
void WEAK TIM2_IRQHandler(void);
void WEAK TIM3_IRQHandler(void);
void WEAK TIM4_IRQHandler(void);
void WEAK I2C1_EV_IRQHandler(void);
void WEAK I2C1_ER_IRQHandler(void);
void WEAK I2C2_EV_IRQHandler(void);
void WEAK I2C2_ER_IRQHandler(void);
void WEAK SPI1_IRQHandler(void);
void WEAK SPI2_IRQHandler(void);
void WEAK USART1_IRQHandler(void);
void WEAK USART2_IRQHandler(void);
void WEAK USART3_IRQHandler(void);
void WEAK EXTI15_10_IRQHandler(void);
void WEAK RTCAlarm_IRQHandler(void);
void WEAK USBWakeUp_IRQHandler(void);
void WEAK TIM8_BRK_IRQHandler(void);
void WEAK TIM8_UP_IRQHandler(void);
void WEAK TIM8_TRG_COM_IRQHandler(void);
void WEAK TIM8_CC_IRQHandler(void);
void WEAK ADC3_IRQHandler(void);
void WEAK FSMC_IRQHandler(void);
void WEAK SDIO_IRQHandler(void);
void WEAK TIM5_IRQHandler(void);
void WEAK SPI3_IRQHandler(void);
void WEAK UART4_IRQHandler(void);
void WEAK UART5_IRQHandler(void);
void WEAK TIM6_IRQHandler(void);
void WEAK TIM7_IRQHandler(void);
void WEAK DMA2_Channel1_IRQHandler(void);
void WEAK DMA2_Channel2_IRQHandler(void);
void WEAK DMA2_Channel3_IRQHandler(void);
void WEAK DMA2_Channel4_5_IRQHandler(void);

__attribute__ ((section(".isr_vector")))
void (* const g_pfnVectors[])(void) = {
    (intfunc)((unsigned long)&_estack), /* The stack pointer after relocation */
    Reset_Handler,              /* Reset Handler */
    NMI_Handler,                /* NMI Handler */
    HardFault_Handler,          /* Hard Fault Handler */
    MemManage_Handler,          /* MPU Fault Handler */
    BusFault_Handler,           /* Bus Fault Handler */
    UsageFault_Handler,         /* Usage Fault Handler */
    0,                          /* Reserved */
    0,                          /* Reserved */
    0,                          /* Reserved */
    0,                          /* Reserved */
    SVC_Handler,                /* SVCall Handler */
    DebugMon_Handler,           /* Debug Monitor Handler */
    0,                          /* Reserved */
    PendSV_Handler,             /* PendSV Handler */
    SysTick_Handler,            /* SysTick Handler */

    /* External Interrupts */
    WWDG_IRQHandler,            /* Window Watchdog */
    PVD_IRQHandler,             /* PVD through EXTI Line detect */
    TAMPER_IRQHandler,          /* Tamper */
    RTC_IRQHandler,             /* RTC */
    FLASH_IRQHandler,           /* Flash */
    RCC_IRQHandler,             /* RCC */
    EXTI0_IRQHandler,           /* EXTI Line 0 */
    EXTI1_IRQHandler,           /* EXTI Line 1 */
    EXTI2_IRQHandler,           /* EXTI Line 2 */
    EXTI3_IRQHandler,           /* EXTI Line 3 */
    EXTI4_IRQHandler,           /* EXTI Line 4 */
    DMA1_Channel1_IRQHandler,   /* DMA1 Channel 1 */
    DMA1_Channel2_IRQHandler,   /* DMA1 Channel 2 */
    DMA1_Channel3_IRQHandler,   /* DMA1 Channel 3 */
    DMA1_Channel4_IRQHandler,   /* DMA1 Channel 4 */
    DMA1_Channel5_IRQHandler,   /* DMA1 Channel 5 */
    DMA1_Channel6_IRQHandler,   /* DMA1 Channel 6 */
    DMA1_Channel7_IRQHandler,   /* DMA1 Channel 7 */
    ADC1_2_IRQHandler,          /* ADC1 & ADC2 */
    USB_HP_CAN1_TX_IRQHandler,  /* USB High Priority or CAN1 TX */
    USB_LP_CAN1_RX0_IRQHandler, /* USB Low  Priority or CAN1 RX0 */
    CAN1_RX1_IRQHandler,        /* CAN1 RX1 */
    CAN1_SCE_IRQHandler,        /* CAN1 SCE */
    EXTI9_5_IRQHandler,         /* EXTI Line 9..5 */
    TIM1_BRK_IRQHandler,        /* TIM1 Break */
    TIM1_UP_IRQHandler,         /* TIM1 Update */
    TIM1_TRG_COM_IRQHandler,    /* TIM1 Trigger and Commutation */
    TIM1_CC_IRQHandler,         /* TIM1 Capture Compare */
    TIM2_IRQHandler,            /* TIM2 */
    TIM3_IRQHandler,            /* TIM3 */
    TIM4_IRQHandler,            /* TIM4 */
    I2C1_EV_IRQHandler,         /* I2C1 Event */
    I2C1_ER_IRQHandler,         /* I2C1 Error */
    I2C2_EV_IRQHandler,         /* I2C2 Event */
    I2C2_ER_IRQHandler,         /* I2C2 Error */
    SPI1_IRQHandler,            /* SPI1 */
    SPI2_IRQHandler,            /* SPI2 */
    USART1_IRQHandler,          /* USART1 */
    USART2_IRQHandler,          /* USART2 */
    USART3_IRQHandler,          /* USART3 */
    EXTI15_10_IRQHandler,       /* EXTI Line 15..10 */
    RTCAlarm_IRQHandler,        /* RTC Alarm through EXTI Line */
    USBWakeUp_IRQHandler,       /* USB Wakeup from suspend */
    TIM8_BRK_IRQHandler,
    TIM8_UP_IRQHandler,
    TIM8_TRG_COM_IRQHandler,
    TIM8_CC_IRQHandler,
    ADC3_IRQHandler,
    FSMC_IRQHandler,
    SDIO_IRQHandler,
    TIM5_IRQHandler,
    SPI3_IRQHandler,
    UART4_IRQHandler,
    UART5_IRQHandler,
    TIM6_IRQHandler,
    TIM7_IRQHandler,
    DMA2_Channel1_IRQHandler,
    DMA2_Channel2_IRQHandler,
    DMA2_Channel3_IRQHandler,
    DMA2_Channel4_5_IRQHandler,
};

#pragma weak MMI_Handler				= Default_Handler
#pragma weak MemManage_Handler			= Default_Handler
#pragma weak BusFault_Handler			= Default_Handler
#pragma weak UsageFault_Handler			= Default_Handler
#pragma weak SVC_Handler				= Default_Handler
#pragma weak DebugMon_Handler			= Default_Handler
#pragma weak PendSV_Handler				= Default_Handler
#pragma weak SysTick_Handler			= Default_Handler
#pragma weak WWDG_IRQHandler			= Default_Handler
#pragma weak PVD_IRQHandler				= Default_Handler
#pragma weak TAMPER_IRQHandler			= Default_Handler
#pragma weak RTC_IRQHandler				= Default_Handler
#pragma weak FLASH_IRQHandler			= Default_Handler
#pragma weak RCC_IRQHandler				= Default_Handler
#pragma weak EXTI0_IRQHandler			= Default_Handler
#pragma weak EXTI1_IRQHandler			= Default_Handler
#pragma weak EXTI2_IRQHandler			= Default_Handler
#pragma weak EXTI3_IRQHandler			= Default_Handler
#pragma weak EXTI4_IRQHandler			= Default_Handler
#pragma weak DMA1_Channel1_IRQHandler	= Default_Handler
#pragma weak DMA1_Channel2_IRQHandler	= Default_Handler
#pragma weak DMA1_Channel3_IRQHandler	= Default_Handler
#pragma weak DMA1_Channel4_IRQHandler	= Default_Handler
#pragma weak DMA1_Channel5_IRQHandler	= Default_Handler
#pragma weak DMA1_Channel6_IRQHandler	= Default_Handler
#pragma weak DMA1_Channel7_IRQHandler	= Default_Handler
#pragma weak ADC1_2_IRQHandler			= Default_Handler
#pragma weak USB_HP_CAN1_TX_IRQHandler	= Default_Handler
#pragma weak USB_LP_CAN1_RX0_IRQHandler	= Default_Handler
#pragma weak CAN1_RX1_IRQHandler		= Default_Handler
#pragma weak CAN1_SCE_IRQHandler		= Default_Handler
#pragma weak EXTI9_5_IRQHandler			= Default_Handler
#pragma weak TIM1_BRK_IRQHandler		= Default_Handler
#pragma weak TIM1_UP_IRQHandler			= Default_Handler
#pragma weak TIM1_TRG_COM_IRQHandler	= Default_Handler
#pragma weak TIM1_CC_IRQHandler			= Default_Handler
#pragma weak TIM2_IRQHandler			= Default_Handler
#pragma weak TIM3_IRQHandler			= Default_Handler
#pragma weak TIM4_IRQHandler			= Default_Handler
#pragma weak I2C1_EV_IRQHandler			= Default_Handler
#pragma weak I2C1_ER_IRQHandler			= Default_Handler
#pragma weak I2C2_EV_IRQHandler			= Default_Handler
#pragma weak I2C2_ER_IRQHandler			= Default_Handler
#pragma weak SPI1_IRQHandler			= Default_Handler
#pragma weak SPI2_IRQHandler			= Default_Handler
#pragma weak USART1_IRQHandler			= Default_Handler
#pragma weak USART2_IRQHandler			= Default_Handler
#pragma weak USART3_IRQHandler			= Default_Handler
#pragma weak EXTI15_10_IRQHandler		= Default_Handler
#pragma weak RTCAlarm_IRQHandler		= Default_Handler
#pragma weak USBWakeUp_IRQHandler		= Default_Handler
#pragma weak TIM8_BRK_IRQHandler		= Default_Handler
#pragma weak TIM8_UP_IRQHandler			= Default_Handler
#pragma weak TIM8_TRG_COM_IRQHandler	= Default_Handler
#pragma weak TIM8_CC_IRQHandler			= Default_Handler
#pragma weak ADC3_IRQHandler			= Default_Handler
#pragma weak FSMC_IRQHandler			= Default_Handler
#pragma weak SDIO_IRQHandler			= Default_Handler
#pragma weak TIM5_IRQHandler			= Default_Handler
#pragma weak SPI3_IRQHandler			= Default_Handler
#pragma weak UART4_IRQHandler			= Default_Handler
#pragma weak UART5_IRQHandler			= Default_Handler
#pragma weak TIM6_IRQHandler			= Default_Handler
#pragma weak TIM7_IRQHandler			= Default_Handler
#pragma weak DMA2_Channel1_IRQHandler	= Default_Handler
#pragma weak DMA2_Channel2_IRQHandler	= Default_Handler
#pragma weak DMA2_Channel3_IRQHandler	= Default_Handler
#pragma weak DMA2_Channel4_5_IRQHandler	= Default_Handler

void Default_Handler(void) {
	while (1) {}
}

The startup file is actually not as complicated as it looks. Basically it does the following:

Note also that I've included the header 'stm32f10x.h' for all those register name definitions.

The 'openocd.cfg' file and the 'write_bin.sh' file are the same as the previous section so I won't list them here. The Makefile, of course, is different because we have two C files now:

####################################
# stm32 timer led example Makefile
####################################

PROGRAM = timer_led

ENTRY_FILE = startup

ENTRY_CFILE = $(ENTRY_FILE).c
ENTRY_OFILE = $(ENTRY_FILE).o

ALL_OFILES = main.o $(ENTRY_OFILE)

TARGET_BIN = $(PROGRAM).bin
TARGET_ELF = $(PROGRAM).elf

CC = arm-none-eabi-gcc
LD = arm-none-eabi-ld
CP = arm-none-eabi-objcopy

LKR_SCRIPT = $(PROGRAM).ld

FAMILY = STM32F10X_MD

DEFINES = -D$(FAMILY)
INCLUDES = -I./Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/ \
			-I./Libraries/CMSIS/CM3/CoreSupport/ \
			-I./Libraries/STM32F10x_StdPeriph_Driver/inc

CFLAGS  = -c -march=armv7-m -mcpu=cortex-m3 -mthumb \
          -fno-common -nostdlib -fno-builtin -ffreestanding \
          -Wall -O0 -g  \
          $(DEFINES) 
          
LFLAGS  = -nostartfiles -T$(LKR_SCRIPT)
CPFLAGS = -Obinary

.PHONY: all clean write

all: $(TARGET_BIN)

$(TARGET_BIN): $(TARGET_ELF)
	$(CP) $(CPFLAGS) $< $@
	
$(TARGET_ELF): $(ALL_OFILES)
	$(LD) $(LFLAGS) $(ALL_OFILES) -o $@ 
	
main.o: main.c
	$(CC) $(INCLUDES) $(CFLAGS) main.c -o $@

$(ENTRY_OFILE): $(ENTRY_CFILE)
	$(CC) $(INCLUDES) $(CFLAGS) $(ENTRY_CFILE) -o $@

clean:
	rm -rf *.o *.elf *.bin

write: 
	./write_bin.sh openocd.cfg $(TARGET_ELF)

Also note that we need to specify the inclusion path for the library files in the compiler options. Finally, here's the main application code:

/*********************************
 * stm32 timer led example main.c
 ********************************/

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_tim.h"

/* User defined function prototypes */
void GPIOA_Init(void);
void TIM2_CC1_Init(void);
void led_toggle(void);

/* Prototypes for func. from stm32f10x lib. */
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);

int main(void)
{
	/* Initialize GPIOA PIN8 */
	GPIOA_Init();
	
	/* Initialize TIM2 Capture/Compare 1 in output compare mode */
	TIM2_CC1_Init();
	
	/* Toggle LED forever */
	while(1)
	{
		/* Do nothing, all happens in ISR */
	}
}	

/* Initialize GPIOA PIN8 */
void GPIOA_Init(void)
{
	/* Configuration info. for PORTA PIN8:
	 * - Speed = 50MHz
	 * - Push-pull output mode 
	 */
	GPIO_InitTypeDef gpioa_pin8_config = { GPIO_Pin_8, 
										   GPIO_Speed_50MHz, 
										   GPIO_Mode_Out_PP };
										   	
	/* Enable PORT A clock */
	RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;	
	/* Configure PORTA PIN 8 */
	GPIO_Init(GPIOA, &gpioa_pin8_config);	
	
	/* Turn off LED to start with */
	GPIOA->BSRR = (uint32_t)1 << 8;	
}

/* Toggle LED */
void led_toggle(void)
{
	/* If PORTA BIT 8 clear, set it */
	if((GPIOA->ODR & (uint32_t)1<<8) == 0)
	{
		GPIOA->BSRR = (uint32_t)1 << 8;
	}
	/* If PORTA BIT 8 set, clear it */
	else
	{
		GPIOA->BRR = (uint32_t)1 << 8;
	}
}

/* Configure TIM2 Capture/Compare 1 to work in output compare mode 
 * so that the red LED flashes at 1HZ (toggle every 0.5s) */
void TIM2_CC1_Init(void)
{
	/* Enable TIM2 clock */
	RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
	/* Enable TIM2 global interrupt */
	NVIC->ISER[0] |= 0x10000000;
	
	/* Frequency after prescalar = 72MHz/(7199+1) = 10KHz.
	 * Compare register = 5000 so a compare match occurs every 0.5s.
	 */
	TIM2->PSC = (uint16_t)7199;
	TIM2->CCR1 = (uint16_t)5000;
	
	/* Enable Capture/Compare 1 interrupt */
	TIM2->DIER |= (uint16_t)0x0002;
		
	/* Enable TIM2 counter (in upcount mode) */
	TIM2->CR1 |= (uint16_t)0x0001;	
}

/* Timer 2 Interrupt Service Routine */
void TIM2_IRQHandler(void)
{
	/* Toggle LED if TIM2's Capture/Compare 1 interrupt occurred. */
	if(TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)
	{
		led_toggle();
		/* Clear TIM2's Capture/Compare 1 interrupt pending bit. */
		TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
		/* Increment compare register by 5000 so next interrupt
		 * occurs in 0.5s */
		TIM2->CCR1 += (uint16_t)5000;
	}	
	
	/* ===== Other TIM2 interrupt types can go below ======
	 * .........
	 */
}

/* This function comes straight from stm32f10x_gpio.c. All it does is
 * configure a GPIO pin's speed and mode etc. but seems so complicated.
 */
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
  uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
  uint32_t tmpreg = 0x00, pinmask = 0x00;
  /* Check the parameters */
  /*
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
  assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));  
  */
  
/*---------------------------- GPIO Mode Configuration -----------------------*/
  currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
  if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
  { 
    /* Check the parameters */
    /* assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed)); */
    /* Output mode */
    currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
  }
/*---------------------------- GPIO CRL Configuration ------------------------*/
  /* Configure the eight low port pins */
  if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
  {
    tmpreg = GPIOx->CRL;
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
      pos = ((uint32_t)0x01) << pinpos;
      /* Get the port pins position */
      currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
      if (currentpin == pos)
      {
        pos = pinpos << 2;
        /* Clear the corresponding low control register bits */
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
        /* Write the mode configuration in the corresponding bits */
        tmpreg |= (currentmode << pos);
        /* Reset the corresponding ODR bit */
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
          GPIOx->BRR = (((uint32_t)0x01) << pinpos);
        }
        else
        {
          /* Set the corresponding ODR bit */
          if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
          {
            GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
          }
        }
      }
    }
    GPIOx->CRL = tmpreg;
  }
/*---------------------------- GPIO CRH Configuration ------------------------*/
  /* Configure the eight high port pins */
  if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
  {
    tmpreg = GPIOx->CRH;
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
      pos = (((uint32_t)0x01) << (pinpos + 0x08));
      /* Get the port pins position */
      currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
      if (currentpin == pos)
      {
        pos = pinpos << 2;
        /* Clear the corresponding high control register bits */
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
        /* Write the mode configuration in the corresponding bits */
        tmpreg |= (currentmode << pos);
        /* Reset the corresponding ODR bit */
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
          GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
        /* Set the corresponding ODR bit */
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
        {
          GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
      }
    }
    GPIOx->CRH = tmpreg;
  }
}

/* This function comes straight from stm32f10x_tim.c. It checks 
 * whether interrupt TIM_IT in TIMx has occurred or not.
 */
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT)
{
  ITStatus bitstatus = RESET;  
  uint16_t itstatus = 0x0, itenable = 0x0;
  /* Check the parameters */
  //assert_param(IS_TIM_ALL_PERIPH(TIMx));
  //assert_param(IS_TIM_GET_IT(TIM_IT));
   
  itstatus = TIMx->SR & TIM_IT;
  
  itenable = TIMx->DIER & TIM_IT;
  if ((itstatus != (uint16_t)RESET) && (itenable != (uint16_t)RESET))
  {
    bitstatus = SET;
  }
  else
  {
    bitstatus = RESET;
  }
  return bitstatus;
}

/* This function comes straight from stm32f10x_tim.c.
 * It clears the TIMx's TIM_IT interrupt pending bits.
 */
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT)
{
  /* Check the parameters */
  //assert_param(IS_TIM_ALL_PERIPH(TIMx));
  //assert_param(IS_TIM_IT(TIM_IT));
  /* Clear the IT pending Bit */
  TIMx->SR = (uint16_t)~TIM_IT;
}

I have not compiled any library C files in this project. All the '#include' are just for peripheral register name definitions. Instead, I simply copy and pasted the functions I need for this application into the main file so that I could focus on what exactly is required to get the code working. To build and program:

me@pandafruits:~$ make
me@pandafruits:~$ make write

The LED should flash at exactly 1Hz now. I'll do one more example with the UART next.