Skip to content
This repository has been archived by the owner on Feb 17, 2024. It is now read-only.

USB Device #40

Merged
merged 20 commits into from
Apr 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
63d7bd5
memcopy interface changed
r4gus Apr 12, 2023
e2bf7bb
usb device support added. started working on raw hid
r4gus Apr 12, 2023
db5436b
two usb examples added: the first echoes everything back using bulk e…
r4gus Apr 12, 2023
acafb44
two test scripts added. one is from tinyusb and the other from the pi…
r4gus Apr 12, 2023
0f12f28
made hal.zig the root for all test cases
r4gus Apr 13, 2023
c485e07
fido report descriptor added; plus required functions for creating RD…
r4gus Apr 13, 2023
8ff3175
UsbDeviceConfiguration now takes an optional report descriptor
r4gus Apr 13, 2023
1ceb11b
usb device now also returns device qualifier descriptor on request
r4gus Apr 14, 2023
4b0508c
hid descriptor and report descriptor grouped together in optional dev…
r4gus Apr 15, 2023
5f8f74d
usb_start_tx function now waits a few nops before setting the AVAILAB…
r4gus Apr 15, 2023
32e0362
UART debug output made optional
r4gus Apr 15, 2023
0195f90
merged zig embedded group main
r4gus Apr 16, 2023
90159d3
moved all usb data types into microzig.core.usb
r4gus Apr 18, 2023
bd0d722
rp2040 specific code extracted from usb_task()
r4gus Apr 19, 2023
0e4f02f
platform independent usb device code extracted into microzig repo
r4gus Apr 19, 2023
68c558d
Usb prefix removed
r4gus Apr 20, 2023
7a9e902
helper function added to convert from utf8 to utf16; the function ret…
r4gus Apr 20, 2023
d4ad8e2
helper functions now work with all control registers
r4gus Apr 21, 2023
9769cc6
Merge branch 'ZigEmbeddedGroup:main' into main
r4gus Apr 22, 2023
523218f
Merge branch 'main' into main
r4gus Apr 24, 2023
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
4 changes: 3 additions & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub fn build(b: *Builder) !void {

const pio_tests = b.addTest(.{
.root_source_file = .{
.path = "src/hal/pio.zig",
.path = "src/hal.zig",
},
.optimize = optimize,
});
Expand All @@ -69,6 +69,8 @@ pub const Examples = struct {
squarewave: *microzig.EmbeddedExecutable,
//uart_pins: microzig.EmbeddedExecutable,
flash_program: *microzig.EmbeddedExecutable,
usb_device: *microzig.EmbeddedExecutable,
usb_hid: *microzig.EmbeddedExecutable,
random: *microzig.EmbeddedExecutable,

pub fn init(b: *Builder, optimize: std.builtin.OptimizeMode) Examples {
Expand Down
29 changes: 29 additions & 0 deletions examples/scripts/hid_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env python3

# Install python3 HID package https://pypi.org/project/hid/
import hid

# default is TinyUSB (0xcafe), Adafruit (0x239a), RaspberryPi (0x2e8a), Espressif (0x303a) VID
USB_VID = (0xcafe, 0x239a, 0x2e8a, 0x303a)

print("VID list: " + ", ".join('%02x' % v for v in USB_VID))

for vid in USB_VID:
for dict in hid.enumerate(vid):
print(dict)
dev = hid.Device(dict['vendor_id'], dict['product_id'])
if dev:
while True:
inp = input("Send text to HID Device : ").encode('utf-8')
dev.write(inp)

x = 0
l = len(inp)
r = b""
while (x < l):
str_in = dev.read(64)
r += str_in
x += 64

print("Received from HID Device:\n", r)
print("hex:\n", r.hex())
48 changes: 48 additions & 0 deletions examples/scripts/usb_device_loopback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env python3

#
# Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
#
# SPDX-License-Identifier: BSD-3-Clause
#

# sudo pip3 install pyusb

import usb.core
import usb.util

# find our device
dev = usb.core.find(idVendor=0x0000, idProduct=0x0001)

# was it found?
if dev is None:
raise ValueError('Device not found')

# get an endpoint instance
cfg = dev.get_active_configuration()
intf = cfg[(0, 0)]

outep = usb.util.find_descriptor(
intf,
# match the first OUT endpoint
custom_match= \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_OUT)

inep = usb.util.find_descriptor(
intf,
# match the first IN endpoint
custom_match= \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_IN)

assert inep is not None
assert outep is not None

test_string = "Hello World!"
outep.write(test_string)
from_device = inep.read(len(test_string))

print("Device Says: {}".format(''.join([chr(x) for x in from_device])))
173 changes: 173 additions & 0 deletions examples/usb_device.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
const std = @import("std");
const microzig = @import("microzig");

const rp2040 = microzig.hal;
const flash = rp2040.flash;
const time = rp2040.time;
const gpio = rp2040.gpio;
const clocks = rp2040.clocks;
const usb = rp2040.usb;

const led = 25;
const uart_id = 0;
const baud_rate = 115200;
const uart_tx_pin = 0;
const uart_rx_pin = 1;

// First we define two callbacks that will be used by the endpoints we define next...
fn ep1_in_callback(dc: *usb.DeviceConfiguration, data: []const u8) void {
_ = data;
// The host has collected the data we repeated onto
// EP1! Set up to receive more data on EP1.
usb.Usb.callbacks.usb_start_rx(
dc.endpoints[2], // EP1_OUT_CFG,
64,
);
}

fn ep1_out_callback(dc: *usb.DeviceConfiguration, data: []const u8) void {
// We've gotten data from the host on our custom
// EP1! Set up EP1 to repeat it.
usb.Usb.callbacks.usb_start_tx(
dc.endpoints[3], // EP1_IN_CFG,
data,
);
}

// The endpoints EP0_IN and EP0_OUT are already defined but you can
// add your own endpoints to...
pub var EP1_OUT_CFG: usb.EndpointConfiguration = .{
.descriptor = &usb.EndpointDescriptor{
.length = @intCast(u8, @sizeOf(usb.EndpointDescriptor)),
.descriptor_type = usb.DescType.Endpoint,
.endpoint_address = usb.Dir.Out.endpoint(1),
.attributes = @enumToInt(usb.TransferType.Bulk),
.max_packet_size = 64,
.interval = 0,
},
.endpoint_control_index = 2,
.buffer_control_index = 3,
.data_buffer_index = 2,
.next_pid_1 = false,
// The callback will be executed if we got an interrupt on EP1_OUT
.callback = ep1_out_callback,
};

pub var EP1_IN_CFG: usb.EndpointConfiguration = .{
.descriptor = &usb.EndpointDescriptor{
.length = @intCast(u8, @sizeOf(usb.EndpointDescriptor)),
.descriptor_type = usb.DescType.Endpoint,
.endpoint_address = usb.Dir.In.endpoint(1),
.attributes = @enumToInt(usb.TransferType.Bulk),
.max_packet_size = 64,
.interval = 0,
},
.endpoint_control_index = 1,
.buffer_control_index = 2,
.data_buffer_index = 3,
.next_pid_1 = false,
// The callback will be executed if we got an interrupt on EP1_IN
.callback = ep1_in_callback,
};

// This is our device configuration
pub var DEVICE_CONFIGURATION: usb.DeviceConfiguration = .{
.device_descriptor = &.{
.length = @intCast(u8, @sizeOf(usb.DeviceDescriptor)),
.descriptor_type = usb.DescType.Device,
.bcd_usb = 0x0110,
.device_class = 0,
.device_subclass = 0,
.device_protocol = 0,
.max_packet_size0 = 64,
.vendor = 0,
.product = 1,
.bcd_device = 0,
.manufacturer_s = 1,
.product_s = 2,
.serial_s = 0,
.num_configurations = 1,
},
.interface_descriptor = &.{
.length = @intCast(u8, @sizeOf(usb.InterfaceDescriptor)),
.descriptor_type = usb.DescType.Interface,
.interface_number = 0,
.alternate_setting = 0,
// We have two endpoints (EP0 IN/OUT don't count)
.num_endpoints = 2,
.interface_class = 0xff,
.interface_subclass = 0,
.interface_protocol = 0,
.interface_s = 0,
},
.config_descriptor = &.{
.length = @intCast(u8, @sizeOf(usb.ConfigurationDescriptor)),
.descriptor_type = usb.DescType.Config,
.total_length = @intCast(u8, @sizeOf(usb.ConfigurationDescriptor) + @sizeOf(usb.InterfaceDescriptor) + @sizeOf(usb.EndpointDescriptor) + @sizeOf(usb.EndpointDescriptor)),
.num_interfaces = 1,
.configuration_value = 1,
.configuration_s = 0,
.attributes = 0xc0,
.max_power = 0x32,
},
.lang_descriptor = "\x04\x03\x09\x04", // length || string descriptor (0x03) || Engl (0x0409)
.descriptor_strings = &.{
// ugly unicode :|
"R\x00a\x00s\x00p\x00b\x00e\x00r\x00r\x00y\x00 \x00P\x00i\x00",
"P\x00i\x00c\x00o\x00 \x00T\x00e\x00s\x00t\x00 \x00D\x00e\x00v\x00i\x00c\x00e\x00",
},
// Here we pass all endpoints to the config
// Dont forget to pass EP0_[IN|OUT] in the order seen below!
.endpoints = .{
&usb.EP0_OUT_CFG,
&usb.EP0_IN_CFG,
&EP1_OUT_CFG,
&EP1_IN_CFG,
},
};

pub fn panic(message: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn {
std.log.err("panic: {s}", .{message});
@breakpoint();
while (true) {}
}

pub const std_options = struct {
pub const log_level = .debug;
pub const logFn = rp2040.uart.log;
};

pub fn main() !void {
gpio.reset();
gpio.init(led);
gpio.set_direction(led, .out);
gpio.put(led, 1);

const uart = rp2040.uart.UART.init(uart_id, .{
.baud_rate = baud_rate,
.tx_pin = uart_tx_pin,
.rx_pin = uart_rx_pin,
.clock_config = rp2040.clock_config,
});

rp2040.uart.init_logger(uart);

// First we initialize the USB clock
rp2040.usb.Usb.init_clk();
// Then initialize the USB device using the configuration defined above
rp2040.usb.Usb.init_device(&DEVICE_CONFIGURATION) catch unreachable;
var old: u64 = time.get_time_since_boot().us_since_boot;
var new: u64 = 0;
while (true) {
// You can now poll for USB events
rp2040.usb.Usb.task(
false, // debug output over UART [Y/n]
) catch unreachable;

new = time.get_time_since_boot().us_since_boot;
if (new - old > 500000) {
old = new;
gpio.toggle(led);
}
}
}
Loading