Programming STM32F10x I/O port pins

Previously we learned how to compile STM32VL Discovey projects that are included in package. But in order to understand how to write own programs we need to get to some basics. I think best place to start is input and output system (I/O). Before we begin to write some code lets go through whats in side STM32 ports. I you look in to STM32 reference manual you’ll find that I/O system is pretty flexible. Port pins are able to work in several modes:

  • Input floating;
  • Input pull-up;
  • Input pull-down;
  • Analog;
  • Output open drain;
  • Output push-pull;
  • Alternate function push-pull;
  • Alternate function open drain.

Pins are organized as 16 bit ports that have their names like PORTA, PORTB, PORC, PORTD… Despite the fact that ports are 16-bit wide they are controlled with 32-bit words. Individually each port pin can be configured to one of these functions additionally each pin maximum speed can be set to one of values: 2MHz, 10MHz and 50MHz. STM32 I/Os are 5V tolerant. Anyway proper design should use 5 to 3.3V level converters.

Each port has several special GPIO registers. These include two 32-wide configuration registers (GPIOx_CRL and GPIOx_CRH), input register (GPIOx_IDR), output register (GPIOx_ODR), also there are bit set/reset (GPIOx_BSRR) and reset (GPIOx_BRR) registers and configuration lock (GPIOx_LCKR) register. Where x represents port letter: A, B, C…

According to this, port output register can be written as word wide (16-bit) register but also it is possible atomic bit manipulation with set/reset and reset registers. So in order to set one bit in port you don’t have to read-modify-write port value. This is dangerous situation where interrupt may occur in the middle of event. And surely registers can also be bit manipulated with bit banding functionality. Lock register is convenient when you need to prevent configuration registers. Once they are locked registers cannot be modified until unlocked.

And last thing before we can start programming I/O ports is clock source. Peripherals like GPIO, USART, timers, ADC and other are connected to Advanced High Speed Bus (AHB) matrix through Advanced Peripheral Buses (APB). There are two peripheral buses APB1 and APB2. They have clock prescallers allowing to select different speeds. Microcontroller ports are connected to APB2 bus so before using ports it is important to configure bus.

Lets blink some LEDs

Having some theory we can start writing code. From now on we have to decide which direction we should go. One way is to manipulate MCU registers directly or use Cortex Microcontroller Software Interface Standard called CMSIS. CMSIS gives more abstraction when programming hardware and also makes code portable among different Cortex microcontrollers. Of course CMSIS takes some space and requires some resources but this isn’t significant influence comparing to what you get. Some hobbyists tend to write code by accessing hardware directly – it is great if you want to dig in to hardware and have control of every operation, but from my point of view this would be stupid not to use some abstraction and save some time.

As always first we have to wet up our project with Standard Peripheral Library included. We can use same project template that we prepared in last tutorial. Just we are going to omit

STM32vldiscovery.h

device library that was designed specially for discovery board.

In our program we will read button status. If button is pressed it simply will toggle blue LED while button press will be indicated with green LED.

// Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#define LEDG 			GPIO_Pin_9
#define LEDB 			GPIO_Pin_8
#define LEDPORT			GPIOC
#define LEDPORTCLK		RCC_APB2Periph_GPIOC
#define BUTTON			GPIO_Pin_0
#define BUTTONPORT		GPIOA
#define BUTTONPORTCLK	RCC_APB2Periph_GPIOA
//delay function
void Delay(__IO uint32_t nCount)
{
  for(; nCount != 0; nCount--);
}
int main(void)
{
  //flasher flag
  uint32_t ledon=0;
  //GPIO structure used to initialize port
  GPIO_InitTypeDef GPIO_InitStructure;
  //Enable clock on APB2 pripheral bus where button and LEDs are connected
  RCC_APB2PeriphClockCmd(LEDPORTCLK | BUTTONPORTCLK,  ENABLE);
  //select pins to initialize LED
  GPIO_InitStructure.GPIO_Pin = LEDG|LEDB;
  //select output push-pull mode
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  //highest speed available
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(LEDPORT, &GPIO_InitStructure);
  //using same structure we will initialize button pin
  //select pin to initialize button
  GPIO_InitStructure.GPIO_Pin = BUTTON;
  //select input floating
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(BUTTONPORT, &GPIO_InitStructure);
while (1)
  {
	//read button
	if (GPIO_ReadInputDataBit(BUTTONPORT, BUTTON))
	{
		//green led on
		GPIO_SetBits(LEDPORT, LEDG);
		//toggle flasher
		ledon ^= 1;
		//dummy debounce
		Delay(500000);
		//green led off
		GPIO_ResetBits(LEDPORT, LEDG);
	}
	if (ledon)
	{
		GPIO_SetBits(LEDPORT, LEDB);
	}
	else
	{
		GPIO_ResetBits(LEDPORT, LEDB);
	}
  }
}


To compile this project you need to include these libraries in stm32f10x_conf.h:

#include"stm32f10x_gpio.h"

#include"stm32f10x_rcc.h"

Also do same in Makefile. Download full CodeSourcery project here[~700KB].

Bookmark the permalink.

One Comment

  1. i have this

    usermain.c(49): error: #18: expected a “)”

Leave a Reply

Your email address will not be published. Required fields are marked *