-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgpio.zig
More file actions
133 lines (116 loc) · 4.39 KB
/
Copy pathgpio.zig
File metadata and controls
133 lines (116 loc) · 4.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
const std = @import("std");
// Driver: GPIO
//
// This is a basic gpio pin driver that has been specifically designed for the
// Raspberry Pi 4B (and written in Zig!). The drivers makes the following guarantees:
// 1. Zero-Allocation: Out of the box, all 57 pins are usable without zero allocations
// being required!
//
// 2. No Assumptions: There are no assumptions about the environment
// that the driver is operating in (which can be limiting, but
// there will be an interrupt-able version at some point).
//
// constants
const gpio_base = 0xfe_200_000;
const offset_fsel_base = 0x00;
const offset_set_base = 0x1c;
const offset_clear_base = 0x28;
const offset_read_base = 0x34;
// compile time allocation of the all supported raspberry pi 4
// gpio pins.
pub const Pins: [58]Pin = for (0..1) |_| {
var dummy_array: [58]Pin = undefined;
for (0..58) |i| {
dummy_array[i] = CreatePin(i);
}
break dummy_array;
};
// represents a gpio pin's characteristics, all information was
// taken directly from the datasheet.
//
// (Page. 65)
// https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf
const Pin = struct {
// attributes
n: u6, // the pin number
// in-memory registers as pointers, actual addresses
// are calculated with the CreatePin function.
fsel: *align(4) u32,
set: *align(4) u32,
clear: *align(4) u32,
level: *align(4) u32,
// configures this pin with a given function type
pub fn FuncSelect(self: @This(), pin_type: PinFunction) !void {
const shift_am: u5 = @truncate((self.n % 10) * 3);
const value: u32 = 0 | @intFromEnum(pin_type);
self.fsel.* |= (value << shift_am);
}
// sets the pin to "high"
pub fn Set(self: @This()) void {
const value: u32 = 0b1;
const shift_am: u5 = @truncate((self.n % 32));
self.set.* |= (value << shift_am);
}
// sets the pin to "low"
pub fn Clear(self: @This()) void {
const value: u32 = 0b1;
const shift_am: u5 = @truncate((self.n % 32));
self.clear.* |= (value << shift_am);
}
// performs a basic "read" of the pin.
pub fn Read(self: @This()) !u1 {
const shift_am: u5 = @truncate((self.n % 32));
const returned_value: u1 = 0b1 & (self.level.* >> shift_am);
return returned_value;
}
};
// a helper function that creates pins at compile time.
fn CreatePin(n: u6) Pin {
// the following variable(s) are made to help with readability, but can be
// inlined.
const singleBitGranularityOffset = CalculateOffset(n, 1);
return Pin{
.n = n,
.fsel = @ptrFromInt(gpio_base + offset_fsel_base + CalculateOffset(n, 3)),
.set = @ptrFromInt(gpio_base + offset_set_base + singleBitGranularityOffset),
.clear = @ptrFromInt(gpio_base + offset_clear_base + singleBitGranularityOffset),
.level = @ptrFromInt(gpio_base + offset_read_base + singleBitGranularityOffset),
};
}
// helper funtion to calculate the offset from a register base
// from a known pin number and data granularity.
fn CalculateOffset(pin: u6, data_granularity: u6) u32 {
// a gpio register represents the state of 'x' number of pins.
// we know 'x' to be 32 / data_granularity (where data_granularity
// is the number of bits it takes to represent that 'state')
//
// therefore we calculate the offset by taking the pin number and
// dividing it by 'x'; we then find the byte offset by multiplying
// by 4 (32 bits).
return (pin / ((32 / data_granularity))) * 4;
}
// gpio pins must first be configured for a specific function.
// the different categories of functions are listed below and
// can be found in the documentation:
//
// (Page. 67)
// https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf
pub const PinFunction = enum(u4) {
None = 0b1111,
Input = 0b000,
Output = 0b001,
ALT0 = 0b100,
ALT1 = 0b101,
ALT2 = 0b110,
ALT3 = 0b111,
ALT4 = 0b011,
ALT5 = 0b010,
};
test "offset calculation is correct" {
const test_pins = [_]u6{ 2, 9, 17, 21, 39, 40, 57 };
const test_offsets = [_]u8{ 0, 0, 4, 8, 0xc, 0x10, 0x14 };
for (test_pins, test_offsets) |pin, offset| {
const calculated_offset = CalculateOffset(pin, 3);
try std.testing.expect(calculated_offset == offset);
}
}