STM32 Primer - UART Example

Here I'll do one more example with the micro's UART. Infrastructure wise it's pretty much the same as the previous timer LED example. The likner script, startup file, OpenOcd config. file and the write binary bash script are all the same. The only thing I did differently is that, instead of copy and pasting the library functions into my application file, I compiled the relevant library C files separately and left everything to the linker. So here is our Makefile:

################################
# stm32 uart example Makefile
################################

PROGRAM = uart

ENTRY_FILE = startup

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

RCC_CFILE = ../STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_rcc.c
RCC_OFILE = stm32f10x_rcc.o

GPIO_CFILE = ../STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_gpio.c
GPIO_OFILE = stm32f10x_gpio.o

USART_CFILE = ../STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_usart.c
USART_OFILE = stm32f10x_usart.o

ALL_OFILES = main.o $(ENTRY_OFILE) $(RCC_OFILE) $(GPIO_OFILE) $(USART_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)\
          -DUSE_STDPERIPH_DRIVER
          
INCLUDES = -I../STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/ \
		   -I../STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/CMSIS/CM3/CoreSupport/ \
		   -I../STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/STM32F10x_StdPeriph_Driver/inc/ \
		   -I../STM32F10x_StdPeriph_Lib_V3.5.0/Project/STM32F10x_StdPeriph_Template

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 $@
	
$(RCC_OFILE): $(RCC_CFILE)
	$(CC) $(INCLUDES) $(CFLAGS) $(RCC_CFILE) -o $@

$(GPIO_OFILE): $(GPIO_CFILE)
	$(CC) $(INCLUDES) $(CFLAGS) $(GPIO_CFILE) -o $@

$(USART_OFILE): $(USART_CFILE)
	$(CC) $(INCLUDES) $(CFLAGS) $(USART_CFILE) -o $@
	
clean:
	rm -rf *.o *.elf *.bin

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

As you can see, five object files linked to produce the final binary. Also note that I've put the peripheral library contents in a directory at the same level as the project directory.

What the application does is that, on reception of the character 't' at UART1, toggles the LED and transmit the character 'T'. So from my PC in a serial terminal I can press 't' to toggle the LED and get a 'T' echoed back. Here is the code:

/*************************
 * stm32 uart main.c
 *************************/

#include "stm32f10x.h"

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

int main(void)
{
	/* Initialize GPIOA PIN8 */
	GPIOA_Init();
	/* Initialize USART1 */
	USART1_Init();

	while(1)
	{
		/* Do nothing, all happens in ISR */
	}
}	

/***********************************************
 * Initialize GPIOA PIN8 as push-pull output
 ***********************************************/
void GPIOA_Init(void)
{
	/* Bit configuration structure for GPIOA PIN8 */
	GPIO_InitTypeDef gpioa_init_struct = { GPIO_Pin_8, GPIO_Speed_50MHz, 
										   GPIO_Mode_Out_PP };
										   	
	/* Enable PORT A clock */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	/* Initialize GPIOA: 50MHz, PIN8, Push-pull Output */
	GPIO_Init(GPIOA, &gpioa_init_struct);	
	
	/* Turn off LED to start with */
	GPIO_SetBits(GPIOA, GPIO_Pin_8);
}

/*****************************************************
 * Initialize USART1: enable interrupt on reception
 * of a character
 *****************************************************/
void USART1_Init(void)
{
	/* USART configuration structure for USART1 */
	USART_InitTypeDef usart1_init_struct;
	/* Bit configuration structure for GPIOA PIN9 and PIN10 */
	GPIO_InitTypeDef gpioa_init_struct;
	
	/* Enalbe clock for USART1, AFIO and GPIOA */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_AFIO | 
						   RCC_APB2Periph_GPIOA, ENABLE);
						   
	/* GPIOA PIN9 alternative function Tx */
	gpioa_init_struct.GPIO_Pin = GPIO_Pin_9;
	gpioa_init_struct.GPIO_Speed = GPIO_Speed_50MHz;
	gpioa_init_struct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_Init(GPIOA, &gpioa_init_struct);
	/* GPIOA PIN9 alternative function Rx */
	gpioa_init_struct.GPIO_Pin = GPIO_Pin_10;
	gpioa_init_struct.GPIO_Speed = GPIO_Speed_50MHz;
	gpioa_init_struct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  	GPIO_Init(GPIOA, &gpioa_init_struct);

 	/* Enable USART1 */
	USART_Cmd(USART1, ENABLE);	
	/* Baud rate 9600, 8-bit data, One stop bit
	 * No parity, Do both Rx and Tx, No HW flow control
	 */
	usart1_init_struct.USART_BaudRate = 9600;	
	usart1_init_struct.USART_WordLength = USART_WordLength_8b;	
	usart1_init_struct.USART_StopBits = USART_StopBits_1;	
	usart1_init_struct.USART_Parity = USART_Parity_No ;
	usart1_init_struct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	usart1_init_struct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	/* Configure USART1 */
	USART_Init(USART1, &usart1_init_struct);
	/* Enable RXNE interrupt */
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	/* Enable USART1 global interrupt */
	NVIC_EnableIRQ(USART1_IRQn);
}

/*******************************************
 * Toggle LED 
 *******************************************/
void led_toggle(void)
{
	/* Read LED output (GPIOA PIN8) status */ 
	uint8_t led_bit = GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_8);
	
	/* If LED output set, clear it */
	if(led_bit == (uint8_t)Bit_SET)
	{
		GPIO_ResetBits(GPIOA, GPIO_Pin_8);
	}
	/* If LED output clear, set it */
	else
	{
		GPIO_SetBits(GPIOA, GPIO_Pin_8);
	}
}

/**********************************************************
 * USART1 interrupt request handler: on reception of a 
 * character 't', toggle LED and transmit a character 'T'
 *********************************************************/
void USART1_IRQHandler(void)
{
	/* RXNE handler */
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
	{
		/* If received 't', toggle LED and transmit 'T' */
		if((char)USART_ReceiveData(USART1) == 't')
		{
			led_toggle();
			USART_SendData(USART1, 'T');
			/* Wait until Tx data register is empty, not really 
			 * required for this example but put in here anyway.
			 */
			/*
			while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)
			{
			}*/
		}
	}
	
	/* ------------------------------------------------------------ */
	/* Other USART1 interrupts handler can go here ...             */
}