-
Notifications
You must be signed in to change notification settings - Fork 99
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
Add blok_usb_serial example #34
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
//! # USB Serial Example for the Blok | ||
//! | ||
//! Creates a USB Serial device on a blok board, with the USB driver running in | ||
//! the USB interrupt and all writes being done in the main loop with the usage | ||
//! of critical section. | ||
//! | ||
//! This will create a USB Serial device and then write to it. | ||
//! A loop will write its current loop number 10 times after which | ||
//! the Blok will reset to usb boot mode. | ||
//! If "stop" is read by the serial device, the Blok will also | ||
//! reset to usb boot mode. | ||
//! | ||
//! See the `Cargo.toml` file for Copyright and license details. | ||
|
||
#![no_std] | ||
#![no_main] | ||
|
||
use boardsource_blok::{entry, hal}; | ||
use boardsource_blok::{ | ||
hal::{ | ||
clocks::{init_clocks_and_plls, Clock}, | ||
pac, | ||
pac::interrupt, | ||
timer::Timer, | ||
watchdog::Watchdog, | ||
Sio, | ||
}, | ||
Pins, XOSC_CRYSTAL_FREQ, | ||
}; | ||
use core::fmt::Write; | ||
use heapless::String; | ||
use panic_halt as _; | ||
use usb_device::{ | ||
bus::UsbBusAllocator, device::UsbDevice, device::UsbDeviceBuilder, device::UsbVidPid, | ||
}; | ||
use usbd_serial::SerialPort; | ||
|
||
// shared with the interrupt | ||
static mut USB_DEVICE: Option<UsbDevice<hal::usb::UsbBus>> = None; | ||
static mut USB_BUS: Option<UsbBusAllocator<hal::usb::UsbBus>> = None; | ||
static mut USB_SERIAL: Option<SerialPort<hal::usb::UsbBus>> = None; | ||
|
||
#[entry] | ||
fn main() -> ! { | ||
let mut pac = pac::Peripherals::take().unwrap(); | ||
|
||
let mut watchdog = Watchdog::new(pac.WATCHDOG); | ||
|
||
let clocks = init_clocks_and_plls( | ||
XOSC_CRYSTAL_FREQ, | ||
pac.XOSC, | ||
pac.CLOCKS, | ||
pac.PLL_SYS, | ||
pac.PLL_USB, | ||
&mut pac.RESETS, | ||
&mut watchdog, | ||
) | ||
.ok() | ||
.unwrap(); | ||
|
||
let sio = Sio::new(pac.SIO); | ||
let _pins = Pins::new( | ||
pac.IO_BANK0, | ||
pac.PADS_BANK0, | ||
sio.gpio_bank0, | ||
&mut pac.RESETS, | ||
); | ||
|
||
let _timer = Timer::new(pac.TIMER, &mut pac.RESETS); | ||
|
||
let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new( | ||
pac.USBCTRL_REGS, | ||
pac.USBCTRL_DPRAM, | ||
clocks.usb_clock, | ||
true, | ||
&mut pac.RESETS, | ||
)); | ||
unsafe { | ||
USB_BUS = Some(usb_bus); | ||
} | ||
|
||
let bus_ref = unsafe { USB_BUS.as_ref().unwrap() }; | ||
|
||
let serial = SerialPort::new(bus_ref); | ||
unsafe { | ||
USB_SERIAL = Some(serial); | ||
} | ||
|
||
let usb_device = UsbDeviceBuilder::new(bus_ref, UsbVidPid(0x1209, 0x0001)) | ||
.product("serial port") | ||
.device_class(2) // from: https://www.usb.org/defined-class-codes | ||
.build(); | ||
unsafe { | ||
USB_DEVICE = Some(usb_device); | ||
} | ||
|
||
unsafe { | ||
pac::NVIC::unmask(hal::pac::Interrupt::USBCTRL_IRQ); | ||
} | ||
|
||
let core = pac::CorePeripherals::take().unwrap(); | ||
let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()); | ||
|
||
let mut i: u8 = 0; | ||
|
||
loop { | ||
delay.delay_ms(2_000); | ||
|
||
let mut text: String<20> = String::new(); | ||
writeln!(&mut text, "loop number: {}\r\n", i).unwrap(); | ||
|
||
write_serial(text.as_bytes()); | ||
|
||
i += 1; | ||
if i >= 10 { | ||
hal::rom_data::reset_to_usb_boot(0, 0); | ||
} | ||
} | ||
} | ||
|
||
/// Writes to the serial port. | ||
/// | ||
/// We do this with interrupts disabled, to avoid a race hazard with the USB IRQ. | ||
fn write_serial(byte_array: &[u8]) { | ||
let _ = critical_section::with(|_| unsafe { | ||
USB_SERIAL.as_mut().map(|serial| serial.write(byte_array)) | ||
}) | ||
.unwrap(); | ||
} | ||
|
||
/// This function is called whenever the USB Hardware generates | ||
/// an Interrupt Request | ||
#[allow(non_snake_case)] | ||
#[interrupt] | ||
unsafe fn USBCTRL_IRQ() { | ||
Comment on lines
+133
to
+135
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIRC, this is not sufficient to remain compliant with USB's spec. See: https://docs.rs/usb-device/latest/usb_device/device/struct.UsbDevice.html#method.poll There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It says there that it should be called "preferably from an interrupt handler.". And I'm not sure where you read that it's not sufficient to remain compliant with USB's spec and what you mean by that. I'm quite new to programming, please help me out. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should be called preferably from an interrupt handler in order to minimise the latency between the event and its processing.
The USB interrupt is not guaranteed to be called at any interval (eg IIRC during usb-suspend, there's no USB interrupt). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry for all the questions and thank you for your time. If I understand correctly the usb interrupt shouldn't be used to poll the I'm once again sorry, but i don't understand what "it" and "this" refer to in your last sentence. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The usb-interrupt should be used to poll the
It's well possible. Some of the examples predate the
If you want to sleep in the main loop but still wake at set interval, you can use the
My apologies, let me rephrase that sentence:
I hope this all help. |
||
let usb_device = USB_DEVICE.as_mut().unwrap(); | ||
let serial = USB_SERIAL.as_mut().unwrap(); | ||
|
||
if usb_device.poll(&mut [serial]) { | ||
let mut buf = [0u8; 64]; | ||
|
||
match serial.read(&mut buf) { | ||
Err(_e) => {} | ||
Ok(_count) => { | ||
// gets the first 4 bytes of buf | ||
if &buf[0..4] == b"stop" { | ||
hal::rom_data::reset_to_usb_boot(0, 0); | ||
} else { | ||
let _ = serial.write("write stop to reset to usb boot\r\n".as_bytes()); | ||
} | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few unsafe block could be avoided if this were using https://docs.rs/critical-section/latest/critical_section/struct.Mutex.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this good practice? The serial interrupt example of the pico does also use these unsafe blocks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both
UsbDevice
andSerialPort
referenceUsbBus
which is why I was not able to store them in multipleMutexe
s.Is this is a readable and viable solution to avoid the unsafe blocks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
take
removes the value from its container (seeMutex::take
pointing toRefCell::take
.You should use
Mutex::borrow_ref_mut
instead.But yes, that's the idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll be creating a PR this evening (BST) to update the rp-pico's usb_serial_interrupt example to show a more comprehensive and up-to-date example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See: #37
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you very much