mini-spi.zig (6768B)
1 const std = @import("std"); 2 3 const aux = @import("./aux.zig"); 4 const gpio = @import("./gpio.zig"); 5 const mem = @import("../mem.zig"); 6 const register = @import("../register.zig"); 7 8 const BASE_ADDRESS_1: usize = aux.BASE_ADDRESS + 0x80; 9 const BASE_ADDRESS_2: usize = aux.BASE_ADDRESS + 0xC0; 10 11 const AUX_SPI1_CNTL0: register.Register(Control0, .ReadModifyWrite) = .init(BASE_ADDRESS_1 + 0x00); 12 const AUX_SPI1_CNTL1: register.Register(Control1, .ReadModifyWrite) = .init(BASE_ADDRESS_1 + 0x04); 13 const AUX_SPI1_STAT: register.Register(Status, .ReadModifyWrite) = .init(BASE_ADDRESS_1 + 0x08); 14 const AUX_SPI1_PEEK: register.Register(FiFo, .ReadOnly) = .init(BASE_ADDRESS_1 + 0x0C); 15 const AUX_SPI1_IO: register.Register(FiFo, .ReadModifyWrite) = .init(BASE_ADDRESS_1 + 0x20); 16 17 const AUX_SPI2_CNTL0: register.Register(Control0, .ReadModifyWrite) = .init(BASE_ADDRESS_2 + 0x00); 18 const AUX_SPI2_CNTL1: register.Register(Control1, .ReadModifyWrite) = .init(BASE_ADDRESS_2 + 0x04); 19 const AUX_SPI2_STAT: register.Register(Status, .ReadModifyWrite) = .init(BASE_ADDRESS_2 + 0x08); 20 const AUX_SPI2_PEEK: register.Register(FiFo, .ReadOnly) = .init(BASE_ADDRESS_2 + 0x0C); 21 const AUX_SPI2_IO: register.Register(FiFo, .ReadModifyWrite) = .init(BASE_ADDRESS_2 + 0x20); 22 23 // Unlike UART, only 1 pair 24 const SPI1_CE2 = 16; 25 const SPI1_CE1 = 17; 26 const SPI1_CE0 = 18; 27 const SPI1_MISO = 19; 28 const SPI1_MOSI = 20; 29 const SPI1_SCLK = 21; 30 31 const SPI2_CE2 = 40; 32 const SPI2_CE1 = 41; 33 const SPI2_CE0 = 42; 34 const SPI2_MISO = 43; 35 const SPI2_MOSI = 44; 36 const SPI2_SCLK = 45; 37 38 const Control0 = packed struct(u32) { 39 pub const DOutHold = enum(u2) { 40 None = 0b00, 41 @"1" = 0b01, 42 @"4" = 0b10, 43 @"7" = 0b11, 44 }; 45 pub const ChipSelect = packed struct(u3) { 46 @"0": bool, 47 @"1": bool, 48 @"2": bool, 49 }; 50 shift_length: u6, 51 shift_out_ms: bool, 52 invert_spi_clk: bool, 53 out_rising: bool, 54 clear_fifos: bool, 55 in_rising: bool, 56 enable: bool, 57 dout_hold: DOutHold, 58 variable_width: bool, 59 variable_cs: bool, 60 post_input: bool, 61 chip_select: ChipSelect, 62 speed: u12, 63 }; 64 const Control1 = packed struct(u32) { 65 keep_input: bool, 66 shift_in_ms: bool, 67 _reserved_5_2: u4 = 0, 68 done_irq: bool, 69 tx_empty_irq: bool, 70 cs_high_time: u3, 71 _reserved_31_11: u21 = 0, 72 }; 73 const Status = packed struct(u32) { 74 bit_count: u6, 75 busy: bool, 76 rx_empty: bool, 77 rx_full: bool, 78 tx_empty: bool, 79 tx_full: bool, 80 _reserved_15_11: u5 = 0, 81 rx_fill_level: u4, 82 tx_fill_level: u4, 83 _reserved_31_22: u8, 84 }; 85 const FiFo = packed struct(u32) { 86 data: u16, 87 _reserved_16_31: u16 = 0, 88 }; 89 90 pub const SpiDevice = enum { 91 SPI1, 92 SPI2, 93 }; 94 95 pub const SpiConfig = struct { 96 speed: u12, 97 chip_select: Control0.ChipSelect = .{ 98 .@"0" = false, 99 .@"1" = false, 100 .@"2" = false, 101 }, 102 shift_length: u6 = 8, 103 out_rising: bool = true, 104 in_rising: bool = false, 105 invert_clk: bool = false, 106 shift_out_ms: bool = true, 107 shift_in_ms: bool = false, 108 cs_high_time: u3 = 0, 109 }; 110 111 pub fn init(device: SpiDevice, config: SpiConfig) void { 112 const cntl0_reg = switch (device) { 113 .SPI1 => AUX_SPI1_CNTL0, 114 .SPI2 => AUX_SPI2_CNTL0, 115 }; 116 const cntl1_reg = switch (device) { 117 .SPI1 => AUX_SPI1_CNTL1, 118 .SPI2 => AUX_SPI2_CNTL1, 119 }; 120 121 switch (device) { 122 .SPI1 => { 123 gpio.fn_sel(SPI1_MISO, .alt_fn_4) catch {}; 124 gpio.set_pull(SPI1_MISO, .Up) catch {}; 125 126 gpio.fn_sel(SPI1_MOSI, .alt_fn_4) catch {}; 127 gpio.set_pull(SPI1_MOSI, .Up) catch {}; 128 129 gpio.fn_sel(SPI1_SCLK, .alt_fn_4) catch {}; 130 if (config.invert_clk) { 131 gpio.set_pull(SPI1_SCLK, .Up) catch {}; 132 } else { 133 gpio.set_pull(SPI1_SCLK, .Down) catch {}; 134 } 135 136 gpio.fn_sel(SPI1_CE0, .alt_fn_4) catch {}; 137 gpio.set_pull(SPI1_CE2, .Up) catch {}; 138 139 gpio.fn_sel(SPI1_CE1, .alt_fn_4) catch {}; 140 gpio.set_pull(SPI1_CE2, .Up) catch {}; 141 142 gpio.fn_sel(SPI1_CE2, .alt_fn_4) catch {}; 143 gpio.set_pull(SPI1_CE2, .Up) catch {}; 144 145 aux.enable_spi_1(); 146 }, 147 .SPI2 => { 148 gpio.fn_sel(SPI2_MISO, .alt_fn_4) catch {}; 149 gpio.set_pull(SPI2_MISO, .Up) catch {}; 150 151 gpio.fn_sel(SPI2_MOSI, .alt_fn_4) catch {}; 152 gpio.set_pull(SPI2_MOSI, .Up) catch {}; 153 154 gpio.fn_sel(SPI2_SCLK, .alt_fn_4) catch {}; 155 if (config.invert_clk) { 156 gpio.set_pull(SPI2_SCLK, .Up) catch {}; 157 } else { 158 gpio.set_pull(SPI2_SCLK, .Down) catch {}; 159 } 160 161 gpio.fn_sel(SPI2_CE0, .alt_fn_4) catch {}; 162 gpio.set_pull(SPI2_CE2, .Up) catch {}; 163 164 gpio.fn_sel(SPI2_CE1, .alt_fn_4) catch {}; 165 gpio.set_pull(SPI2_CE1, .Up) catch {}; 166 167 gpio.fn_sel(SPI2_CE2, .alt_fn_4) catch {}; 168 gpio.set_pull(SPI2_CE2, .Up) catch {}; 169 170 aux.enable_spi_2(); 171 }, 172 } 173 174 cntl0_reg.set(.{ 175 .shift_length = config.shift_length, 176 .shift_out_ms = config.shift_out_ms, 177 .invert_spi_clk = config.invert_clk, 178 .out_rising = config.out_rising, 179 .clear_fifos = true, 180 .in_rising = config.in_rising, 181 .enable = true, 182 .dout_hold = .None, 183 .variable_width = false, 184 .variable_cs = false, 185 .post_input = false, 186 .chip_select = config.chip_select, 187 .speed = config.speed, 188 }); 189 190 cntl1_reg.set(.{ 191 .keep_input = false, 192 .shift_in_ms = config.shift_in_ms, 193 .done_irq = false, 194 .tx_empty_irq = false, 195 .cs_high_time = config.cs_high_time, 196 }); 197 } 198 199 pub fn status(device: SpiDevice) Status { 200 return switch (device) { 201 .SPI1 => AUX_SPI1_STAT.get(), 202 .SPI2 => AUX_SPI2_STAT.get(), 203 }; 204 } 205 206 pub fn write_polled(device: SpiDevice, data: []const u8) void { 207 const io_reg = switch (device) { 208 .SPI1 => AUX_SPI1_IO, 209 .SPI2 => AUX_SPI2_IO, 210 }; 211 212 for (data) |b| { 213 while (status(device).tx_full) {} 214 io_reg.set(.{ 215 .data = @as(u16, b), 216 }); 217 } 218 } 219 220 pub fn read_polled(device: SpiDevice, data: []u8) void { 221 const io_reg = switch (device) { 222 .SPI1 => AUX_SPI1_IO, 223 .SPI2 => AUX_SPI2_IO, 224 }; 225 226 for (0..data.len) |i| { 227 while (status(device).rx_empty) {} 228 data[i] = @truncate(io_reg.get().data); 229 } 230 } 231 232 pub fn transfer(device: SpiDevice, src: []const u8, dst: []u8) void { 233 write_polled(device, src); 234 flush(device); 235 read_polled(device, dst); 236 } 237 238 pub fn flush(device: SpiDevice) void { 239 while (status(device).busy) {} 240 }