gpio.zig (6424B)
1 const std = @import("std"); 2 const shared = @import("shared"); 3 4 const mem = @import("../mem.zig"); 5 const interrupts = @import("../interrupts.zig"); 6 7 pub const Error = error{ 8 PinOutOfRange, 9 }; 10 11 // Page 90, Table 6-1: GPIO Register Assignment 12 const BASE_ADDRESS: usize = mem.BASE_ADDRESS + 0x0020_0000; 13 const GPFSEL0: usize = BASE_ADDRESS + 0x0000; 14 const GPSET0: usize = BASE_ADDRESS + 0x001C; 15 const GPCLR0: usize = BASE_ADDRESS + 0x0028; 16 const GPLEV0: usize = BASE_ADDRESS + 0x0034; 17 const GPEDS0: usize = BASE_ADDRESS + 0x0040; 18 const GPREN0: usize = BASE_ADDRESS + 0x004C; 19 const GPFEN0: usize = BASE_ADDRESS + 0x0058; 20 const GPHEN0: usize = BASE_ADDRESS + 0x0064; 21 const GPLEN0: usize = BASE_ADDRESS + 0x0070; 22 const GPPUD: usize = BASE_ADDRESS + 0x0094; 23 const GPPUDCLK0: usize = BASE_ADDRESS + 0x0098; 24 25 pub const MAX_GPIO: u8 = 53; 26 const GPIO_PER_FSEL: u8 = 10; 27 28 fn gpio_check(pin: u8) Error!void { 29 if (pin > MAX_GPIO) return Error.PinOutOfRange; 30 } 31 32 fn get_address(pin: u8, offset: usize, chunk: ?u32) Error!*u32 { 33 try gpio_check(pin); 34 35 // One might say "@bitSizeOf(u32)" is redundant but I do not care 36 const index = @divFloor(pin, chunk orelse @bitSizeOf(u32)) * @sizeOf(u32); 37 const address: *u32 = @ptrFromInt(offset + index); 38 39 return address; 40 } 41 42 // Page 92, Table 6-2: GPIO Alternate function select register 0 43 // All function select registers reflect this bit layout 44 pub const FunctionSelect = enum(u3) { 45 input = 0b000, 46 output = 0b001, 47 48 alt_fn_0 = 0b100, 49 alt_fn_1 = 0b101, 50 alt_fn_2 = 0b110, 51 alt_fn_3 = 0b111, 52 alt_fn_4 = 0b011, 53 alt_fn_5 = 0b010, 54 }; 55 56 pub fn fn_sel(pin: u8, sel: FunctionSelect) Error!void { 57 const address = try get_address(pin, GPFSEL0, GPIO_PER_FSEL); 58 59 const offset: u5 = @truncate((pin % GPIO_PER_FSEL) * 3); 60 const mask = ~(@as(u32, 0b111) << offset); 61 62 var state = mem.get_u32(address); 63 state &= mask; 64 state |= (@as(u32, @intCast(@intFromEnum(sel))) << offset); 65 mem.put_u32(address, state); 66 } 67 68 fn addr_bitset(pin: u8, address: *u32) void { 69 const offset: u5 = @truncate(pin % 32); 70 const mask = (@as(u32, 1) << offset); 71 72 // We don't want to preserve the old value 73 // SET and CLR are separated for this purpose 74 mem.put_u32(address, mask); 75 } 76 77 fn addr_bitget(pin: u8, address: *u32) bool { 78 const offset: u5 = @truncate(pin % 32); 79 const mask = (@as(u32, 1) << offset); 80 81 return (mem.get_u32(address) & mask) != 0; 82 } 83 84 fn addr_bitor_set(pin: u8, address: *u32) void { 85 const offset: u5 = @truncate(pin % 32); 86 const mask = (@as(u32, 1) << offset); 87 88 mem.put_u32(address, mem.get_u32(address) | mask); 89 } 90 91 fn addr_bitor_unset(pin: u8, address: *u32) void { 92 const offset: u5 = @truncate(pin % 32); 93 const mask = (@as(u32, 1) << offset); 94 95 mem.put_u32(address, mem.get_u32(address) | ~mask); 96 } 97 98 // Turn on 99 fn output_set(pin: u8) Error!void { 100 addr_bitset(pin, try get_address(pin, GPSET0, null)); 101 } 102 103 // Turn off 104 fn output_clear(pin: u8) Error!void { 105 addr_bitset(pin, try get_address(pin, GPCLR0, null)); 106 } 107 108 // Public API 109 pub fn set_output(pin: u8) Error!void { 110 try fn_sel(pin, .output); 111 } 112 113 pub fn set_input(pin: u8) Error!void { 114 try fn_sel(pin, .input); 115 } 116 117 pub fn set_on(pin: u8) Error!void { 118 try output_set(pin); 119 } 120 121 pub fn set_off(pin: u8) Error!void { 122 try output_clear(pin); 123 } 124 125 pub fn read(pin: u8) Error!bool { 126 const address = try get_address(pin, GPLEV0, null); 127 128 const offset: u5 = @truncate(pin % 32); 129 const mask = (@as(u32, 1) << offset); 130 131 return (mem.get_u32(address) & mask) == mask; 132 } 133 134 pub fn write(pin: u8, state: bool) Error!void { 135 if (state) { 136 try set_on(pin); 137 } else { 138 try set_off(pin); 139 } 140 } 141 142 pub const PullMode = enum(u2) { 143 Off = 0b00, 144 Down = 0b01, 145 Up = 0b10, 146 }; 147 148 fn wait(count: usize) void { 149 var c = count; 150 while (c != 0) { 151 c -= 1; 152 } 153 } 154 155 pub fn set_pull(pin: u8, pull: PullMode) Error!void { 156 try gpio_check(pin); 157 158 // Set GPPUD 159 mem.put_u32(@ptrFromInt(GPPUD), @intFromEnum(pull)); 160 161 // Wait 150 cycles 162 wait(150); 163 164 // Set GPPUDCLK 165 const GPPUDCLK = try get_address(pin, GPPUDCLK0, null); 166 addr_bitset(pin, GPPUDCLK); 167 168 // Wait 150 cycles 169 wait(150); 170 171 // Clear GPPUD & GPPUDCLK 172 mem.put_u32(GPPUDCLK, 0); 173 mem.put_u32(@ptrFromInt(GPPUDCLK0), 0); 174 } 175 176 pub const Event = union(enum) { 177 PinEvent: u8, 178 PinRisingEdge: u8, 179 PinFallingEdge: u8, 180 }; 181 var buffer: [1024]u8 = undefined; 182 var fba: std.heap.FixedBufferAllocator = undefined; 183 pub var ev: shared.Publisher(Event) = undefined; 184 185 pub fn rising_edge_interrupt(pin: u8, enable: bool) Error!void { 186 try gpio_check(pin); 187 188 if (enable) { 189 addr_bitor_set(pin, try get_address(pin, GPREN0, null)); 190 } else { 191 addr_bitor_unset(pin, try get_address(pin, GPREN0, null)); 192 } 193 } 194 195 pub fn falling_edge_interrupt(pin: u8, enable: bool) Error!void { 196 try gpio_check(pin); 197 198 if (enable) { 199 addr_bitor_set(pin, try get_address(pin, GPFEN0, null)); 200 } else { 201 addr_bitor_unset(pin, try get_address(pin, GPFEN0, null)); 202 } 203 } 204 205 fn has_event(pin: u8) Error!bool { 206 try gpio_check(pin); 207 208 return addr_bitget(pin, try get_address(pin, GPEDS0, null)); 209 } 210 211 fn clear_event(pin: u8) Error!void { 212 try gpio_check(pin); 213 214 addr_bitset(pin, try get_address(pin, GPEDS0, null)); 215 } 216 217 pub fn enable_interrupts() !void { 218 mem.barrier(.Write); 219 220 fba = .init(&buffer); 221 ev = try .init(fba.allocator()); 222 223 interrupts.set_exception_handler(.IRQ, gpio_handler); 224 interrupts.enable_peripheral_interrupt(.GPIO0); 225 interrupts.enable_peripheral_interrupt(.GPIO1); 226 227 mem.barrier(.Write); 228 } 229 230 noinline fn gpio_handler(registers: interrupts.Registers) void { 231 _ = registers; 232 mem.barrier(.Write); 233 defer mem.barrier(.Write); 234 235 // Pins 0-53 236 if (!interrupts.pending_peripheral_interrupt(.GPIO0) and !interrupts.pending_peripheral_interrupt(.GPIO1)) { 237 return; 238 } 239 240 // Find out which pin had the event 241 242 // A bit rubbish 243 for (0..(MAX_GPIO + 1)) |n| { 244 const pin: u8 = @truncate(n); 245 if (!(has_event(pin) catch false)) continue; 246 247 if (read(pin) catch unreachable) { 248 // Likely rising 249 ev.publish(.PinRisingEdge, pin); 250 } else { 251 // Likely falling 252 ev.publish(.PinFallingEdge, pin); 253 } 254 255 clear_event(pin) catch {}; 256 } 257 }