diff options
| author | Dario Nieuwenhuis <[email protected]> | 2022-03-09 01:34:35 +0100 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2022-04-06 05:38:11 +0200 |
| commit | 37598a5b3792ec1b763b5c16fe422c9e1347d7d6 (patch) | |
| tree | 558d06652cd95998e2d7b4eebc8e85329aa17a6b /examples | |
| parent | c1b382296434e762d16a36d658d2f308358e3f87 (diff) | |
wip: experimental async usb stack
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/nrf/Cargo.toml | 5 | ||||
| -rw-r--r-- | examples/nrf/src/bin/usb/cdc_acm.rs | 356 | ||||
| -rw-r--r-- | examples/nrf/src/bin/usb/main.rs | 53 | ||||
| -rw-r--r-- | examples/nrf/src/bin/usb_uart.rs | 89 | ||||
| -rw-r--r-- | examples/nrf/src/bin/usb_uart_io.rs | 66 |
5 files changed, 411 insertions, 158 deletions
diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index a704eb3bc..fb846b3a9 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml | |||
| @@ -10,7 +10,8 @@ nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits"] | |||
| 10 | 10 | ||
| 11 | [dependencies] | 11 | [dependencies] |
| 12 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } | 12 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } |
| 13 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } | 13 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } |
| 14 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 14 | 15 | ||
| 15 | defmt = "0.3" | 16 | defmt = "0.3" |
| 16 | defmt-rtt = "0.3" | 17 | defmt-rtt = "0.3" |
| @@ -22,5 +23,3 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa | |||
| 22 | rand = { version = "0.8.4", default-features = false } | 23 | rand = { version = "0.8.4", default-features = false } |
| 23 | embedded-storage = "0.3.0" | 24 | embedded-storage = "0.3.0" |
| 24 | 25 | ||
| 25 | usb-device = "0.2" | ||
| 26 | usbd-serial = "0.1.1" | ||
diff --git a/examples/nrf/src/bin/usb/cdc_acm.rs b/examples/nrf/src/bin/usb/cdc_acm.rs new file mode 100644 index 000000000..345d00389 --- /dev/null +++ b/examples/nrf/src/bin/usb/cdc_acm.rs | |||
| @@ -0,0 +1,356 @@ | |||
| 1 | use core::convert::TryInto; | ||
| 2 | use core::mem; | ||
| 3 | use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut, ReadError, WriteError}; | ||
| 4 | use embassy_usb::{driver::Driver, types::*, UsbDeviceBuilder}; | ||
| 5 | |||
| 6 | /// This should be used as `device_class` when building the `UsbDevice`. | ||
| 7 | pub const USB_CLASS_CDC: u8 = 0x02; | ||
| 8 | |||
| 9 | const USB_CLASS_CDC_DATA: u8 = 0x0a; | ||
| 10 | const CDC_SUBCLASS_ACM: u8 = 0x02; | ||
| 11 | const CDC_PROTOCOL_NONE: u8 = 0x00; | ||
| 12 | |||
| 13 | const CS_INTERFACE: u8 = 0x24; | ||
| 14 | const CDC_TYPE_HEADER: u8 = 0x00; | ||
| 15 | const CDC_TYPE_CALL_MANAGEMENT: u8 = 0x01; | ||
| 16 | const CDC_TYPE_ACM: u8 = 0x02; | ||
| 17 | const CDC_TYPE_UNION: u8 = 0x06; | ||
| 18 | |||
| 19 | const REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00; | ||
| 20 | #[allow(unused)] | ||
| 21 | const REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01; | ||
| 22 | const REQ_SET_LINE_CODING: u8 = 0x20; | ||
| 23 | const REQ_GET_LINE_CODING: u8 = 0x21; | ||
| 24 | const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; | ||
| 25 | |||
| 26 | /// Packet level implementation of a CDC-ACM serial port. | ||
| 27 | /// | ||
| 28 | /// This class can be used directly and it has the least overhead due to directly reading and | ||
| 29 | /// writing USB packets with no intermediate buffers, but it will not act like a stream-like serial | ||
| 30 | /// port. The following constraints must be followed if you use this class directly: | ||
| 31 | /// | ||
| 32 | /// - `read_packet` must be called with a buffer large enough to hold max_packet_size bytes, and the | ||
| 33 | /// method will return a `WouldBlock` error if there is no packet to be read. | ||
| 34 | /// - `write_packet` must not be called with a buffer larger than max_packet_size bytes, and the | ||
| 35 | /// method will return a `WouldBlock` error if the previous packet has not been sent yet. | ||
| 36 | /// - If you write a packet that is exactly max_packet_size bytes long, it won't be processed by the | ||
| 37 | /// host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP) | ||
| 38 | /// can be sent if there is no other data to send. This is because USB bulk transactions must be | ||
| 39 | /// terminated with a short packet, even if the bulk endpoint is used for stream-like data. | ||
| 40 | pub struct CdcAcmClass<'d, D: Driver<'d>> { | ||
| 41 | comm_if: InterfaceNumber, | ||
| 42 | comm_ep: D::EndpointIn, | ||
| 43 | data_if: InterfaceNumber, | ||
| 44 | read_ep: D::EndpointOut, | ||
| 45 | write_ep: D::EndpointIn, | ||
| 46 | line_coding: LineCoding, | ||
| 47 | dtr: bool, | ||
| 48 | rts: bool, | ||
| 49 | } | ||
| 50 | |||
| 51 | impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { | ||
| 52 | /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For | ||
| 53 | /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. | ||
| 54 | pub fn new(builder: &mut UsbDeviceBuilder<'d, D>, max_packet_size: u16) -> Self { | ||
| 55 | let comm_if = builder.alloc_interface(); | ||
| 56 | let comm_ep = builder.alloc_interrupt_endpoint_in(8, 255); | ||
| 57 | let data_if = builder.alloc_interface(); | ||
| 58 | let read_ep = builder.alloc_bulk_endpoint_out(max_packet_size); | ||
| 59 | let write_ep = builder.alloc_bulk_endpoint_in(max_packet_size); | ||
| 60 | |||
| 61 | builder | ||
| 62 | .config_descriptor | ||
| 63 | .iad( | ||
| 64 | comm_if, | ||
| 65 | 2, | ||
| 66 | USB_CLASS_CDC, | ||
| 67 | CDC_SUBCLASS_ACM, | ||
| 68 | CDC_PROTOCOL_NONE, | ||
| 69 | ) | ||
| 70 | .unwrap(); | ||
| 71 | |||
| 72 | builder | ||
| 73 | .config_descriptor | ||
| 74 | .interface(comm_if, USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE) | ||
| 75 | .unwrap(); | ||
| 76 | |||
| 77 | builder | ||
| 78 | .config_descriptor | ||
| 79 | .write( | ||
| 80 | CS_INTERFACE, | ||
| 81 | &[ | ||
| 82 | CDC_TYPE_HEADER, // bDescriptorSubtype | ||
| 83 | 0x10, | ||
| 84 | 0x01, // bcdCDC (1.10) | ||
| 85 | ], | ||
| 86 | ) | ||
| 87 | .unwrap(); | ||
| 88 | |||
| 89 | builder | ||
| 90 | .config_descriptor | ||
| 91 | .write( | ||
| 92 | CS_INTERFACE, | ||
| 93 | &[ | ||
| 94 | CDC_TYPE_ACM, // bDescriptorSubtype | ||
| 95 | 0x00, // bmCapabilities | ||
| 96 | ], | ||
| 97 | ) | ||
| 98 | .unwrap(); | ||
| 99 | |||
| 100 | builder | ||
| 101 | .config_descriptor | ||
| 102 | .write( | ||
| 103 | CS_INTERFACE, | ||
| 104 | &[ | ||
| 105 | CDC_TYPE_UNION, // bDescriptorSubtype | ||
| 106 | comm_if.into(), // bControlInterface | ||
| 107 | data_if.into(), // bSubordinateInterface | ||
| 108 | ], | ||
| 109 | ) | ||
| 110 | .unwrap(); | ||
| 111 | |||
| 112 | builder | ||
| 113 | .config_descriptor | ||
| 114 | .write( | ||
| 115 | CS_INTERFACE, | ||
| 116 | &[ | ||
| 117 | CDC_TYPE_CALL_MANAGEMENT, // bDescriptorSubtype | ||
| 118 | 0x00, // bmCapabilities | ||
| 119 | data_if.into(), // bDataInterface | ||
| 120 | ], | ||
| 121 | ) | ||
| 122 | .unwrap(); | ||
| 123 | |||
| 124 | builder.config_descriptor.endpoint(comm_ep.info()).unwrap(); | ||
| 125 | |||
| 126 | builder | ||
| 127 | .config_descriptor | ||
| 128 | .interface(data_if, USB_CLASS_CDC_DATA, 0x00, 0x00) | ||
| 129 | .unwrap(); | ||
| 130 | |||
| 131 | builder.config_descriptor.endpoint(write_ep.info()).unwrap(); | ||
| 132 | builder.config_descriptor.endpoint(read_ep.info()).unwrap(); | ||
| 133 | |||
| 134 | CdcAcmClass { | ||
| 135 | comm_if, | ||
| 136 | comm_ep, | ||
| 137 | data_if, | ||
| 138 | read_ep, | ||
| 139 | write_ep, | ||
| 140 | line_coding: LineCoding { | ||
| 141 | stop_bits: StopBits::One, | ||
| 142 | data_bits: 8, | ||
| 143 | parity_type: ParityType::None, | ||
| 144 | data_rate: 8_000, | ||
| 145 | }, | ||
| 146 | dtr: false, | ||
| 147 | rts: false, | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | /// Gets the maximum packet size in bytes. | ||
| 152 | pub fn max_packet_size(&self) -> u16 { | ||
| 153 | // The size is the same for both endpoints. | ||
| 154 | self.read_ep.info().max_packet_size | ||
| 155 | } | ||
| 156 | |||
| 157 | /// Gets the current line coding. The line coding contains information that's mainly relevant | ||
| 158 | /// for USB to UART serial port emulators, and can be ignored if not relevant. | ||
| 159 | pub fn line_coding(&self) -> &LineCoding { | ||
| 160 | &self.line_coding | ||
| 161 | } | ||
| 162 | |||
| 163 | /// Gets the DTR (data terminal ready) state | ||
| 164 | pub fn dtr(&self) -> bool { | ||
| 165 | self.dtr | ||
| 166 | } | ||
| 167 | |||
| 168 | /// Gets the RTS (request to send) state | ||
| 169 | pub fn rts(&self) -> bool { | ||
| 170 | self.rts | ||
| 171 | } | ||
| 172 | |||
| 173 | /// Writes a single packet into the IN endpoint. | ||
| 174 | pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), WriteError> { | ||
| 175 | self.write_ep.write(data).await | ||
| 176 | } | ||
| 177 | |||
| 178 | /// Reads a single packet from the OUT endpoint. | ||
| 179 | pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, ReadError> { | ||
| 180 | self.read_ep.read(data).await | ||
| 181 | } | ||
| 182 | |||
| 183 | /// Gets the address of the IN endpoint. | ||
| 184 | pub(crate) fn write_ep_address(&self) -> EndpointAddress { | ||
| 185 | self.write_ep.info().addr | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | /* | ||
| 190 | impl<B: UsbBus> UsbClass<B> for CdcAcmClass<'_, B> { | ||
| 191 | fn get_configuration_descriptors(&self, builder.config_descriptor: &mut Descriptorbuilder.config_descriptor) -> Result<()> { | ||
| 192 | |||
| 193 | Ok(()) | ||
| 194 | } | ||
| 195 | |||
| 196 | fn reset(&mut self) { | ||
| 197 | self.line_coding = LineCoding::default(); | ||
| 198 | self.dtr = false; | ||
| 199 | self.rts = false; | ||
| 200 | } | ||
| 201 | |||
| 202 | fn control_in(&mut self, xfer: ControlIn<B>) { | ||
| 203 | let req = xfer.request(); | ||
| 204 | |||
| 205 | if !(req.request_type == control::RequestType::Class | ||
| 206 | && req.recipient == control::Recipient::Interface | ||
| 207 | && req.index == u8::from(self.comm_if) as u16) | ||
| 208 | { | ||
| 209 | return; | ||
| 210 | } | ||
| 211 | |||
| 212 | match req.request { | ||
| 213 | // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below. | ||
| 214 | REQ_GET_LINE_CODING if req.length == 7 => { | ||
| 215 | xfer.accept(|data| { | ||
| 216 | data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes()); | ||
| 217 | data[4] = self.line_coding.stop_bits as u8; | ||
| 218 | data[5] = self.line_coding.parity_type as u8; | ||
| 219 | data[6] = self.line_coding.data_bits; | ||
| 220 | |||
| 221 | Ok(7) | ||
| 222 | }) | ||
| 223 | .ok(); | ||
| 224 | } | ||
| 225 | _ => { | ||
| 226 | xfer.reject().ok(); | ||
| 227 | } | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | fn control_out(&mut self, xfer: ControlOut<B>) { | ||
| 232 | let req = xfer.request(); | ||
| 233 | |||
| 234 | if !(req.request_type == control::RequestType::Class | ||
| 235 | && req.recipient == control::Recipient::Interface | ||
| 236 | && req.index == u8::from(self.comm_if) as u16) | ||
| 237 | { | ||
| 238 | return; | ||
| 239 | } | ||
| 240 | |||
| 241 | match req.request { | ||
| 242 | REQ_SEND_ENCAPSULATED_COMMAND => { | ||
| 243 | // We don't actually support encapsulated commands but pretend we do for standards | ||
| 244 | // compatibility. | ||
| 245 | xfer.accept().ok(); | ||
| 246 | } | ||
| 247 | REQ_SET_LINE_CODING if xfer.data().len() >= 7 => { | ||
| 248 | self.line_coding.data_rate = | ||
| 249 | u32::from_le_bytes(xfer.data()[0..4].try_into().unwrap()); | ||
| 250 | self.line_coding.stop_bits = xfer.data()[4].into(); | ||
| 251 | self.line_coding.parity_type = xfer.data()[5].into(); | ||
| 252 | self.line_coding.data_bits = xfer.data()[6]; | ||
| 253 | |||
| 254 | xfer.accept().ok(); | ||
| 255 | } | ||
| 256 | REQ_SET_CONTROL_LINE_STATE => { | ||
| 257 | self.dtr = (req.value & 0x0001) != 0; | ||
| 258 | self.rts = (req.value & 0x0002) != 0; | ||
| 259 | |||
| 260 | xfer.accept().ok(); | ||
| 261 | } | ||
| 262 | _ => { | ||
| 263 | xfer.reject().ok(); | ||
| 264 | } | ||
| 265 | }; | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | */ | ||
| 270 | |||
| 271 | /// Number of stop bits for LineCoding | ||
| 272 | #[derive(Copy, Clone, PartialEq, Eq)] | ||
| 273 | pub enum StopBits { | ||
| 274 | /// 1 stop bit | ||
| 275 | One = 0, | ||
| 276 | |||
| 277 | /// 1.5 stop bits | ||
| 278 | OnePointFive = 1, | ||
| 279 | |||
| 280 | /// 2 stop bits | ||
| 281 | Two = 2, | ||
| 282 | } | ||
| 283 | |||
| 284 | impl From<u8> for StopBits { | ||
| 285 | fn from(value: u8) -> Self { | ||
| 286 | if value <= 2 { | ||
| 287 | unsafe { mem::transmute(value) } | ||
| 288 | } else { | ||
| 289 | StopBits::One | ||
| 290 | } | ||
| 291 | } | ||
| 292 | } | ||
| 293 | |||
| 294 | /// Parity for LineCoding | ||
| 295 | #[derive(Copy, Clone, PartialEq, Eq)] | ||
| 296 | pub enum ParityType { | ||
| 297 | None = 0, | ||
| 298 | Odd = 1, | ||
| 299 | Event = 2, | ||
| 300 | Mark = 3, | ||
| 301 | Space = 4, | ||
| 302 | } | ||
| 303 | |||
| 304 | impl From<u8> for ParityType { | ||
| 305 | fn from(value: u8) -> Self { | ||
| 306 | if value <= 4 { | ||
| 307 | unsafe { mem::transmute(value) } | ||
| 308 | } else { | ||
| 309 | ParityType::None | ||
| 310 | } | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | /// Line coding parameters | ||
| 315 | /// | ||
| 316 | /// This is provided by the host for specifying the standard UART parameters such as baud rate. Can | ||
| 317 | /// be ignored if you don't plan to interface with a physical UART. | ||
| 318 | pub struct LineCoding { | ||
| 319 | stop_bits: StopBits, | ||
| 320 | data_bits: u8, | ||
| 321 | parity_type: ParityType, | ||
| 322 | data_rate: u32, | ||
| 323 | } | ||
| 324 | |||
| 325 | impl LineCoding { | ||
| 326 | /// Gets the number of stop bits for UART communication. | ||
| 327 | pub fn stop_bits(&self) -> StopBits { | ||
| 328 | self.stop_bits | ||
| 329 | } | ||
| 330 | |||
| 331 | /// Gets the number of data bits for UART communication. | ||
| 332 | pub fn data_bits(&self) -> u8 { | ||
| 333 | self.data_bits | ||
| 334 | } | ||
| 335 | |||
| 336 | /// Gets the parity type for UART communication. | ||
| 337 | pub fn parity_type(&self) -> ParityType { | ||
| 338 | self.parity_type | ||
| 339 | } | ||
| 340 | |||
| 341 | /// Gets the data rate in bits per second for UART communication. | ||
| 342 | pub fn data_rate(&self) -> u32 { | ||
| 343 | self.data_rate | ||
| 344 | } | ||
| 345 | } | ||
| 346 | |||
| 347 | impl Default for LineCoding { | ||
| 348 | fn default() -> Self { | ||
| 349 | LineCoding { | ||
| 350 | stop_bits: StopBits::One, | ||
| 351 | data_bits: 8, | ||
| 352 | parity_type: ParityType::None, | ||
| 353 | data_rate: 8_000, | ||
| 354 | } | ||
| 355 | } | ||
| 356 | } | ||
diff --git a/examples/nrf/src/bin/usb/main.rs b/examples/nrf/src/bin/usb/main.rs new file mode 100644 index 000000000..21ca2ba4f --- /dev/null +++ b/examples/nrf/src/bin/usb/main.rs | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | #[path = "../../example_common.rs"] | ||
| 6 | mod example_common; | ||
| 7 | |||
| 8 | mod cdc_acm; | ||
| 9 | |||
| 10 | use core::mem; | ||
| 11 | use defmt::*; | ||
| 12 | use embassy::executor::Spawner; | ||
| 13 | use embassy_nrf::interrupt; | ||
| 14 | use embassy_nrf::pac; | ||
| 15 | use embassy_nrf::usb::{self, Driver}; | ||
| 16 | use embassy_nrf::Peripherals; | ||
| 17 | use embassy_usb::{Config, UsbDeviceBuilder}; | ||
| 18 | |||
| 19 | use crate::cdc_acm::CdcAcmClass; | ||
| 20 | |||
| 21 | #[embassy::main] | ||
| 22 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 23 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 24 | let power: pac::POWER = unsafe { mem::transmute(()) }; | ||
| 25 | |||
| 26 | info!("Enabling ext hfosc..."); | ||
| 27 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | ||
| 28 | while clock.events_hfclkstarted.read().bits() != 1 {} | ||
| 29 | |||
| 30 | info!("Waiting for vbus..."); | ||
| 31 | while !power.usbregstatus.read().vbusdetect().is_vbus_present() {} | ||
| 32 | info!("vbus OK"); | ||
| 33 | |||
| 34 | let irq = interrupt::take!(USBD); | ||
| 35 | let driver = Driver::new(p.USBD, irq); | ||
| 36 | let config = Config::new(0xc0de, 0xcafe); | ||
| 37 | let mut device_descriptor = [0; 256]; | ||
| 38 | let mut config_descriptor = [0; 256]; | ||
| 39 | let mut bos_descriptor = [0; 256]; | ||
| 40 | |||
| 41 | let mut builder = UsbDeviceBuilder::new( | ||
| 42 | driver, | ||
| 43 | config, | ||
| 44 | &mut device_descriptor, | ||
| 45 | &mut config_descriptor, | ||
| 46 | &mut bos_descriptor, | ||
| 47 | ); | ||
| 48 | |||
| 49 | let mut class = CdcAcmClass::new(&mut builder, 64); | ||
| 50 | |||
| 51 | let mut usb = builder.build(); | ||
| 52 | usb.run().await; | ||
| 53 | } | ||
diff --git a/examples/nrf/src/bin/usb_uart.rs b/examples/nrf/src/bin/usb_uart.rs deleted file mode 100644 index d283dccd1..000000000 --- a/examples/nrf/src/bin/usb_uart.rs +++ /dev/null | |||
| @@ -1,89 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{info, unwrap}; | ||
| 6 | use embassy::executor::Spawner; | ||
| 7 | use embassy::interrupt::InterruptExt; | ||
| 8 | use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; | ||
| 9 | use embassy_nrf::usb::{State, Usb, UsbBus, UsbSerial}; | ||
| 10 | use embassy_nrf::{interrupt, Peripherals}; | ||
| 11 | use futures::pin_mut; | ||
| 12 | use usb_device::device::{UsbDeviceBuilder, UsbVidPid}; | ||
| 13 | |||
| 14 | use defmt_rtt as _; // global logger | ||
| 15 | use panic_probe as _; // print out panic messages | ||
| 16 | |||
| 17 | #[embassy::main] | ||
| 18 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 19 | let mut rx_buffer = [0u8; 64]; | ||
| 20 | // we send back input + cr + lf | ||
| 21 | let mut tx_buffer = [0u8; 66]; | ||
| 22 | |||
| 23 | let usb_bus = UsbBus::new(p.USBD); | ||
| 24 | |||
| 25 | let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer); | ||
| 26 | |||
| 27 | let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) | ||
| 28 | .manufacturer("Fake company") | ||
| 29 | .product("Serial port") | ||
| 30 | .serial_number("TEST") | ||
| 31 | .device_class(0x02) | ||
| 32 | .build(); | ||
| 33 | |||
| 34 | let irq = interrupt::take!(USBD); | ||
| 35 | irq.set_priority(interrupt::Priority::P3); | ||
| 36 | |||
| 37 | let mut state = State::new(); | ||
| 38 | let usb = unsafe { Usb::new(&mut state, device, serial, irq) }; | ||
| 39 | pin_mut!(usb); | ||
| 40 | |||
| 41 | let (mut reader, mut writer) = usb.as_ref().take_serial_0(); | ||
| 42 | |||
| 43 | info!("usb initialized!"); | ||
| 44 | |||
| 45 | unwrap!( | ||
| 46 | writer | ||
| 47 | .write_all(b"\r\nInput returned upper cased on CR+LF\r\n") | ||
| 48 | .await | ||
| 49 | ); | ||
| 50 | |||
| 51 | let mut buf = [0u8; 64]; | ||
| 52 | loop { | ||
| 53 | let mut n = 0; | ||
| 54 | |||
| 55 | async { | ||
| 56 | loop { | ||
| 57 | let char = unwrap!(reader.read_byte().await); | ||
| 58 | |||
| 59 | // throw away, read more on cr, exit on lf | ||
| 60 | if char == b'\r' { | ||
| 61 | continue; | ||
| 62 | } else if char == b'\n' { | ||
| 63 | break; | ||
| 64 | } | ||
| 65 | |||
| 66 | buf[n] = char; | ||
| 67 | n += 1; | ||
| 68 | |||
| 69 | // stop if we're out of room | ||
| 70 | if n == buf.len() { | ||
| 71 | break; | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 75 | .await; | ||
| 76 | |||
| 77 | if n > 0 { | ||
| 78 | for char in buf[..n].iter_mut() { | ||
| 79 | // upper case | ||
| 80 | if 0x61 <= *char && *char <= 0x7a { | ||
| 81 | *char &= !0x20; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | unwrap!(writer.write_all(&buf[..n]).await); | ||
| 85 | unwrap!(writer.write_all(b"\r\n").await); | ||
| 86 | unwrap!(writer.flush().await); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | } | ||
diff --git a/examples/nrf/src/bin/usb_uart_io.rs b/examples/nrf/src/bin/usb_uart_io.rs deleted file mode 100644 index ef2629844..000000000 --- a/examples/nrf/src/bin/usb_uart_io.rs +++ /dev/null | |||
| @@ -1,66 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{info, unwrap}; | ||
| 6 | use embassy::executor::Spawner; | ||
| 7 | use embassy::interrupt::InterruptExt; | ||
| 8 | use embassy::io::{read_line, AsyncWriteExt}; | ||
| 9 | use embassy_nrf::usb::{State, Usb, UsbBus, UsbSerial}; | ||
| 10 | use embassy_nrf::{interrupt, Peripherals}; | ||
| 11 | use futures::pin_mut; | ||
| 12 | use usb_device::device::{UsbDeviceBuilder, UsbVidPid}; | ||
| 13 | |||
| 14 | use defmt_rtt as _; // global logger | ||
| 15 | use panic_probe as _; // print out panic messages | ||
| 16 | |||
| 17 | #[embassy::main] | ||
| 18 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 19 | let mut rx_buffer = [0u8; 64]; | ||
| 20 | // we send back input + cr + lf | ||
| 21 | let mut tx_buffer = [0u8; 66]; | ||
| 22 | |||
| 23 | let usb_bus = UsbBus::new(p.USBD); | ||
| 24 | |||
| 25 | let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer); | ||
| 26 | |||
| 27 | let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) | ||
| 28 | .manufacturer("Fake company") | ||
| 29 | .product("Serial port") | ||
| 30 | .serial_number("TEST") | ||
| 31 | .device_class(0x02) | ||
| 32 | .build(); | ||
| 33 | |||
| 34 | let irq = interrupt::take!(USBD); | ||
| 35 | irq.set_priority(interrupt::Priority::P3); | ||
| 36 | |||
| 37 | let mut state = State::new(); | ||
| 38 | let usb = unsafe { Usb::new(&mut state, device, serial, irq) }; | ||
| 39 | pin_mut!(usb); | ||
| 40 | |||
| 41 | let (mut reader, mut writer) = usb.as_ref().take_serial_0(); | ||
| 42 | |||
| 43 | info!("usb initialized!"); | ||
| 44 | |||
| 45 | unwrap!( | ||
| 46 | writer | ||
| 47 | .write_all(b"\r\nInput returned upper cased on CR+LF\r\n") | ||
| 48 | .await | ||
| 49 | ); | ||
| 50 | |||
| 51 | let mut buf = [0u8; 64]; | ||
| 52 | loop { | ||
| 53 | let n = unwrap!(read_line(&mut reader, &mut buf).await); | ||
| 54 | |||
| 55 | for char in buf[..n].iter_mut() { | ||
| 56 | // upper case | ||
| 57 | if 0x61 <= *char && *char <= 0x7a { | ||
| 58 | *char &= !0x20; | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | unwrap!(writer.write_all(&buf[..n]).await); | ||
| 63 | unwrap!(writer.write_all(b"\r\n").await); | ||
| 64 | unwrap!(writer.flush().await); | ||
| 65 | } | ||
| 66 | } | ||
