const std = @import("std");
const microzig = @import("microzig");
const rp2xxx = microzig.hal;
const time = rp2xxx.time;
const gpio = rp2xxx.gpio;
const usb = rp2xxx.usb;
const adc = rp2xxx.adc;
// UART
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 {
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;
adc.configure_gpio_pin_num(.ain0);
adc.apply(.{
.temp_sensor_enabled = false,
});
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;
const sample = adc.convert_one_shot_blocking(.ain0) catch {
std.log.err("conversion failed!", .{});
continue;
};
usb_cdc_write("Potmeter state: {}\r\n", .{sample});
}
// 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];
}