diff --git a/examples/raspberrypi/rp2xxx/build.zig b/examples/raspberrypi/rp2xxx/build.zig index aebbc3f4..e7ca851e 100644 --- a/examples/raspberrypi/rp2xxx/build.zig +++ b/examples/raspberrypi/rp2xxx/build.zig @@ -20,7 +20,6 @@ pub fn build(b: *std.Build) void { .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_pwm", .file = "src/rp2040_only/pwm.zig" }, .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_random", .file = "src/rp2040_only/random.zig" }, .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_rtc", .file = "src/rp2040_only/rtc.zig" }, - .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_spi-host", .file = "src/rp2040_only/spi_host.zig" }, .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_uart-echo", .file = "src/rp2040_only/uart_echo.zig" }, .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_uart-log", .file = "src/rp2040_only/uart_log.zig" }, .{ .target = mb.ports.rp2xxx.boards.raspberrypi.pico, .name = "pico_usb-hid", .file = "src/rp2040_only/usb_hid.zig" }, @@ -40,6 +39,8 @@ pub fn build(b: *std.Build) void { }; const chip_agnostic_examples: []const ChipAgnosticExample = &.{ + .{ .name = "spi-master", .file = "src/spi_master.zig" }, + .{ .name = "spi-slave", .file = "src/spi_slave.zig" }, .{ .name = "squarewave", .file = "src/squarewave.zig" }, .{ .name = "ws2812", .file = "src/ws2812.zig" }, .{ .name = "blinky", .file = "src/blinky.zig" }, diff --git a/examples/raspberrypi/rp2xxx/src/rp2040_only/spi_host.zig b/examples/raspberrypi/rp2xxx/src/rp2040_only/spi_host.zig deleted file mode 100644 index f39bf518..00000000 --- a/examples/raspberrypi/rp2xxx/src/rp2040_only/spi_host.zig +++ /dev/null @@ -1,67 +0,0 @@ -const std = @import("std"); -const microzig = @import("microzig"); - -const rp2xxx = microzig.hal; -const time = rp2xxx.time; -const gpio = rp2xxx.gpio; -const clocks = rp2xxx.clocks; -const peripherals = microzig.chip.peripherals; - -const BUF_LEN = 0x100; -const spi = rp2xxx.spi.instance.SPI0; - -// Communicate with another RP2040 over spi -// No device implementation yet in Zig, see Rpi Pico SDK for an example: https://github.com/raspberrypi/pico-examples/blob/master/spi/spi_master_slave/spi_slave/spi_slave.c -pub fn main() !void { - - // Note that CSN pin is manually controlled here rather than by the SPI peripheral. - // If CSN is configured to "SPI" function, it will get toggled after every data packet by the RP2040's - // SPI peripheral which is problematic for certain chips. It's generally easier to just toggle it - // manually before/after every transaction. - const csn = rp2xxx.gpio.num(1); - csn.set_function(.sio); - csn.set_direction(.out); - csn.put(1); - - // These will change depending on which GPIO pins you have your SPI device routed to - const hodi = rp2xxx.gpio.num(3); - const hido = rp2xxx.gpio.num(4); - const sck = rp2xxx.gpio.num(2); - inline for (&.{ hodi, hido, sck }) |pin| { - pin.set_function(.spi); - } - - // 8 bit data words - try spi.apply(.{ - .clock_config = rp2xxx.clock_config, - .data_width = .eight, - }); - var out_buf_eight: [BUF_LEN]u8 = .{ 0xAA, 0xBB, 0xCC, 0xDD } ** (BUF_LEN / 4); - var in_buf_eight: [BUF_LEN]u8 = undefined; - csn.put(0); - spi.transceive_blocking(u8, &out_buf_eight, &in_buf_eight); - csn.put(1); - - // 12 bit data words - try spi.apply(.{ - .clock_config = rp2xxx.clock_config, - .data_width = .twelve, - }); - var out_buf_twelve: [BUF_LEN]u12 = .{ 0xAA, 0xBB, 0xCC, 0xDD } ** (BUF_LEN / 4); - var in_buf_twelve: [BUF_LEN]u12 = undefined; - csn.put(0); - spi.transceive_blocking(u12, &out_buf_twelve, &in_buf_twelve); - csn.put(1); - - // Back to 8 bit mode - try spi.apply(.{ - .clock_config = rp2xxx.clock_config, - .data_width = .eight, - }); - while (true) { - csn.put(0); - spi.transceive_blocking(u8, &out_buf_eight, &in_buf_eight); - csn.put(1); - time.sleep_ms(1 * 1000); - } -} diff --git a/examples/raspberrypi/rp2xxx/src/spi_master.zig b/examples/raspberrypi/rp2xxx/src/spi_master.zig new file mode 100644 index 00000000..a0f9cb8d --- /dev/null +++ b/examples/raspberrypi/rp2xxx/src/spi_master.zig @@ -0,0 +1,37 @@ +const std = @import("std"); +const microzig = @import("microzig"); + +const rp2xxx = microzig.hal; +const time = rp2xxx.time; +const gpio = rp2xxx.gpio; + +const BUF_LEN = 0x100; +const spi = rp2xxx.spi.instance.SPI0; + +// These may change depending on which GPIO pins you have your SPI device routed to. +const CS_PIN = 17; +const SCK_PIN = 18; +// NOTE: rp2xxx doesn't label pins as MOSI/MISO. Instead a pin is always for +// either receiving or transmitting SPI data, no matter whether the chip is in +// master or slave mode. +const TX_PIN = 19; + +// Communicate with another RP2040 over spi +pub fn main() !void { + // Set pin functions for CS, SCK, RX + const csn = rp2xxx.gpio.num(CS_PIN); + const mosi = rp2xxx.gpio.num(TX_PIN); + const sck = rp2xxx.gpio.num(SCK_PIN); + inline for (&.{ csn, mosi, sck }) |pin| { + pin.set_function(.spi); + } + + try spi.apply(.{ .clock_config = rp2xxx.clock_config }); + var out_buf: [BUF_LEN]u8 = .{ 'h', 'e', 'y', ' ', 'y', 'o', 'u', '!' } ** (BUF_LEN / 8); + + while (true) { + std.log.info("Sending some data\n", .{}); + spi.write_blocking(u8, &out_buf); + time.sleep_ms(1 * 1000); + } +} diff --git a/examples/raspberrypi/rp2xxx/src/spi_slave.zig b/examples/raspberrypi/rp2xxx/src/spi_slave.zig new file mode 100644 index 00000000..8d208476 --- /dev/null +++ b/examples/raspberrypi/rp2xxx/src/spi_slave.zig @@ -0,0 +1,62 @@ +const std = @import("std"); +const microzig = @import("microzig"); + +const rp2xxx = microzig.hal; +const time = rp2xxx.time; +const gpio = rp2xxx.gpio; +const chip = rp2xxx.compatibility.chip; + +const BUF_LEN = 0x100; +const spi = rp2xxx.spi.instance.SPI0; + +const uart = rp2xxx.uart.instance.num(0); +const uart_baud_rate = 115200; +const uart_tx_pin = gpio.num(0); + +// These may change depending on which GPIO pins you have your SPI device routed to. +const CS_PIN = 17; +const SCK_PIN = 18; +// NOTE: rp2xxx doesn't label pins as MOSI/MISO. Instead a pin is always for +// either receiving or transmitting SPI data, no matter whether the chip is in +// master or slave mode. +const RX_PIN = 16; + +pub const microzig_options = .{ + .log_level = .debug, + .logFn = rp2xxx.uart.logFn, +}; + +pub fn main() !void { + // Set pin functions for CS, SCK, RX + const csn = gpio.num(CS_PIN); + const mosi = gpio.num(RX_PIN); + const sck = gpio.num(SCK_PIN); + inline for (&.{ csn, mosi, sck }) |pin| { + pin.set_function(.spi); + } + + switch (chip) { + .RP2040 => uart_tx_pin.set_function(.uart), + .RP2350 => uart_tx_pin.set_function(.uart_first), + } + uart.apply(.{ + .baud_rate = uart_baud_rate, + .clock_config = rp2xxx.clock_config, + }); + + rp2xxx.uart.init_logger(uart); + + std.log.info("Setting SPI as slave device", .{}); + spi.set_slave(true); + + try spi.apply(.{ .clock_config = rp2xxx.clock_config }); + var in_buf: [BUF_LEN]u8 = undefined; + + std.log.info("Reading", .{}); + + while (true) { + spi.read_blocking(u8, 0, &in_buf); + std.log.info("Got: {s}", .{in_buf}); + time.sleep_ms(1 * 1000); + } +} diff --git a/port/raspberrypi/rp2xxx/src/hal/spi.zig b/port/raspberrypi/rp2xxx/src/hal/spi.zig index aa9c147f..87629f00 100644 --- a/port/raspberrypi/rp2xxx/src/hal/spi.zig +++ b/port/raspberrypi/rp2xxx/src/hal/spi.zig @@ -4,7 +4,6 @@ const peripherals = microzig.chip.peripherals; const SPI0_reg = peripherals.SPI0; const SPI1_reg = peripherals.SPI1; -const gpio = @import("gpio.zig"); const clocks = @import("clocks.zig"); const resets = @import("resets.zig"); const time = @import("time.zig"); @@ -162,6 +161,17 @@ pub const SPI = enum(u1) { spi_regs.SSPCPSR.modify(.{ .CPSDVSR = 0 }); } + pub fn set_slave(spi: SPI, slave: bool) void { + const regs = spi.get_regs(); + // Disable SPI + regs.SSPCR1.modify(.{ .SSE = 0 }); + + regs.SSPCR1.modify(.{ .MS = @intFromBool(slave) }); + + // Re-enable SPI + regs.SSPCR1.modify(.{ .SSE = 1 }); + } + pub inline fn is_writable(spi: SPI) bool { return spi.get_regs().SSPSR.read().TNF == 1; } @@ -336,7 +346,7 @@ pub const SPI = enum(u1) { /// be 0, but some devices require a specific value here, /// e.g. SD cards expect 0xff /// - /// NOTE: This function is a vectored version of `write_blocking` and takes an array of arrays. + /// NOTE: This function is a vectored version of `read_blocking` and takes an array of arrays. /// This pattern allows one to create better zero-copy send routines as message prefixes and /// suffixes won't need to be concatenated/inserted to the original buffer, but can be managed /// in a separate memory.