Last time we have made a good starting point with setting up a project template for the STM32F103ZET6 development board using GNU tools. Using the same project template, we can move forward and start programming other elements. This time a quick note about adding a button library. This is a modest implementation that initializes port pins and then reads their status.
The Development board is equipped with four user-programmable buttons named WAKEUP, TAMPER, USER1, and USER2. We will not care about the meaning of names; use them as general-purpose buttons for now.
For this, we need to create another pair of driver files buttons.c and buttons.h. In buttons.h file, we define button ports and pins with some friendly names. Also as well we have to think about clocking proper peripheral buses.
#define BWAKEUP GPIO_Pin_0
#define BWAKEUPPORT GPIOA
#define BWAKEUPPORTCLK RCC_APB2Periph_GPIOA
#define BTAMPER GPIO_Pin_13
#define BTAMPERPORT GPIOC
#define BTAMPERPORTCLK RCC_APB2Periph_GPIOC
#define BUSER1 GPIO_Pin_8
#define BUSER1PORT GPIOA
#define BUSER1PORTCLK RCC_APB2Periph_GPIOA
#define BUSER2 GPIO_Pin_3
#define BUSER2PORT GPIOD
#define BUSER2PORTCLK RCC_APB2Periph_GPIOD
Next step is button initialization. In buttons.c we create a function:
void ButtonsInit(void)
{
//GPIO structure used to initialize Button pins
GPIO_InitTypeDef GPIO_InitStructure;
//Enable clock on APB2 pripheral bus where buttons are connected
RCC_APB2PeriphClockCmd(BWAKEUPPORTCLK|BTAMPERPORTCLK|BUSER1PORTCLK|BUSER2PORTCLK, ENABLE);
//select pins to initialize WAKEUP and USER1 buttons
GPIO_InitStructure.GPIO_Pin = BWAKEUP|BUSER1;
//select floating input mode
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
//select GPIO speed
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(BWAKEUPPORT, &GPIO_InitStructure);
//select pins to initialize TAMPER button
GPIO_InitStructure.GPIO_Pin = BTAMPER;
GPIO_Init(BTAMPERPORT, &GPIO_InitStructure);
//select pins to initialize USER2 button
GPIO_InitStructure.GPIO_Pin = BUSER2;
GPIO_Init(BUSER2PORT, &GPIO_InitStructure);
}
Here we have to enable peripheral clocks for used ports with the RCC_APB2PeriphClockCmd command. Then we can start setting up port pins. According to the peripheral library, we need to do this through the structure variable where each field carries a particular setting. These would be GPIO_Pins (all pins set for the same port), then GPIO_Mode – we set it as input floating, and GPIO_Speed as max. Buttons aren’t connected to the same port, so we have to do initialization separately for each port with the GPIO_Init function.
Lastly, we write a simple function that reads a button state.
uint32_t ButtonRead(GPIO_TypeDef* Button_Port, uint16_t Button)
{
return !GPIO_ReadInputDataBit(Button_Port, Button);
}
When pressed, buttons pulls port pin low so GPIO_ReadInputDataBit() function returns ‘0’. Probably it is more logical to have ‘1’ on click, so we used the NOT ‘!’ operator to do so. And here is a simple demo code that lights the different LEDs on each button press. Code still runs in a super loop, so don’t judge code efficiency; it’s only for demonstration.
//Buttons Test
#include "stm32f10x.h"
#include "leds.h"
#include "buttons.h"
//delay function
void Delay(__IO uint32_t nCount)
{
for(; nCount != 0; nCount--);
}
int main(void)
{
//init leds
LEDsInit();
ButtonsInit();
while (1)
{
//read Wakeup button
if (ButtonRead(BWAKEUPPORT, BWAKEUP))
{
//led 1 on
LEDOn(1);
//delay
Delay(500000);
//led 1 off
LEDOff(1);
}
//read TAMPER button
if (ButtonRead(BTAMPERPORT, BTAMPER))
{
//led 2 on
LEDOn(2);
//delay
Delay(500000);
//led 2 off
LEDOff(2);
}
//read User1 button
if (ButtonRead(BUSER1PORT, BUSER1))
{
//led 3 on
LEDOn(3);
//delay
Delay(500000);
//led 3 off
LEDOff(3);
}
//read User2 button
if (ButtonRead(BUSER2PORT, BUSER2))
{
//led 4 on
LEDOn(4);
//delay
Delay(500000);
//led 4 off
LEDOff(4);
}
}
}
In real applications buttons, a check should be interrupt based. But we will get to that soon.