Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rp2040: Add spi slave example #391

Merged
merged 10 commits into from
Feb 17, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/raspberrypi/rp2xxx/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand All @@ -40,6 +39,8 @@ pub fn build(b: *std.Build) void {
};

const chip_agnostic_examples: []const ChipAgnosticExample = &.{
.{ .name = "spi-host", .file = "src/rp2040_only/spi_host.zig" },
.{ .name = "spi-slave", .file = "src/rp2040_only/spi_slave.zig" },
.{ .name = "squarewave", .file = "src/squarewave.zig" },
.{ .name = "ws2812", .file = "src/ws2812.zig" },
.{ .name = "blinky", .file = "src/blinky.zig" },
Expand Down
48 changes: 29 additions & 19 deletions examples/raspberrypi/rp2xxx/src/rp2040_only/spi_host.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,57 +10,67 @@ const peripherals = microzig.chip.peripherals;
const BUF_LEN = 0x100;
const spi = rp2xxx.spi.instance.SPI0;

// These will change depending on which GPIO pins you have your SPI device routed to.
const CS_PIN = 5;
const SCK_PIN = 2;
const MOSI_PIN = 3;
const MISO_PIN = 4;

// Communicate with another RP2040 over spi
// TODO Remove this once working!
// 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);
const csn = rp2xxx.gpio.num(CS_PIN);
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| {
const miso = rp2xxx.gpio.num(MISO_PIN);
const mosi = rp2xxx.gpio.num(MOSI_PIN);
const sck = rp2xxx.gpio.num(SCK_PIN);
inline for (&.{ mosi, miso, sck }) |pin| {
pin.set_function(.spi);
}

// 8 bit data words
try spi.apply(.{
.clock_config = rp2xxx.clock_config,
.data_width = .eight,
.baud_rate = 500_000,
});
var out_buf_eight: [BUF_LEN]u8 = .{ 0xAA, 0xBB, 0xCC, 0xDD } ** (BUF_LEN / 4);
var in_buf_eight: [BUF_LEN]u8 = undefined;
var out_buf_eight: [BUF_LEN]u8 = .{ 'h', 'e', 'l', 'o' } ** (BUF_LEN / 4);
csn.put(0);
spi.transceive_blocking(u8, &out_buf_eight, &in_buf_eight);
spi.write_blocking(u8, out_buf_eight[0..4]);
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);
// // 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 out_buf_twelve: [BUF_LEN]u12 = .{ 'h', 'e', 'l', 'o' } ** (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,
.baud_rate = 500_000,
});
while (true) {
csn.put(0);
spi.transceive_blocking(u8, &out_buf_eight, &in_buf_eight);
std.log.info("Sending some data\n", .{});
spi.write_blocking(u8, &out_buf_eight);
csn.put(1);
time.sleep_ms(1 * 1000);
}
Expand Down
76 changes: 76 additions & 0 deletions examples/raspberrypi/rp2xxx/src/rp2040_only/spi_slave.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
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 baud_rate = 115200;
const uart_tx_pin = gpio.num(0);

// These will 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);
}

inline for (&.{uart_tx_pin}) |pin| {
switch (chip) {
.RP2040 => pin.set_function(.uart),
.RP2350 => pin.set_function(.uart_first),
}
}
uart.apply(.{
.baud_rate = baud_rate,
.clock_config = rp2xxx.clock_config,
});

rp2xxx.uart.init_logger(uart);

std.log.info("Setting SPI as slave device", .{});
spi.set_slave(true);

// Back to 8 bit mode
spi.apply(.{
.clock_config = rp2xxx.clock_config,
.data_width = .eight,
}) catch {};
var in_buf_eight: [BUF_LEN]u8 = undefined;
std.log.info("Reading", .{});
spi.read_blocking(u8, 0, &in_buf_eight);

std.log.info("Got: {s}", .{in_buf_eight});

// Back to 8 bit mode
try spi.apply(.{
.clock_config = rp2xxx.clock_config,
.data_width = .eight,
});

while (true) {
spi.read_blocking(u8, 0, &in_buf_eight);
std.log.info("Got: {s}", .{in_buf_eight});
time.sleep_ms(1 * 1000);
}
}
14 changes: 12 additions & 2 deletions port/raspberrypi/rp2xxx/src/hal/spi.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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.
Expand Down
Loading