Using SysTick on a STM32F411 blackpill with #ziglang

So, I’m now going to write my second program in Zig. Last time I got a simple blinky LED working using the main loop. This time I want to see if I can do it using the SysTick timer. Refer to my previous post for the general discussion on the layout of the code.

It turns out that it’s relatively straightforward to implement a systick handler. The struct that contains the systick registers is called STK. Add the following code to main() for a minimal setup of systick:

    regs.STK.LOAD.modify(.{.RELOAD = 16_000_000/4 -1});
    regs.STK.VAL.modify(.{.CURRENT = 0});
    regs.STK.CTRL.modify(.{.CLKSOURCE = 1, .TICKINT = 1, .ENABLE = 1});

This is a cut-down version of the SysTick_Config() function provided by CMSIS. The full version of the code for the F411 is:

__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
  if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
    return (1UL);                                                   /* Reload value impossible */

  SysTick->LOAD  = (uint32_t)(ticks - 1UL);                         /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
  SysTick->VAL   = 0UL;                                             /* Load the SysTick Counter Value */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;                         /* Enable SysTick IRQ and SysTick Timer */
  return (0UL);                                                     /* Function successful */

So you can see I’ve ommited a sanity check for the input parameter, and hadn’t set the interrupt priority. I just wanted to see if I could get something working.

The clock speed is 16MHz. This is the default speed, although it can be changed by twiddling with various registers and mucking around with clocks. So I set the RELOAD register of systick so that it triggers 4 times a second. It is more usual to have it trigger at a rate of once every 1ms. I reset the CURRENT value to 0. I could probably omit this. Then I enable the systick interrupt.

So you can probably boil the whole thing down to 2 lines of code if you want. More if you didn’t want to play it so fast-and-loose, and set up the interrupt priorities better.

You’ll need to provide the systick interrupt handler. Here it is:

export fn sysTickHandler() void {
    var pins =;
    regs.GPIOC.ODR.modify(.{ .ODR13 = ~pins.ODR13});

sysTickHandler() is a fixed name defined in linker.ld. It is weakly defined to be nullHandler(), but we want to override the default behaviour to provide something useful to our program.

As you can see, we just toggle pin PC13. That’s the built-in LED on a blackpill, and the user button on the equivalent Nucleo board. So you’ll need to tweak things a little if you are using the latter device.

In the systick handler you would typically provide a count of the number of ticks it has made, which you can use for simple timing purposes. I haven’t done this, though. The two lines of code I have used look a little unwieldy to me. Perhaps there is a simpler way of doing it.

I see that the linker script has actually been borrowed from libopencm3. It seems to have a neat way of providing weak default handlers. The way that ST does it seems more laborious (quelle surprise). The memory layout I used is actually wrong for my board. But it works. It also contains gloop that I’m pretty sure is only relevant to C++. It can be eliminated. Linker scripts can be a bit of a dark art.

If I get a lot of feedback, I’ll probably make this stuff available via github.

So far, Zig is looking pretty do-able on microcontrollers. Huge advantage over C? Probably not, at least not so far as I can discern from my minimal exposure to the language. I am interested in checking out the async functionality of Zig, though, and that may prove a game-changer. We shall see.

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: Logo

You are commenting using your 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