Programming STM32F10x I/O port pins

Previously we learned how to compile STM32VL Discovery projects that were included in the package. But to understand how to write own programs we need to get to some basics. I think the best place to start is the input and output system (I/O). Before we begin to write some code lets go through whats inside STM32 ports. If you look into STM32 reference manual, you’ll find that I/O system is pretty flexible. Port pins can 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… 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 the 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), an 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 to set one bit in port you don’t have to read-modify-write port value. This is a dangerous situation where interrupt may occur in the middle of the 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 the last thing before we can start programming I/O ports is clock source. Peripherals like GPIO, USART, timers, ADC, and others 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 essential to configure bus.

Let us 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 the 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 a significant influence comparing to what you get. Some hobbyists tend to write code by accessing hardware directly – it is excellent if you want to dig into device 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 set up our project with Standard Peripheral Library included. We can use the same project template that we prepared in the last tutorial. Just we are going to omit

STM32vldiscovery.h

device library that was designed especially for discovery board.

In our program, we will read button status. If the 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 the 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