sylveos

Toy Operating System
Log | Files | Refs

nrf.zig (15059B)


      1 // nRF24L01+
      2 
      3 const std = @import("std");
      4 const spi = @import("./spi.zig");
      5 const gpio = @import("./gpio.zig");
      6 const clock = @import("./clock.zig");
      7 const uart = @import("./mini-uart.zig");
      8 
      9 // 51: 8.3.1 SPI commands
     10 const CMD_R_REGISTER: u8 = 0b0000_0000;
     11 const CMD_W_REGISTER: u8 = 0b0010_0000;
     12 const CMD_R_RX_PAYLOAD: u8 = 0b0110_0001;
     13 const CMD_W_TX_PAYLOAD: u8 = 0b1010_0000;
     14 const CMD_FLUSH_TX: u8 = 0b1110_0001;
     15 const CMD_FLUSH_RX: u8 = 0b1110_0010;
     16 const CMD_REUSE_TX_PL: u8 = 0b1110_0011;
     17 const CMD_R_RX_PL_WID: u8 = 0b0110_0000;
     18 const CMD_W_ACK_PAYLOAD: u8 = 0b1010_1000;
     19 const CMD_W_TX_PAYLOAD_NO_ACK: u8 = 0b1011_0000;
     20 const CMD_NOP: u8 = 0b1111_1111;
     21 
     22 // 57: 9.1 Register map table
     23 const REG_CONFIG: u5 = 0x00;
     24 const nRFConfig = packed struct(u8) {
     25     const PrimRx = enum(u1) {
     26         PRX = 1,
     27         PTX = 0,
     28     };
     29     const Crc = enum(u1) {
     30         @"1" = 0,
     31         @"2" = 1,
     32     };
     33     prim_rx: PrimRx = .PTX,
     34     pwr_up: bool = false,
     35     crc: Crc = .@"1",
     36     enable_crc: bool = true,
     37     mask_max_rt: bool = false,
     38     mask_tx_rs: bool = false,
     39     mask_rx_dr: bool = false,
     40     _reserved_7: u1 = 0,
     41 };
     42 
     43 // Pipes
     44 const Pipes = packed struct(u8) {
     45     @"0": bool = true,
     46     @"1": bool = true,
     47     @"2": bool = true,
     48     @"3": bool = true,
     49     @"4": bool = true,
     50     @"5": bool = true,
     51     _reserved_7_6: u2 = 0,
     52 };
     53 const REG_EN_AA: u5 = 0x01;
     54 const REG_EN_RXADDR: u5 = 0x02;
     55 
     56 const REG_SETUP_AW: u5 = 0x03;
     57 const AddressWidths = packed struct(u8) {
     58     const Widths = enum(u2) {
     59         @"3" = 0b01,
     60         @"4" = 0b10,
     61         @"5" = 0b11,
     62     };
     63 
     64     width: Widths,
     65     _reserved_7_2: u6 = 0,
     66 };
     67 
     68 const REG_SETUP_RETR: u5 = 0x04;
     69 const SetupRetry = packed struct(u8) {
     70     delay: u4 = 0b0000,
     71     attempts: u4 = 0b011,
     72 };
     73 
     74 const REG_RF_CH: u5 = 0x05;
     75 const RFChannel = packed struct(u8) {
     76     channel: u7 = 0b0000010,
     77     _reserved_7: u1 = 0,
     78 };
     79 
     80 const REG_RF_SETUP: u5 = 0x06;
     81 const RFSetup = packed struct(u8) {
     82     const OutputPower = enum(u2) {
     83         @"-18dBm" = 0b00,
     84         @"-12dBm" = 0b01,
     85         @"-6dBm" = 0b10,
     86         @"0dBm" = 0b11,
     87     };
     88     const Speed = enum(u2) {
     89         @"1Mbps" = 0b00,
     90         @"2Mbps" = 0b01,
     91         @"250kbps" = 0b10,
     92     };
     93 
     94     _reserved_0: u1 = 0,
     95     output_power: OutputPower = .@"0dBm",
     96     rf_high: bool = true,
     97     pll_lock: bool = false,
     98     rf_low: bool = false,
     99     _reserved_6: u1 = 0,
    100     continuous_transmission: bool = false,
    101 };
    102 
    103 const REG_STATUS: u5 = 0x07;
    104 const Status = packed struct(u8) {
    105     tx_full: bool = false,
    106     rx_pipe_number: u3 = 0b111,
    107     maxed_retries: bool = false,
    108     tx_ds: bool = false,
    109     rx_dr: bool = false,
    110     _reserved_7: u1 = 0,
    111 };
    112 
    113 const REG_OBSERVE_TX: u5 = 0x08;
    114 const ObserveTx = packed struct(u8) {
    115     retransmit_count: u4 = 0,
    116     lost_count: u4 = 0,
    117 };
    118 
    119 const REG_RPD: u5 = 0x09;
    120 const ReceivedPowerDetector = packed struct(u8) {
    121     rpd: bool = false,
    122     _reserved_7_1: u7,
    123 };
    124 
    125 const REG_RX_ADDR_P0: u5 = 0x0A;
    126 const REG_RX_ADDR_P1: u5 = 0x0B;
    127 const REG_RX_ADDR_P2: u5 = 0x0C;
    128 const REG_RX_ADDR_P3: u5 = 0x0D;
    129 const REG_RX_ADDR_P4: u5 = 0x0E;
    130 const REG_RX_ADDR_P5: u5 = 0x0F;
    131 const REG_TX_ADDR: u5 = 0x10;
    132 
    133 const RxPayload = packed struct(u8) {
    134     bytes: u6 = 0,
    135     _reserved_7_6: u2 = 0,
    136 };
    137 const REG_RX_PW_P0: u5 = 0x11;
    138 const REG_RX_PW_P1: u5 = 0x12;
    139 const REG_RX_PW_P2: u5 = 0x13;
    140 const REG_RX_PW_P3: u5 = 0x14;
    141 const REG_RX_PW_P4: u5 = 0x15;
    142 const REG_RX_PW_P5: u5 = 0x16;
    143 
    144 const REG_FIFO_STATUS: u5 = 0x17;
    145 const FiFoStatus = packed struct(u8) {
    146     rx_empty: bool,
    147     rx_full: bool,
    148     _reserved_3_2: u2,
    149     tx_empty: bool,
    150     tx_full: bool,
    151     tx_reuse: bool,
    152     _reserved_7: u1 = 0,
    153 };
    154 
    155 // Pipes
    156 const REG_DYNPD: u5 = 0x1C;
    157 
    158 const REG_FEATURE: u5 = 0x1D;
    159 const Feature = packed struct(u8) {
    160     enable_dynamic_acknowledge: bool = false,
    161     enable_acknowledge_payload: bool = false,
    162     enable_dynamic_payload_length: bool = false,
    163     _reserved_7_3: u5 = 0,
    164 };
    165 
    166 // Implementation
    167 pub const Error = error{
    168     InvalidPipe,
    169     InvalidPayload,
    170     InvalidPayloadSize,
    171     NotReady,
    172     PayloadTooLarge,
    173     Timeout,
    174 };
    175 
    176 pub const Config = struct {
    177     spi_config: spi.SpiConfig,
    178     ce_pin: u8,
    179     int_pin: u8,
    180 
    181     channel: u7 = 2,
    182     output_power: RFSetup.OutputPower = .@"0dBm",
    183     data_rate: RFSetup.Speed = .@"1Mbps",
    184     crc_length: nRFConfig.Crc = .@"2",
    185     auto_retransmit_delay: u4 = 2, // 500us
    186     auto_retransmit_count: u4 = 15,
    187     payload_size: u6 = 32,
    188 };
    189 
    190 pub const Device = struct {
    191     spi_config: spi.SpiConfig,
    192     ce_pin: u8,
    193     int_pin: u8,
    194 
    195     const Self = @This();
    196 
    197     pub fn init(config: Config) !Self {
    198         var self: Self = .{
    199             .spi_config = config.spi_config,
    200             .ce_pin = config.ce_pin,
    201             .int_pin = config.int_pin,
    202         };
    203 
    204         self.spi_config.chip_select_polarity = .ActiveLow;
    205         self.spi_config.clock_polarity = .Low;
    206         self.spi_config.clock_phase = .Middle;
    207         spi.init(&self.spi_config, 64);
    208 
    209         try gpio.fn_sel(config.ce_pin, .output);
    210         try gpio.set_off(config.ce_pin);
    211 
    212         // Power on reset 100ms
    213         clock.delay_ms(100);
    214 
    215         // Configure
    216         self.write_register(nRFConfig, REG_CONFIG, .{
    217             .pwr_up = false,
    218             .crc = .@"2",
    219             .prim_rx = .PTX,
    220         });
    221 
    222         // Setup Auto Acknowledge
    223         self.write_register(Pipes, REG_EN_AA, .{
    224             .@"0" = true,
    225             .@"1" = true,
    226             .@"2" = true,
    227             .@"3" = true,
    228             .@"4" = true,
    229             .@"5" = true,
    230         });
    231 
    232         // Enable Pipes 0/1
    233         self.write_register(Pipes, REG_EN_RXADDR, .{
    234             .@"0" = true,
    235             .@"1" = true,
    236             .@"2" = false,
    237             .@"3" = false,
    238             .@"4" = false,
    239             .@"5" = false,
    240         });
    241 
    242         // Set Address Width
    243         self.write_register(AddressWidths, REG_SETUP_AW, .{
    244             .width = .@"5",
    245         });
    246 
    247         // Setup retry
    248         self.write_register(SetupRetry, REG_SETUP_RETR, .{
    249             .attempts = config.auto_retransmit_count,
    250             .delay = config.auto_retransmit_delay,
    251         });
    252 
    253         // Set channel
    254         self.write_register(RFChannel, REG_RF_CH, .{
    255             .channel = config.channel,
    256         });
    257 
    258         // Set data rate and power
    259         self.set_rf(config.output_power, config.data_rate);
    260 
    261         // Clear DYNPD & FEATURE
    262         self.write_register(u8, REG_FEATURE, 0);
    263         self.write_register(u8, REG_DYNPD, 0);
    264 
    265         if (config.payload_size > 32) return Error.InvalidPayloadSize;
    266         try self.set_rx_payload_size(0, config.payload_size);
    267         try self.set_rx_payload_size(1, config.payload_size);
    268 
    269         // Flush before power up
    270         self.flush_tx();
    271         self.flush_rx();
    272         self.clear_irq();
    273 
    274         // Power on (Standby-I)
    275         self.power_up();
    276 
    277         return self;
    278     }
    279 
    280     fn power_up(self: *const Self) void {
    281         var cfg = self.read_register(nRFConfig, REG_CONFIG);
    282         cfg.pwr_up = true;
    283         self.write_register(nRFConfig, REG_CONFIG, cfg);
    284         clock.delay_ms(2);
    285     }
    286 
    287     fn power_down(self: *const Self) void {
    288         var cfg = self.read_register(nRFConfig, REG_CONFIG);
    289         cfg.pwr_up = false;
    290         self.write_register(nRFConfig, REG_CONFIG, cfg);
    291         self.set_ce_low();
    292     }
    293 
    294     fn set_ce_high(self: *const Self) void {
    295         gpio.set_on(self.ce_pin) catch {};
    296     }
    297 
    298     fn set_ce_low(self: *const Self) void {
    299         gpio.set_off(self.ce_pin) catch {};
    300     }
    301 
    302     fn pulse_ce(self: *const Self) void {
    303         self.set_ce_high();
    304         clock.delay(15);
    305         self.set_ce_low();
    306     }
    307 
    308     fn set_rf(self: *const Self, power: RFSetup.OutputPower, speed: RFSetup.Speed) void {
    309         const rate: u2 = @intFromEnum(speed);
    310         const low = (rate & 0b10) == 0b10;
    311         const high = (rate & 0b01) == 0b01;
    312 
    313         self.write_register(RFSetup, REG_RF_SETUP, .{
    314             .output_power = power,
    315             .rf_low = low,
    316             .rf_high = high,
    317         });
    318     }
    319 
    320     // u40 = 5 byte addresses
    321     pub fn set_tx_addr(self: *const Self, addr: u40) void {
    322         var tx_data: [5]u8 = undefined;
    323         std.mem.writeInt(u40, &tx_data, addr, .little);
    324         self.write_register_n(REG_TX_ADDR, &tx_data);
    325     }
    326 
    327     pub fn set_rx_addr(self: *const Self, pipe: u3, addr: u40) !void {
    328         if (pipe > 5) return Error.InvalidPipe;
    329 
    330         if (pipe <= 1) {
    331             var tx_data: [5]u8 = undefined;
    332             std.mem.writeInt(u40, &tx_data, addr, .little);
    333             self.write_register_n(REG_RX_ADDR_P0 + pipe, &tx_data);
    334         } else {
    335             const tx_data = [_]u8{@truncate(addr)};
    336             self.write_register_n(REG_RX_ADDR_P0 + pipe, &tx_data);
    337         }
    338     }
    339 
    340     fn set_rx_payload_size(self: *const Self, pipe: u3, size: u6) Error!void {
    341         if (size > 32) return Error.InvalidPayloadSize;
    342         if (pipe > 5) return Error.InvalidPipe;
    343 
    344         self.write_register(RxPayload, REG_RX_PW_P0 + pipe, .{
    345             .bytes = size,
    346         });
    347     }
    348 
    349     fn enable_dynamic_payloads(self: *const Self) void {
    350         var feature = self.read_register(Feature, REG_FEATURE);
    351         feature.enable_dynamic_payload_length = true;
    352         self.write_register(Feature, REG_FEATURE, feature);
    353         self.write_register(Pipes, REG_DYNPD, .{
    354             .@"0" = true,
    355             .@"1" = true,
    356             .@"2" = true,
    357             .@"3" = true,
    358             .@"4" = true,
    359             .@"5" = true,
    360         });
    361     }
    362 
    363     fn disable_dynamic_payloads(self: *const Self) void {
    364         var feature = self.read_register(Feature, REG_FEATURE);
    365         feature.enable_dynamic_payload_length = false;
    366         self.write_register(Feature, REG_FEATURE, feature);
    367         self.write_register(Pipes, REG_DYNPD, .{
    368             .@"0" = false,
    369             .@"1" = false,
    370             .@"2" = false,
    371             .@"3" = false,
    372             .@"4" = false,
    373             .@"5" = false,
    374         });
    375     }
    376 
    377     pub fn connect(self: *const Self) void {
    378         var cfg = self.read_register(nRFConfig, REG_CONFIG);
    379         cfg.prim_rx = .PRX;
    380         self.write_register(nRFConfig, REG_CONFIG, cfg);
    381 
    382         self.clear_irq();
    383         self.flush_rx();
    384 
    385         self.set_ce_high();
    386         clock.delay(150);
    387     }
    388 
    389     pub fn disconnect(self: *const Self) void {
    390         self.set_ce_low();
    391         clock.delay(150);
    392 
    393         var cfg = self.read_register(nRFConfig, REG_CONFIG);
    394         cfg.prim_rx = .PTX;
    395         self.write_register(nRFConfig, REG_CONFIG, cfg);
    396 
    397         self.flush_tx();
    398     }
    399 
    400     pub fn transmit(self: *const Self, payload: []const u8, timeout_ms: u32) Error!bool {
    401         if (payload.len == 0 or payload.len > 32) return Error.InvalidPayload;
    402 
    403         self.disconnect();
    404         self.clear_irq();
    405         self.flush_tx();
    406 
    407         self.write_command(CMD_W_TX_PAYLOAD, payload);
    408 
    409         self.pulse_ce();
    410 
    411         const deadline = clock.current_count() + std.time.us_per_ms * timeout_ms;
    412         while (clock.current_count() < deadline) {
    413             const status = self.get_status();
    414 
    415             if (status.tx_ds) {
    416                 self.write_register(Status, REG_STATUS, .{
    417                     .tx_ds = true,
    418                 });
    419 
    420                 return true;
    421             }
    422 
    423             if (status.maxed_retries) {
    424                 self.write_register(Status, REG_STATUS, .{
    425                     .maxed_retries = true,
    426                 });
    427                 self.flush_tx();
    428 
    429                 return false;
    430             }
    431 
    432             clock.delay_ms(1);
    433         }
    434 
    435         return Error.Timeout;
    436     }
    437 
    438     pub fn receive(self: *const Self, buffer: []u8) Error!usize {
    439         const fifo = self.read_register(FiFoStatus, REG_FIFO_STATUS);
    440         if (fifo.rx_empty) return 0;
    441 
    442         var width: usize = buffer.len;
    443 
    444         const feature = self.read_register(Feature, REG_FEATURE);
    445         if (feature.enable_dynamic_payload_length) {
    446             const w = self.read_rx_payload_width();
    447             if (w > 32) {
    448                 self.flush_rx();
    449                 return Error.NotReady;
    450             }
    451             width = @intCast(w);
    452             if (width > buffer.len) return Error.PayloadTooLarge;
    453         }
    454 
    455         _ = self.read_command(CMD_R_RX_PAYLOAD, buffer[0..width]);
    456 
    457         self.write_register(Status, REG_STATUS, .{
    458             .rx_dr = true,
    459         });
    460 
    461         return width;
    462     }
    463 
    464     fn read_register(self: *const Self, comptime T: type, reg: u5) T {
    465         const cmd: u8 = CMD_R_REGISTER | @as(u8, reg);
    466         const tx_data = [_]u8{ cmd, CMD_NOP };
    467         var rx_data: [2]u8 = undefined;
    468 
    469         spi.transfer(&self.spi_config, &tx_data, &rx_data);
    470 
    471         return @bitCast(rx_data[1]);
    472     }
    473 
    474     fn read_register_n(self: *const Self, reg: u5, buffer: []u8) void {
    475         return self.read_command(CMD_R_REGISTER | @as(u8, reg), buffer);
    476     }
    477 
    478     fn read_rx_payload_width(self: *const Self) u8 {
    479         const tx_data = [_]u8{ CMD_R_RX_PL_WID, CMD_NOP };
    480         var rx_data: [2]u8 = undefined;
    481 
    482         spi.transfer(&self.spi_config, &tx_data, &rx_data);
    483 
    484         return rx_data[1];
    485     }
    486 
    487     fn write_register(self: *const Self, comptime T: type, reg: u5, value: T) void {
    488         const cmd: u8 = CMD_W_REGISTER | @as(u8, reg);
    489         const value_byte: u8 = @bitCast(value);
    490         const tx_data = [_]u8{ cmd, value_byte };
    491 
    492         var rx_data: [2]u8 = undefined;
    493 
    494         spi.transfer(&self.spi_config, &tx_data, &rx_data);
    495     }
    496 
    497     fn write_register_n(self: *const Self, reg: u5, buffer: []const u8) void {
    498         return self.write_command(CMD_W_REGISTER | @as(u8, reg), buffer);
    499     }
    500 
    501     fn read_command(self: *const Self, cmd: u8, result: []u8) Status {
    502         var tx_data: [64]u8 = undefined;
    503         var rx_data: [64]u8 = undefined;
    504 
    505         tx_data[0] = cmd;
    506         @memset(tx_data[1 .. result.len + 1], CMD_NOP);
    507 
    508         spi.transfer(&self.spi_config, tx_data[0 .. result.len + 1], rx_data[0 .. result.len + 1]);
    509 
    510         @memcpy(result, rx_data[1 .. result.len + 1]);
    511 
    512         return @bitCast(rx_data[0]);
    513     }
    514 
    515     fn write_command(self: *const Self, cmd: u8, bytes: []const u8) void {
    516         var tx_data: [64]u8 = undefined;
    517         var rx_data: [64]u8 = undefined;
    518 
    519         tx_data[0] = cmd;
    520         @memcpy(tx_data[1 .. bytes.len + 1], bytes);
    521 
    522         spi.transfer(&self.spi_config, tx_data[0 .. bytes.len + 1], rx_data[0 .. bytes.len + 1]);
    523     }
    524 
    525     pub fn get_status(self: *const Self) Status {
    526         return self.read_register(Status, REG_STATUS);
    527     }
    528 
    529     pub fn flush_tx(self: *const Self) void {
    530         const tx_data = [_]u8{CMD_FLUSH_TX};
    531         var rx_data: [1]u8 = undefined;
    532         spi.transfer(&self.spi_config, &tx_data, &rx_data);
    533     }
    534 
    535     pub fn flush_rx(self: *const Self) void {
    536         const tx_data = [_]u8{CMD_FLUSH_RX};
    537         var rx_data: [1]u8 = undefined;
    538         spi.transfer(&self.spi_config, &tx_data, &rx_data);
    539     }
    540 
    541     pub fn clear_irq(self: *const Self) void {
    542         self.write_register(Status, REG_STATUS, .{
    543             .rx_dr = true,
    544             .tx_ds = true,
    545             .maxed_retries = true,
    546         });
    547     }
    548 };