The STM32 F4 Discovery board is currently our board of choice for demonstrations and training at Rapita Systems. Here I will explain why we chose it and demonstrate how to set the STM32 F4 Discovery up as an output port without using ST libraries.
Why we chose the STM32 F4 Discovery
It has a suitable amount of constraints to enable us to show how we get round some of the problems of on-target verification on embedded systems, without being too tricky and at a very agreeable price.
How we use the STM32 F4 Discovery
At Rapita, our main interest in writing to output ports of microcontrollers is to collect structural coverage or execution time data from code that has been instrumented by our RVS tools.
The RTBx provides an efficient method to collect RVS data, and as such we'll discuss how to set the STM32 F4 Discovery board up to write data suitable for collection by the RTBx.
We want to set up the output port so we have strong signals on output pins. Here is an example implementation for setting the first 8 pins of port B as outputs:
{ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_Init(GPIOB,&GPIO_InitStructure); }
This is fairly simple, but relies entirely on the firmware Libraries for the board, which can be downloaded from the ST website. So I'm going to explain what each part of this code snippet does and show you how to achieve the same thing manually without the ST libraries.
Setting the STM32 F4 Discovery up without using libraries
For power consumption efficiency the peripheral clock is gated for most peripherals on the device. So, the first call to RCC_AHB1PeriphClockCmd
enables the peripheral clock for GPIO on the B port. This sets bit 1 in the RCC AHB1 peripheral clock register (RCC_AHB1ENR
). To do this manually, you'd simply set
RCC_AHB1ENR |= 0x0002;
Next, we select which pins on that port we want to enable - in this case the full 8 bits for this port, pin 0 to 7. This does not actually have an equivalent register call - it simply defines which parts of the various registers we need to set later. As we're looking at the lower 8 pins on the port, we will either be setting the lower word or lower byte (depending on how wide the register settings are) for the set up explained below. We have the option of setting the IO mode to be input, output, alternative function or analogue. We're setting this to output, which is equivalent to this:
GPIOB_MODER &= 0xFFFF0000; /* clear the bits first - reset value is not 0x00000000 on Port B */ GPIOB_MODER |= 0x00005555;
The first 8 bits of the port are set using the lower word of the register control. Input is 00
in binary, output is 01
. Having set the pins to be output pins we now need to set the type of output and the speed they'll be driven at. This is done using the OTYPER
and OSPEEDR
registers. The output mode options available are push-pull or open drain. In our case we want the pin driven hard to get the cleanest edges possible so we've chosen Push-pull. This is equivalent to cleaning the bits for pin 0-7 like this:
GPIOB_OTYPER &= 0xFF00;
As we can potentially sample at 100 MHz, we want the output pins going as quickly as possible - although the STM32 board can be clocked at 168 MHz the pins seem to be limited to a maximum speed of 100 MHz (assuming 30pF). The reset value of this register is not zero, but as we want to set all the bits in the lower word there is no value in clearing those bits first:
GPIOB_OSPEEDR |= 0x0000FFFF;
Next, we set this output port to have an internal pull down:
GPIOB_PUPDR &= 0xFFFF0000; /* clear the bits first - reset value is not 0x00000000 on Port B */ GPIOB_PUPDR |= 0x0000AAAA;
Each pin has the following options in binary:
00 | no pull up or pull down |
01 | pull up |
10 | pull down |
Calling GPIO_Init
initializes the port with the options I've outlined above. With these registers correctly configured, outputting a value is as simple as:
GPIOB_ODR = output_value;