GPIO STM32, alternative option

When I switched from atmega8 to stm32 in 2011, I was very inspired by the opencm3 project. But he inspired me not to study it, but to write something similar. Today, in my version of almost the library there are register macros for microcontrollers of the stm32f10x and stm32f40x, stm8s003, nrf51, nrf52, rp2040, and cc2640/1310 series. In reality, only stm32f103 from this list has been tested. In addition to registers for the 103rd, I wrote basic functions for turning on/off peripheral clocking and controlling I/O ports. Examples have also been written for USB profiles HID gamepad, HID keyboard and USB serial port. In the same post I will document the functions of ports and clocking.

It's about files stm32/rcc.c, stm32/gpio.c And stm32/delay.c my libraries for microcontrollers.

Peripheral clocking (rcc.h).

As you know, the periphery in STM32, as in all ARMs, is scattered over several buses: AHB, APB1 and APB2. Therefore, when you want to quickly write a sketch, it’s a little inconvenient to remember every time which bus the peripheral you need is located on. This stupid question is solved by a stupid switch-case. Now it is enough to remember the name of the peripheral (for example: DMA1, TIM2, ADC1, USART2, SPI1, IOPA, USB, I2C1) and pass it to the function enablePeriphClock(uint16_t periph) or resetPeriphClock(uint16_t periph) to enable or disable peripherals. The full list of peripheral names can be found there, in the header rcc.h in enum. The only inconvenience is that the file contains code for all 100 series microcontrollers simultaneously and will not warn you if you want to enable peripherals that do not exist in this version.

I/O ports (gpio.h).

All libraries for STM32 have very convenient functions like gpio_set(port, pin) for setting and resetting any number of pins. I decided to continue this tradition for configuration. Thus, in addition to traditional gpioSet And gpioReset I have:

// тип порта (аналоговый ввод, цифровой ввод, вывод, вывод с открытым стоком) 
void gpioSetAnalogue(uint32_t port, uint32_t pin);
void gpioSetInput(uint32_t port, uint32_t pin);
void gpioSetPushPull(uint32_t port, uint32_t pin);
void gpioSetOpenDrain(uint32_t port, uint32_t pin);
// ручное управление или для периферии (сперва нужно выбрать тип порта) 
void gpioSetAlternativeF(uint32_t port, uint32_t pin);
// 20кОм резисторы подтяжки
void gpioSetPullUp(uint32_t port, uint32_t pin);
void gpioSetPullDown(uint32_t port, uint32_t pin);
// частота тактирования порта (максимальная по умолчанию).
void gpioSetOutput2M(uint32_t port, uint32_t pin);
void gpioSetOutput10M(uint32_t port, uint32_t pin);
void gpioSetOutput50M(uint32_t port, uint32_t pin);

As you can see they all have the same syntax port, pin . This way, there is no need to transfer any settings; the required configuration is already contained in the function name, and you can also initialize several pins at once.

Now about the features of use. When initializing the output port (push pull or open drain), the maximum clock frequency is selected – 50 MHz. Thus, to configure a pin for output or input, it is enough to call just one of the four top functions. If a different clock frequency is needed, you additionally need to call the frequency selection function after, but not before. Also, after selecting the port type (analog input, digital input, output, open drain output), you can call the peripheral activation functions on that port (gpioSetAlternativeF) and functions for selecting pull-up to power or ground for inputs (gpioSetPullUp/Down). If you think you messed something up earlier, then there is an option gpioToDefault(GPIOx, GPIOALL) .

Also, the new approach gave a new feature. In addition to the classic gpioSet/Reset And getPortthere is a function gpioIsActivereturns one if at least one of the selected pins is active.

But the new approach also has new disadvantages. Considering the fact that the port configuration is scattered across two registers with four-bit fields, the initialization function is no longer reduced to simple conditional operations. It requires a cycle that goes through all 16 possible pins of the port. In short, the function turned out to be too large and slow to implement software interfaces with constant switching from output to input, although it is suitable for most other tasks.

To implement software interfaces, duplicates of these functions were useful, but they worked only for one pin. Moreover, the pin must be specified not as usual through the GPIOn macro, meaning (1<

Delays (delay.h).

Where without delays, my version of delays differs from the options of any other libraries in a wretched way. Because there are no assembly inserts in it, and the number of clock cycles per microsecond was selected manually at a frequency of 1 Hz. There are delays for microseconds, milliseconds and seconds (rough_delay_us, delay_ms, delay_s) the name of the function hints that the delay accuracy is coarse.

There are also timer delay functions for stm32f103, working on the second timer (timDelayNs, timDelayUs, timDelayMs) . Naturally, they require initialization before use (timDelayInit() ).

Examination.

The functions are tested in combat conditions, here it’s just a classic hello world, blinking the PA1 port. Take the stm32 folder and write this in main.c:

#include "gpio.h"
#include "delay.h"
#include "rcc.h"


int main(void) {
    enablePeriphClock(IOPA);
    gpioSetPushPull(GPIOA, GPIO1);

    while(1){
        gpioSet(GPIOA, GPIO1);
        delay_ms(500);
        gpioReset(GPIOA, GPIO1);
        delay_ms(500);
    }
}

Let's launch makeThen st-flash write bin/example.bin 0x8000000. Under Windows neo, don't judge.

Bottom line

Probably all microcontrollers are not adapted to any common API. Remember for yourself how many lovers of the arduino environment you poked into libavr in your time. Although STM32 is much more complex than eight-bits, it is still better for it to manually manage registers. And any standard functions are useless and interfere, or are completely impossible. I've already tinkered with the STM32 enough, I tried ADCs via DMA and UART and SPI and I2C and timers as PWM. And in the end I thought that there are only four cases when standard functions are really needed and useful, three of them were indicated in this post. Later I will add flash recording functions, I remember I had to store settings in the memory of stmki programs. Perhaps there will be a fifth case – port configuration functions, which I can no longer imagine how I will write.

Similar Posts

Leave a Reply

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