K.I.S.S. #stm32 HAL blinky LED.

The HAL seems to be the most popular library when developing for the STM32. I hate the CubeIDE with a vengeance, though. It’s slow and bloated. It’s based on Java, so no wonder.

I have set up VSCode on Linux, and added PlatformIO. I am not going to go through the set-up procedure here. There’s megabytes of stuff to download, naturally, which I’m not particularly happy with, but there we go.

Create a project for your MCU using PlatformIO. That is easy enough. I am using a blackpill, with the inbuilt LED on PC13. If you are using a discovery board or some-such, the pin is likely different.

I used CubeMX to generate the configuration. I just used it to give me clues as to what I needed to do. Once you have the general idea of how to set things up, you can dispense with MX and roll your own.

So here it is, a complete blinky sketch for the black pill:

#include "stm32f4xx_hal.h"

void SysTick_Handler(void)
{
	HAL_IncTick();
}

void setup_gpio_pc13_as_output(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};	
	__HAL_RCC_GPIOC_CLK_ENABLE(); /* GPIO Ports Clock Enable */
	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); 	/*Configure GPIO pin Output Level */

	/*Configure GPIO pin : PC13 */
	GPIO_InitStruct.Pin = GPIO_PIN_13;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}

int main()
{
	HAL_Init();	      // required
	setup_gpio_pc13_as_output();

	while (1)
	{
		HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
		HAL_Delay(100*1);
	}
}

As you can see, it’s very simple. You don’t need any of the other goofy stuff that MX seems to generate. No extra boilerplate or files are required, at least if you are using VSCode as I described above. It should work as-is.

You do need to call HAL_Init(), which is an inbuilt function.

If you are going to use HAL_delay, you need to setup Systick_Handler(). This function is called as an ISR, but you don’t need any extra setup. I did see that the handler existed as a weak function in the library somewhere. I therefore suspect that you can dispense with the function somehow, but that did not seem to work for me.

Systick_Handler() is called once every millisecond, and as you can see, in this case it just increment a tick, which is 32-bits wide.

I am in two minds about using the handler. If HAL_Delay() was eliminated, then I would not need it. I like to avoid setting up interrupts as much as possible. This avoids potential bottlenecks that I may not have accounted for, and keeps the design as flexible as possible.

Contrariwise, you can at least set up a tick, which is likely to be useful in many places. I also see it as a kind of pre-emptive scheduler, for which I can do things like button debouncing. For a debouncer, I don’t need to check button state every tick. One can also stagger other routines that you want to service. This will ensure that you’re not locking up the CPU for longer than you wanted.

An alternative is to use polling, of course. It’s the simplest approach, but here again, you may want to check on timing.

I’m also tempted to see if I can add my own abstractions on top of the HAL. One neat trick I saw in the rt-thread project is to define pins like so:

#define PC13 GPIOC, GPIO_PIN_13

Maybe that’s not everyone’s cup of tea, but it would enable to write a wrapper function which you can call like:

gpio_toggle(PC13);

That is more convenient.

You could also use it for creating a function like gpio_out(), which does the same thing as setup_gpio_pc13_as_output(); but is more generalised. I am also tempted enable all the RCC clocks in a oner in a separate initialisation function. You probably do not want to do that if you are looking to minimise power, but if that’s not your priority, you can reduce your code size. It’s a design decision, but I’m in favour of trying to avoid gobs and gobs of boilerplate.

This brings me onto my seeming obsession with performance. For many projects it may not be necessary. I like to experiment with audio. It soon becomes apparent when you do this that you need to pay attention to performance. You need to be efficient and give attention to how things are timed, what things can lock you up, and so on.

It is in this respect that I think something like mbed falls a bit short of the mark. I couldn’t see a way of writing an interrupt for when a PWM carry flag is set. The threading is great, and it has nice, interrupt and thread-safe classes for things like circular buffers. But the mcu seemed to baulk when I had a timer that was fired every 8us. Even without that, there seemed to be some problems when I had a couple of threads that seemed incompatible somehow.

This seems to be a general problem with RTOS’s. Critics often say that they’re great, but now how do we get them to work?

Mbed did seem good for some rapid prototyping where there isn’t a lot of time-critical stuff, but there’s only so far you can push it. Mbed is perhaps a place to look if you want to expand your horizons beyond MicroPython, or where MicroPython doesn’t quite cut it.

There is some nice stuff in mbed, so maybe it can work for you. I’m warming to it, but I don’t think it’s the hammer for every nail. Sometimes you just need to get your hands dirty and understand the hardware in greater depth.

Incidentally, I took the briefest of skims through some of the files in the rt-thread project. They seem to make extensive use of interrupts and DMA for things like SPI. So I imagine that one could make very responsive apps without having to sweat much of these issues by oneself.

As to Zephyr, I think I got an example to compile once, but the magic faded, and I ran out of chickens to sacrifice. OK, not chickens, patience. I’ve got a few toolkits that I investigate by rotation, when the mood takes me. There’s generally a lot of infrastructure surrounding them. So there’s a steep learning curve just to blink an LED, with the prospect of more hardship to follow.

So one is always trying to weigh up if the game is worth the candle without knowing what the rules of the game exactly are.

So I’m trying to learn more about the HAL, because it’s what the majority seem to be using, and I’m hoping that it provides a gain over libopencm3. I hope to add my own twist to more suit my sense of aesthetics. Which is a polite way of saying “what is all this crap doing, anyway?” I know I’m not the only one.

So, that’s me done for today. I hope you found the post useful, and are having lots of fun with your own microcontrollers.

About mcturra2000

Computer programmer living in Scotland.
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s