I thought I’d see if I could peek and poke memory addresses for my mcu. Here, for example, is how to set or clear a GPIO pin on my STM32:
fn gpio_clr(comptime pin: Pin) void {
const bits: u32 = @intCast(u32, 1) << @intCast(u5, pin.num + 16);
put32(pin.base_addr + 0x18, bits);
}
fn gpio_set(comptime pin: Pin) void {
const bits: u32 = @intCast(u32, 1) << @intCast(u5, pin.num);
put32(pin.base_addr + 0x18, bits);
}
“pin” should have its parameter declared as comptime. If you don’t do that, then the program computes the bit pattern at runtime, and incorporates a panic call. If you do it at comptime then everything can be calculated ahead of time. It’s a speed vs size trade-off. Zig would have to create multiple instances of gpio_clr(0 and gpio_set(), though, to account for the different offsets. You wouldn’t have this option in C though, because the computation would have to be done.
Here is how I’ve set up the pins:
const GPIOA_baseAddr = 0x40020000;
const GPIOC_baseAddr = 0x40020800;
const Pin = struct {
base_addr: u32,
num: u32,
};
const PC13 = Pin{.base_addr = GPIOC_baseAddr, .num = 13};
const led = PC13;
Here’s a simple pause() implementation:
fn pause(ticks:u32) void {
var i: u32 = 0;
while (i < ticks) {
asm volatile ("nop");
i += 1;
}
}
Here’s its disassembly:
0800020c <pause>:
800020c: b580 push {r7, lr}
800020e: 466f mov r7, sp
8000210: b084 sub sp, #16
8000212: 9002 str r0, [sp, #8]
8000214: 2000 movs r0, #0
8000216: 9003 str r0, [sp, #12]
8000218: e7ff b.n 800021a <pause+0xe>
800021a: 9803 ldr r0, [sp, #12]
800021c: 9902 ldr r1, [sp, #8]
800021e: 4288 cmp r0, r1
8000220: d208 bcs.n 8000234 <pause+0x28>
8000222: e7ff b.n 8000224 <pause+0x18>
8000224: bf00 nop
8000226: 9903 ldr r1, [sp, #12]
8000228: 1c48 adds r0, r1, #1
800022a: 4602 mov r2, r0
800022c: 9201 str r2, [sp, #4]
800022e: 4288 cmp r0, r1
8000230: d302 bcc.n 8000238 <pause+0x2c>
8000232: e008 b.n 8000246 <pause+0x3a>
8000234: b004 add sp, #16
8000236: bd80 pop {r7, pc}
8000238: f240 7040 movw r0, #1856 ; 0x740
800023c: f6c0 0000 movt r0, #2048 ; 0x800
8000240: 2100 movs r1, #0
8000242: f7ff fefd bl 8000040 <std.builtin.default_panic>
8000246: 9801 ldr r0, [sp, #4]
8000248: 9003 str r0, [sp, #12]
800024a: e7e6 b.n 800021a <pause+0xe>
Well, I’m rubbish at assembly, but it looks like it does an overflow check for “i+=1”, and panics if necessary. But “i” can never overflow due to the way that the loop is constructed.