const std = @import("std"); const microzig = @import("microzig"); const rp2xxx = microzig.hal; const flash = rp2xxx.flash; const time = rp2xxx.time; const gpio = rp2xxx.gpio; const usb = rp2xxx.usb; const led = gpio.num(25); const uart = rp2xxx.uart.instance.num(0); const baud_rate = 115200; const uart_tx_pin = gpio.num(0); const uart_rx_pin = gpio.num(1); const usb_dev = rp2xxx.usb.Usb(.{}); const usb_config_len = usb.templates.config_descriptor_len + usb.templates.cdc_descriptor_len; const usb_config_descriptor = usb.templates.config_descriptor(1, 2, 0, usb_config_len, 0xc0, 100) ++ usb.templates.cdc_descriptor(0, 4, usb.Endpoint.to_address(1, .In), 8, usb.Endpoint.to_address(2, .Out), usb.Endpoint.to_address(2, .In), 64); var driver_cdc: usb.cdc.CdcClassDriver(usb_dev) = .{}; var drivers = [_]usb.types.UsbClassDriver{driver_cdc.driver()}; // This is our device configuration pub var DEVICE_CONFIGURATION: usb.DeviceConfiguration = .{ .device_descriptor = &.{ .descriptor_type = usb.DescType.Device, .bcd_usb = 0x0200, .device_class = 0xEF, .device_subclass = 2, .device_protocol = 1, .max_packet_size0 = 64, .vendor = 0x2E8A, .product = 0x000a, .bcd_device = 0x0100, .manufacturer_s = 1, .product_s = 2, .serial_s = 0, .num_configurations = 1, }, .config_descriptor = &usb_config_descriptor, .lang_descriptor = "\x04\x03\x09\x04", // length || string descriptor (0x03) || Engl (0x0409) .descriptor_strings = &.{ &usb.utils.utf8_to_utf16_le("Raspberry Pi"), &usb.utils.utf8_to_utf16_le("Pico Test Device"), &usb.utils.utf8_to_utf16_le("someserial"), &usb.utils.utf8_to_utf16_le("Board CDC"), }, .drivers = &drivers, }; pub fn panic(message: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn { std.log.err("panic: {s}", .{message}); @breakpoint(); while (true) {} } pub const microzig_options = microzig.Options{ .log_level = .debug, .logFn = rp2xxx.uart.log, }; pub fn main() !void { led.set_function(.sio); led.set_direction(.out); led.put(1); inline for (&.{ uart_tx_pin, uart_rx_pin }) |pin| { pin.set_function(.uart); } uart.apply(.{ .baud_rate = baud_rate, .clock_config = rp2xxx.clock_config, }); rp2xxx.uart.init_logger(uart); // First we initialize the USB clock usb_dev.init_clk(); // Then initialize the USB device using the configuration defined above usb_dev.init_device(&DEVICE_CONFIGURATION) catch unreachable; var old: u64 = time.get_time_since_boot().to_us(); var new: u64 = 0; var i: u32 = 0; while (true) { // You can now poll for USB events usb_dev.task( false, // debug output over UART [Y/n] ) catch unreachable; new = time.get_time_since_boot().to_us(); if (new - old > 500000) { old = new; led.toggle(); i += 1; std.log.info("cdc test: {}\r\n", .{i}); usb_cdc_write("This is very very long text sent from RP Pico by USB CDC to your device: {}\r\n", .{i}); } // read and print host command if present const message = usb_cdc_read(); if (message.len > 0) { usb_cdc_write("Your message to me was: {s}\r\n", .{message}); } } } var usb_tx_buff: [1024]u8 = undefined; // Transfer data to host // NOTE: After each USB chunk transfer, we have to call the USB task so that bus TX events can be handled pub fn usb_cdc_write(comptime fmt: []const u8, args: anytype) void { const text = std.fmt.bufPrint(&usb_tx_buff, fmt, args) catch &.{}; var write_buff = text; while (write_buff.len > 0) { write_buff = driver_cdc.write(write_buff); usb_dev.task(false) catch unreachable; } // Short messages are not sent right away; instead, they accumulate in a buffer, so we have to force a flush to send them _ = driver_cdc.write_flush(); usb_dev.task(false) catch unreachable; } var usb_rx_buff: [1024]u8 = undefined; // Receive data from host // NOTE: Read code was not tested extensively. In case of issues, try to call USB task before every read operation pub fn usb_cdc_read() []const u8 { var total_read: usize = 0; var read_buff: []u8 = usb_rx_buff[0..]; while (true) { const len = driver_cdc.read(read_buff); read_buff = read_buff[len..]; total_read += len; if (len == 0) break; } return usb_rx_buff[0..total_read]; }