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 our 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, let’s go through what’s inside STM32 ports. If you look into the STM32 reference manual, you’ll find that the 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’s maximum speed can be set to one of the values: 2MHz, 10MHz, and 50MHz. STM32 I/Os are 5V tolerant. Anyway, the 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, the port output register can be written as a 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 a port, you don’t have to read-modify-write port value. This is a dangerous situation where an interruption may occur in the middle of the event. And surely registers can also be bit manipulated with bit banding functionality. A 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 the 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 them to select different speeds. Microcontroller ports are connected to the APB2 bus, so before using ports, it is essential to configure the 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 the device and control 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 the button press will be indicated with a 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 the full CodeSourcery project here[~700KB].

One Comment:

  1. i have this

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

Comments are closed