diff options
| -rw-r--r-- | embassy-stm32f4-examples/Cargo.toml | 5 | ||||
| -rw-r--r-- | embassy-stm32f4-examples/src/bin/usb_serial.rs | 119 | ||||
| -rw-r--r-- | embassy-stm32f4/Cargo.toml | 1 | ||||
| -rw-r--r-- | embassy-stm32f4/src/cdc_acm.rs | 338 | ||||
| -rw-r--r-- | embassy-stm32f4/src/lib.rs | 7 | ||||
| -rw-r--r-- | embassy-stm32f4/src/usb.rs | 130 | ||||
| -rw-r--r-- | embassy-stm32f4/src/usb_serial.rs | 290 | ||||
| -rw-r--r-- | embassy-stm32f4/src/util/mod.rs | 12 | ||||
| -rw-r--r-- | embassy-stm32f4/src/util/peripheral.rs | 78 | ||||
| -rw-r--r-- | embassy-stm32f4/src/util/ring_buffer.rs | 86 |
10 files changed, 1064 insertions, 2 deletions
diff --git a/embassy-stm32f4-examples/Cargo.toml b/embassy-stm32f4-examples/Cargo.toml index 5bbaecc58..e4f2aa7a1 100644 --- a/embassy-stm32f4-examples/Cargo.toml +++ b/embassy-stm32f4-examples/Cargo.toml | |||
| @@ -46,7 +46,8 @@ cortex-m = "0.7.1" | |||
| 46 | cortex-m-rt = "0.6.13" | 46 | cortex-m-rt = "0.6.13" |
| 47 | embedded-hal = { version = "0.2.4" } | 47 | embedded-hal = { version = "0.2.4" } |
| 48 | panic-probe = "0.1.0" | 48 | panic-probe = "0.1.0" |
| 49 | stm32f4xx-hal = { version = "0.8.3", features = ["rt"], git = "https://github.com/stm32-rs/stm32f4xx-hal.git"} | 49 | stm32f4xx-hal = { version = "0.8.3", features = ["rt", "usb_fs"], git = "https://github.com/stm32-rs/stm32f4xx-hal.git"} |
| 50 | futures = { version = "0.3.8", default-features = false, features = ["async-await"] } | 50 | futures = { version = "0.3.8", default-features = false, features = ["async-await"] } |
| 51 | rtt-target = { version = "0.3", features = ["cortex-m"] } | 51 | rtt-target = { version = "0.3", features = ["cortex-m"] } |
| 52 | bxcan = "0.5.0" \ No newline at end of file | 52 | bxcan = "0.5.0" |
| 53 | usb-device = "0.2.7" | ||
diff --git a/embassy-stm32f4-examples/src/bin/usb_serial.rs b/embassy-stm32f4-examples/src/bin/usb_serial.rs new file mode 100644 index 000000000..d2ccb4b21 --- /dev/null +++ b/embassy-stm32f4-examples/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,119 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | #[path = "../example_common.rs"] | ||
| 6 | mod example_common; | ||
| 7 | use example_common::*; | ||
| 8 | |||
| 9 | use cortex_m_rt::entry; | ||
| 10 | use defmt::panic; | ||
| 11 | use embassy::executor::{task, Executor}; | ||
| 12 | use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; | ||
| 13 | use embassy::time::{Duration, Timer}; | ||
| 14 | use embassy::util::Forever; | ||
| 15 | use embassy_stm32f4::interrupt::OwnedInterrupt; | ||
| 16 | use embassy_stm32f4::usb::Usb; | ||
| 17 | use embassy_stm32f4::usb_serial::UsbSerial; | ||
| 18 | use embassy_stm32f4::{interrupt, pac, rtc}; | ||
| 19 | use futures::future::{select, Either}; | ||
| 20 | use futures::pin_mut; | ||
| 21 | use stm32f4xx_hal::otg_fs::{UsbBus, USB}; | ||
| 22 | use stm32f4xx_hal::prelude::*; | ||
| 23 | use usb_device::bus::UsbBusAllocator; | ||
| 24 | use usb_device::prelude::*; | ||
| 25 | |||
| 26 | #[task] | ||
| 27 | async fn run1(bus: &'static mut UsbBusAllocator<UsbBus<USB>>) { | ||
| 28 | info!("Async task"); | ||
| 29 | |||
| 30 | let mut read_buf = [0u8; 128]; | ||
| 31 | let mut write_buf = [0u8; 128]; | ||
| 32 | let serial = UsbSerial::new(bus, &mut read_buf, &mut write_buf); | ||
| 33 | |||
| 34 | let device = UsbDeviceBuilder::new(bus, UsbVidPid(0x16c0, 0x27dd)) | ||
| 35 | .manufacturer("Fake company") | ||
| 36 | .product("Serial port") | ||
| 37 | .serial_number("TEST") | ||
| 38 | .device_class(0x02) | ||
| 39 | .build(); | ||
| 40 | |||
| 41 | let irq = interrupt::take!(OTG_FS); | ||
| 42 | irq.set_priority(interrupt::Priority::Level3); | ||
| 43 | |||
| 44 | let usb = Usb::new(device, serial, irq); | ||
| 45 | pin_mut!(usb); | ||
| 46 | |||
| 47 | let (mut read_interface, mut write_interface) = usb.as_mut().into_ref().take_serial(); | ||
| 48 | |||
| 49 | let mut buf = [0u8; 5]; | ||
| 50 | loop { | ||
| 51 | let recv_fut = read_interface.read(&mut buf); | ||
| 52 | let timeout = Timer::after(Duration::from_ticks(32768 * 3)); | ||
| 53 | |||
| 54 | match select(recv_fut, timeout).await { | ||
| 55 | Either::Left((recv, _)) => { | ||
| 56 | let recv = unwrap!(recv); | ||
| 57 | unwrap!(write_interface.write_all(&buf[..recv]).await); | ||
| 58 | } | ||
| 59 | Either::Right(_) => { | ||
| 60 | unwrap!(write_interface.write_all(b"Hello\r\n").await); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | static RTC: Forever<rtc::RTC<pac::TIM2>> = Forever::new(); | ||
| 67 | static ALARM: Forever<rtc::Alarm<pac::TIM2>> = Forever::new(); | ||
| 68 | static EXECUTOR: Forever<Executor> = Forever::new(); | ||
| 69 | static USB_BUS: Forever<UsbBusAllocator<UsbBus<USB>>> = Forever::new(); | ||
| 70 | |||
| 71 | #[entry] | ||
| 72 | fn main() -> ! { | ||
| 73 | static mut EP_MEMORY: [u32; 1024] = [0; 1024]; | ||
| 74 | |||
| 75 | info!("Hello World!"); | ||
| 76 | |||
| 77 | let p = unwrap!(pac::Peripherals::take()); | ||
| 78 | |||
| 79 | p.RCC.ahb1enr.modify(|_, w| w.dma1en().enabled()); | ||
| 80 | let rcc = p.RCC.constrain(); | ||
| 81 | let clocks = rcc | ||
| 82 | .cfgr | ||
| 83 | .use_hse(25.mhz()) | ||
| 84 | .sysclk(48.mhz()) | ||
| 85 | .require_pll48clk() | ||
| 86 | .freeze(); | ||
| 87 | |||
| 88 | p.DBGMCU.cr.modify(|_, w| { | ||
| 89 | w.dbg_sleep().set_bit(); | ||
| 90 | w.dbg_standby().set_bit(); | ||
| 91 | w.dbg_stop().set_bit() | ||
| 92 | }); | ||
| 93 | |||
| 94 | let rtc = RTC.put(rtc::RTC::new(p.TIM2, interrupt::take!(TIM2), clocks)); | ||
| 95 | rtc.start(); | ||
| 96 | |||
| 97 | unsafe { embassy::time::set_clock(rtc) }; | ||
| 98 | |||
| 99 | let alarm = ALARM.put(rtc.alarm1()); | ||
| 100 | let executor = EXECUTOR.put(Executor::new()); | ||
| 101 | executor.set_alarm(alarm); | ||
| 102 | |||
| 103 | let gpioa = p.GPIOA.split(); | ||
| 104 | let usb = USB { | ||
| 105 | usb_global: p.OTG_FS_GLOBAL, | ||
| 106 | usb_device: p.OTG_FS_DEVICE, | ||
| 107 | usb_pwrclk: p.OTG_FS_PWRCLK, | ||
| 108 | pin_dm: gpioa.pa11.into_alternate_af10(), | ||
| 109 | pin_dp: gpioa.pa12.into_alternate_af10(), | ||
| 110 | hclk: clocks.hclk(), | ||
| 111 | }; | ||
| 112 | // Rust analyzer isn't recognizing the static ref magic `cortex-m` does | ||
| 113 | #[allow(unused_unsafe)] | ||
| 114 | let usb_bus = USB_BUS.put(UsbBus::new(usb, unsafe { EP_MEMORY })); | ||
| 115 | |||
| 116 | executor.run(move |spawner| { | ||
| 117 | unwrap!(spawner.spawn(run1(usb_bus))); | ||
| 118 | }); | ||
| 119 | } | ||
diff --git a/embassy-stm32f4/Cargo.toml b/embassy-stm32f4/Cargo.toml index ae3273d67..b39a141b4 100644 --- a/embassy-stm32f4/Cargo.toml +++ b/embassy-stm32f4/Cargo.toml | |||
| @@ -41,3 +41,4 @@ embedded-dma = { version = "0.1.2" } | |||
| 41 | stm32f4xx-hal = { version = "0.8.3", features = ["rt", "can"], git = "https://github.com/stm32-rs/stm32f4xx-hal.git"} | 41 | stm32f4xx-hal = { version = "0.8.3", features = ["rt", "can"], git = "https://github.com/stm32-rs/stm32f4xx-hal.git"} |
| 42 | bxcan = "0.5.0" | 42 | bxcan = "0.5.0" |
| 43 | nb = "*" | 43 | nb = "*" |
| 44 | usb-device = "0.2.7" | ||
diff --git a/embassy-stm32f4/src/cdc_acm.rs b/embassy-stm32f4/src/cdc_acm.rs new file mode 100644 index 000000000..5a85b3846 --- /dev/null +++ b/embassy-stm32f4/src/cdc_acm.rs | |||
| @@ -0,0 +1,338 @@ | |||
| 1 | // Copied from https://github.com/mvirkkunen/usbd-serial | ||
| 2 | #![allow(dead_code)] | ||
| 3 | |||
| 4 | use core::convert::TryInto; | ||
| 5 | use core::mem; | ||
| 6 | use usb_device::class_prelude::*; | ||
| 7 | use usb_device::Result; | ||
| 8 | |||
| 9 | /// This should be used as `device_class` when building the `UsbDevice`. | ||
| 10 | pub const USB_CLASS_CDC: u8 = 0x02; | ||
| 11 | |||
| 12 | const USB_CLASS_CDC_DATA: u8 = 0x0a; | ||
| 13 | const CDC_SUBCLASS_ACM: u8 = 0x02; | ||
| 14 | const CDC_PROTOCOL_NONE: u8 = 0x00; | ||
| 15 | |||
| 16 | const CS_INTERFACE: u8 = 0x24; | ||
| 17 | const CDC_TYPE_HEADER: u8 = 0x00; | ||
| 18 | const CDC_TYPE_CALL_MANAGEMENT: u8 = 0x01; | ||
| 19 | const CDC_TYPE_ACM: u8 = 0x02; | ||
| 20 | const CDC_TYPE_UNION: u8 = 0x06; | ||
| 21 | |||
| 22 | const REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00; | ||
| 23 | #[allow(unused)] | ||
| 24 | const REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01; | ||
| 25 | const REQ_SET_LINE_CODING: u8 = 0x20; | ||
| 26 | const REQ_GET_LINE_CODING: u8 = 0x21; | ||
| 27 | const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; | ||
| 28 | |||
| 29 | /// Packet level implementation of a CDC-ACM serial port. | ||
| 30 | /// | ||
| 31 | /// This class can be used directly and it has the least overhead due to directly reading and | ||
| 32 | /// writing USB packets with no intermediate buffers, but it will not act like a stream-like serial | ||
| 33 | /// port. The following constraints must be followed if you use this class directly: | ||
| 34 | /// | ||
| 35 | /// - `read_packet` must be called with a buffer large enough to hold max_packet_size bytes, and the | ||
| 36 | /// method will return a `WouldBlock` error if there is no packet to be read. | ||
| 37 | /// - `write_packet` must not be called with a buffer larger than max_packet_size bytes, and the | ||
| 38 | /// method will return a `WouldBlock` error if the previous packet has not been sent yet. | ||
| 39 | /// - If you write a packet that is exactly max_packet_size bytes long, it won't be processed by the | ||
| 40 | /// host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP) | ||
| 41 | /// can be sent if there is no other data to send. This is because USB bulk transactions must be | ||
| 42 | /// terminated with a short packet, even if the bulk endpoint is used for stream-like data. | ||
| 43 | pub struct CdcAcmClass<'a, B: UsbBus> { | ||
| 44 | comm_if: InterfaceNumber, | ||
| 45 | comm_ep: EndpointIn<'a, B>, | ||
| 46 | data_if: InterfaceNumber, | ||
| 47 | read_ep: EndpointOut<'a, B>, | ||
| 48 | write_ep: EndpointIn<'a, B>, | ||
| 49 | line_coding: LineCoding, | ||
| 50 | dtr: bool, | ||
| 51 | rts: bool, | ||
| 52 | } | ||
| 53 | |||
| 54 | impl<B: UsbBus> CdcAcmClass<'_, B> { | ||
| 55 | /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For | ||
| 56 | /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. | ||
| 57 | pub fn new(alloc: &UsbBusAllocator<B>, max_packet_size: u16) -> CdcAcmClass<'_, B> { | ||
| 58 | CdcAcmClass { | ||
| 59 | comm_if: alloc.interface(), | ||
| 60 | comm_ep: alloc.interrupt(8, 255), | ||
| 61 | data_if: alloc.interface(), | ||
| 62 | read_ep: alloc.bulk(max_packet_size), | ||
| 63 | write_ep: alloc.bulk(max_packet_size), | ||
| 64 | line_coding: LineCoding { | ||
| 65 | stop_bits: StopBits::One, | ||
| 66 | data_bits: 8, | ||
| 67 | parity_type: ParityType::None, | ||
| 68 | data_rate: 8_000, | ||
| 69 | }, | ||
| 70 | dtr: false, | ||
| 71 | rts: false, | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | /// Gets the maximum packet size in bytes. | ||
| 76 | pub fn max_packet_size(&self) -> u16 { | ||
| 77 | // The size is the same for both endpoints. | ||
| 78 | self.read_ep.max_packet_size() | ||
| 79 | } | ||
| 80 | |||
| 81 | /// Gets the current line coding. The line coding contains information that's mainly relevant | ||
| 82 | /// for USB to UART serial port emulators, and can be ignored if not relevant. | ||
| 83 | pub fn line_coding(&self) -> &LineCoding { | ||
| 84 | &self.line_coding | ||
| 85 | } | ||
| 86 | |||
| 87 | /// Gets the DTR (data terminal ready) state | ||
| 88 | pub fn dtr(&self) -> bool { | ||
| 89 | self.dtr | ||
| 90 | } | ||
| 91 | |||
| 92 | /// Gets the RTS (request to send) state | ||
| 93 | pub fn rts(&self) -> bool { | ||
| 94 | self.rts | ||
| 95 | } | ||
| 96 | |||
| 97 | /// Writes a single packet into the IN endpoint. | ||
| 98 | pub fn write_packet(&mut self, data: &[u8]) -> Result<usize> { | ||
| 99 | self.write_ep.write(data) | ||
| 100 | } | ||
| 101 | |||
| 102 | /// Reads a single packet from the OUT endpoint. | ||
| 103 | pub fn read_packet(&mut self, data: &mut [u8]) -> Result<usize> { | ||
| 104 | self.read_ep.read(data) | ||
| 105 | } | ||
| 106 | |||
| 107 | /// Gets the address of the IN endpoint. | ||
| 108 | pub fn write_ep_address(&self) -> EndpointAddress { | ||
| 109 | self.write_ep.address() | ||
| 110 | } | ||
| 111 | |||
| 112 | /// Gets the address of the OUT endpoint. | ||
| 113 | pub fn read_ep_address(&self) -> EndpointAddress { | ||
| 114 | self.read_ep.address() | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | impl<B: UsbBus> UsbClass<B> for CdcAcmClass<'_, B> { | ||
| 119 | fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> { | ||
| 120 | writer.iad( | ||
| 121 | self.comm_if, | ||
| 122 | 2, | ||
| 123 | USB_CLASS_CDC, | ||
| 124 | CDC_SUBCLASS_ACM, | ||
| 125 | CDC_PROTOCOL_NONE, | ||
| 126 | )?; | ||
| 127 | |||
| 128 | writer.interface( | ||
| 129 | self.comm_if, | ||
| 130 | USB_CLASS_CDC, | ||
| 131 | CDC_SUBCLASS_ACM, | ||
| 132 | CDC_PROTOCOL_NONE, | ||
| 133 | )?; | ||
| 134 | |||
| 135 | writer.write( | ||
| 136 | CS_INTERFACE, | ||
| 137 | &[ | ||
| 138 | CDC_TYPE_HEADER, // bDescriptorSubtype | ||
| 139 | 0x10, | ||
| 140 | 0x01, // bcdCDC (1.10) | ||
| 141 | ], | ||
| 142 | )?; | ||
| 143 | |||
| 144 | writer.write( | ||
| 145 | CS_INTERFACE, | ||
| 146 | &[ | ||
| 147 | CDC_TYPE_ACM, // bDescriptorSubtype | ||
| 148 | 0x00, // bmCapabilities | ||
| 149 | ], | ||
| 150 | )?; | ||
| 151 | |||
| 152 | writer.write( | ||
| 153 | CS_INTERFACE, | ||
| 154 | &[ | ||
| 155 | CDC_TYPE_UNION, // bDescriptorSubtype | ||
| 156 | self.comm_if.into(), // bControlInterface | ||
| 157 | self.data_if.into(), // bSubordinateInterface | ||
| 158 | ], | ||
| 159 | )?; | ||
| 160 | |||
| 161 | writer.write( | ||
| 162 | CS_INTERFACE, | ||
| 163 | &[ | ||
| 164 | CDC_TYPE_CALL_MANAGEMENT, // bDescriptorSubtype | ||
| 165 | 0x00, // bmCapabilities | ||
| 166 | self.data_if.into(), // bDataInterface | ||
| 167 | ], | ||
| 168 | )?; | ||
| 169 | |||
| 170 | writer.endpoint(&self.comm_ep)?; | ||
| 171 | |||
| 172 | writer.interface(self.data_if, USB_CLASS_CDC_DATA, 0x00, 0x00)?; | ||
| 173 | |||
| 174 | writer.endpoint(&self.write_ep)?; | ||
| 175 | writer.endpoint(&self.read_ep)?; | ||
| 176 | |||
| 177 | Ok(()) | ||
| 178 | } | ||
| 179 | |||
| 180 | fn reset(&mut self) { | ||
| 181 | self.line_coding = LineCoding::default(); | ||
| 182 | self.dtr = false; | ||
| 183 | self.rts = false; | ||
| 184 | } | ||
| 185 | |||
| 186 | fn control_in(&mut self, xfer: ControlIn<B>) { | ||
| 187 | let req = xfer.request(); | ||
| 188 | |||
| 189 | if !(req.request_type == control::RequestType::Class | ||
| 190 | && req.recipient == control::Recipient::Interface | ||
| 191 | && req.index == u8::from(self.comm_if) as u16) | ||
| 192 | { | ||
| 193 | return; | ||
| 194 | } | ||
| 195 | |||
| 196 | match req.request { | ||
| 197 | // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below. | ||
| 198 | REQ_GET_LINE_CODING if req.length == 7 => { | ||
| 199 | xfer.accept(|data| { | ||
| 200 | data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes()); | ||
| 201 | data[4] = self.line_coding.stop_bits as u8; | ||
| 202 | data[5] = self.line_coding.parity_type as u8; | ||
| 203 | data[6] = self.line_coding.data_bits; | ||
| 204 | |||
| 205 | Ok(7) | ||
| 206 | }) | ||
| 207 | .ok(); | ||
| 208 | } | ||
| 209 | _ => { | ||
| 210 | xfer.reject().ok(); | ||
| 211 | } | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | fn control_out(&mut self, xfer: ControlOut<B>) { | ||
| 216 | let req = xfer.request(); | ||
| 217 | |||
| 218 | if !(req.request_type == control::RequestType::Class | ||
| 219 | && req.recipient == control::Recipient::Interface | ||
| 220 | && req.index == u8::from(self.comm_if) as u16) | ||
| 221 | { | ||
| 222 | return; | ||
| 223 | } | ||
| 224 | |||
| 225 | match req.request { | ||
| 226 | REQ_SEND_ENCAPSULATED_COMMAND => { | ||
| 227 | // We don't actually support encapsulated commands but pretend we do for standards | ||
| 228 | // compatibility. | ||
| 229 | xfer.accept().ok(); | ||
| 230 | } | ||
| 231 | REQ_SET_LINE_CODING if xfer.data().len() >= 7 => { | ||
| 232 | self.line_coding.data_rate = | ||
| 233 | u32::from_le_bytes(xfer.data()[0..4].try_into().unwrap()); | ||
| 234 | self.line_coding.stop_bits = xfer.data()[4].into(); | ||
| 235 | self.line_coding.parity_type = xfer.data()[5].into(); | ||
| 236 | self.line_coding.data_bits = xfer.data()[6]; | ||
| 237 | |||
| 238 | xfer.accept().ok(); | ||
| 239 | } | ||
| 240 | REQ_SET_CONTROL_LINE_STATE => { | ||
| 241 | self.dtr = (req.value & 0x0001) != 0; | ||
| 242 | self.rts = (req.value & 0x0002) != 0; | ||
| 243 | |||
| 244 | xfer.accept().ok(); | ||
| 245 | } | ||
| 246 | _ => { | ||
| 247 | xfer.reject().ok(); | ||
| 248 | } | ||
| 249 | }; | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | /// Number of stop bits for LineCoding | ||
| 254 | #[derive(Copy, Clone, PartialEq, Eq)] | ||
| 255 | pub enum StopBits { | ||
| 256 | /// 1 stop bit | ||
| 257 | One = 0, | ||
| 258 | |||
| 259 | /// 1.5 stop bits | ||
| 260 | OnePointFive = 1, | ||
| 261 | |||
| 262 | /// 2 stop bits | ||
| 263 | Two = 2, | ||
| 264 | } | ||
| 265 | |||
| 266 | impl From<u8> for StopBits { | ||
| 267 | fn from(value: u8) -> Self { | ||
| 268 | if value <= 2 { | ||
| 269 | unsafe { mem::transmute(value) } | ||
| 270 | } else { | ||
| 271 | StopBits::One | ||
| 272 | } | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | /// Parity for LineCoding | ||
| 277 | #[derive(Copy, Clone, PartialEq, Eq)] | ||
| 278 | pub enum ParityType { | ||
| 279 | None = 0, | ||
| 280 | Odd = 1, | ||
| 281 | Event = 2, | ||
| 282 | Mark = 3, | ||
| 283 | Space = 4, | ||
| 284 | } | ||
| 285 | |||
| 286 | impl From<u8> for ParityType { | ||
| 287 | fn from(value: u8) -> Self { | ||
| 288 | if value <= 4 { | ||
| 289 | unsafe { mem::transmute(value) } | ||
| 290 | } else { | ||
| 291 | ParityType::None | ||
| 292 | } | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | /// Line coding parameters | ||
| 297 | /// | ||
| 298 | /// This is provided by the host for specifying the standard UART parameters such as baud rate. Can | ||
| 299 | /// be ignored if you don't plan to interface with a physical UART. | ||
| 300 | pub struct LineCoding { | ||
| 301 | stop_bits: StopBits, | ||
| 302 | data_bits: u8, | ||
| 303 | parity_type: ParityType, | ||
| 304 | data_rate: u32, | ||
| 305 | } | ||
| 306 | |||
| 307 | impl LineCoding { | ||
| 308 | /// Gets the number of stop bits for UART communication. | ||
| 309 | pub fn stop_bits(&self) -> StopBits { | ||
| 310 | self.stop_bits | ||
| 311 | } | ||
| 312 | |||
| 313 | /// Gets the number of data bits for UART communication. | ||
| 314 | pub fn data_bits(&self) -> u8 { | ||
| 315 | self.data_bits | ||
| 316 | } | ||
| 317 | |||
| 318 | /// Gets the parity type for UART communication. | ||
| 319 | pub fn parity_type(&self) -> ParityType { | ||
| 320 | self.parity_type | ||
| 321 | } | ||
| 322 | |||
| 323 | /// Gets the data rate in bits per second for UART communication. | ||
| 324 | pub fn data_rate(&self) -> u32 { | ||
| 325 | self.data_rate | ||
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 329 | impl Default for LineCoding { | ||
| 330 | fn default() -> Self { | ||
| 331 | LineCoding { | ||
| 332 | stop_bits: StopBits::One, | ||
| 333 | data_bits: 8, | ||
| 334 | parity_type: ParityType::None, | ||
| 335 | data_rate: 8_000, | ||
| 336 | } | ||
| 337 | } | ||
| 338 | } | ||
diff --git a/embassy-stm32f4/src/lib.rs b/embassy-stm32f4/src/lib.rs index 0d490525c..1788f5e77 100644 --- a/embassy-stm32f4/src/lib.rs +++ b/embassy-stm32f4/src/lib.rs | |||
| @@ -316,3 +316,10 @@ pub mod exti; | |||
| 316 | pub mod qei; | 316 | pub mod qei; |
| 317 | pub mod rtc; | 317 | pub mod rtc; |
| 318 | pub mod serial; | 318 | pub mod serial; |
| 319 | pub mod usb; | ||
| 320 | pub mod usb_serial; | ||
| 321 | pub mod util; | ||
| 322 | |||
| 323 | pub(crate) mod cdc_acm; | ||
| 324 | |||
| 325 | pub use cortex_m_rt::interrupt; | ||
diff --git a/embassy-stm32f4/src/usb.rs b/embassy-stm32f4/src/usb.rs new file mode 100644 index 000000000..613b9ecb7 --- /dev/null +++ b/embassy-stm32f4/src/usb.rs | |||
| @@ -0,0 +1,130 @@ | |||
| 1 | use core::cell::RefCell; | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | use core::pin::Pin; | ||
| 4 | |||
| 5 | use usb_device::bus::UsbBus; | ||
| 6 | use usb_device::class::UsbClass; | ||
| 7 | use usb_device::device::UsbDevice; | ||
| 8 | |||
| 9 | use crate::interrupt; | ||
| 10 | use crate::usb_serial::{ReadInterface, UsbSerial, WriteInterface}; | ||
| 11 | use crate::util::peripheral::{PeripheralMutex, PeripheralState}; | ||
| 12 | |||
| 13 | pub struct State<'bus, B: UsbBus, T: ClassSet<B>> { | ||
| 14 | device: UsbDevice<'bus, B>, | ||
| 15 | pub(crate) classes: T, | ||
| 16 | } | ||
| 17 | |||
| 18 | pub struct Usb<'bus, B: UsbBus, T: ClassSet<B>> { | ||
| 19 | // Don't you dare moving out `PeripheralMutex` | ||
| 20 | inner: RefCell<PeripheralMutex<State<'bus, B, T>>>, | ||
| 21 | } | ||
| 22 | |||
| 23 | impl<'bus, B, T> Usb<'bus, B, T> | ||
| 24 | where | ||
| 25 | B: UsbBus, | ||
| 26 | T: ClassSet<B>, | ||
| 27 | { | ||
| 28 | pub fn new<S: IntoClassSet<B, T>>( | ||
| 29 | device: UsbDevice<'bus, B>, | ||
| 30 | class_set: S, | ||
| 31 | irq: interrupt::OTG_FSInterrupt, | ||
| 32 | ) -> Self { | ||
| 33 | let state = State { | ||
| 34 | device, | ||
| 35 | classes: class_set.into_class_set(), | ||
| 36 | }; | ||
| 37 | let mutex = PeripheralMutex::new(state, irq); | ||
| 38 | Self { | ||
| 39 | inner: RefCell::new(mutex), | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | pub fn start(self: Pin<&mut Self>) { | ||
| 44 | let this = unsafe { self.get_unchecked_mut() }; | ||
| 45 | let mut mutex = this.inner.borrow_mut(); | ||
| 46 | let mutex = unsafe { Pin::new_unchecked(&mut *mutex) }; | ||
| 47 | |||
| 48 | // Use inner to register the irq | ||
| 49 | mutex.with(|_, _| {}); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | impl<'bus, 'c, B, T> Usb<'bus, B, T> | ||
| 54 | where | ||
| 55 | B: UsbBus, | ||
| 56 | T: ClassSet<B> + SerialState<'bus, 'c, B>, | ||
| 57 | { | ||
| 58 | pub fn take_serial<'a>( | ||
| 59 | self: Pin<&'a Self>, | ||
| 60 | ) -> ( | ||
| 61 | ReadInterface<'a, 'bus, 'c, B, T>, | ||
| 62 | WriteInterface<'a, 'bus, 'c, B, T>, | ||
| 63 | ) { | ||
| 64 | let this = self.get_ref(); | ||
| 65 | |||
| 66 | let r = ReadInterface { | ||
| 67 | inner: &this.inner, | ||
| 68 | _buf_lifetime: PhantomData, | ||
| 69 | }; | ||
| 70 | |||
| 71 | let w = WriteInterface { | ||
| 72 | inner: &this.inner, | ||
| 73 | _buf_lifetime: PhantomData, | ||
| 74 | }; | ||
| 75 | (r, w) | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | impl<'bus, B, T> PeripheralState for State<'bus, B, T> | ||
| 80 | where | ||
| 81 | B: UsbBus, | ||
| 82 | T: ClassSet<B>, | ||
| 83 | { | ||
| 84 | type Interrupt = interrupt::OTG_FSInterrupt; | ||
| 85 | fn on_interrupt(&mut self) { | ||
| 86 | self.classes.poll_all(&mut self.device); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | pub trait ClassSet<B: UsbBus> { | ||
| 91 | fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool; | ||
| 92 | } | ||
| 93 | |||
| 94 | pub trait IntoClassSet<B: UsbBus, C: ClassSet<B>> { | ||
| 95 | fn into_class_set(self) -> C; | ||
| 96 | } | ||
| 97 | |||
| 98 | pub struct ClassSet1<B: UsbBus, T: UsbClass<B>> { | ||
| 99 | class: T, | ||
| 100 | _bus: PhantomData<B>, | ||
| 101 | } | ||
| 102 | |||
| 103 | impl<B, T> ClassSet<B> for ClassSet1<B, T> | ||
| 104 | where | ||
| 105 | B: UsbBus, | ||
| 106 | T: UsbClass<B>, | ||
| 107 | { | ||
| 108 | fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool { | ||
| 109 | device.poll(&mut [&mut self.class]) | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | impl<B: UsbBus, T: UsbClass<B>> IntoClassSet<B, ClassSet1<B, T>> for T { | ||
| 114 | fn into_class_set(self) -> ClassSet1<B, T> { | ||
| 115 | ClassSet1 { | ||
| 116 | class: self, | ||
| 117 | _bus: PhantomData, | ||
| 118 | } | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | pub trait SerialState<'bus, 'a, B: UsbBus> { | ||
| 123 | fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B>; | ||
| 124 | } | ||
| 125 | |||
| 126 | impl<'bus, 'a, B: UsbBus> SerialState<'bus, 'a, B> for ClassSet1<B, UsbSerial<'bus, 'a, B>> { | ||
| 127 | fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> { | ||
| 128 | &mut self.class | ||
| 129 | } | ||
| 130 | } | ||
diff --git a/embassy-stm32f4/src/usb_serial.rs b/embassy-stm32f4/src/usb_serial.rs new file mode 100644 index 000000000..284d7e5f6 --- /dev/null +++ b/embassy-stm32f4/src/usb_serial.rs | |||
| @@ -0,0 +1,290 @@ | |||
| 1 | use core::cell::RefCell; | ||
| 2 | use core::marker::{PhantomData, PhantomPinned}; | ||
| 3 | use core::pin::Pin; | ||
| 4 | use core::task::{Context, Poll}; | ||
| 5 | |||
| 6 | use embassy::io::{self, AsyncBufRead, AsyncWrite}; | ||
| 7 | use embassy::util::WakerRegistration; | ||
| 8 | use usb_device::bus::UsbBus; | ||
| 9 | use usb_device::class_prelude::*; | ||
| 10 | use usb_device::UsbError; | ||
| 11 | |||
| 12 | use crate::cdc_acm::CdcAcmClass; | ||
| 13 | use crate::usb::{ClassSet, SerialState, State}; | ||
| 14 | use crate::util::peripheral::PeripheralMutex; | ||
| 15 | use crate::util::ring_buffer::RingBuffer; | ||
| 16 | |||
| 17 | pub struct ReadInterface<'a, 'bus, 'c, B: UsbBus, T: SerialState<'bus, 'c, B> + ClassSet<B>> { | ||
| 18 | // Don't you dare moving out `PeripheralMutex` | ||
| 19 | pub(crate) inner: &'a RefCell<PeripheralMutex<State<'bus, B, T>>>, | ||
| 20 | pub(crate) _buf_lifetime: PhantomData<&'c T>, | ||
| 21 | } | ||
| 22 | |||
| 23 | /// Write interface for USB CDC_ACM | ||
| 24 | /// | ||
| 25 | /// This interface is buffered, meaning that after the write returns the bytes might not be fully | ||
| 26 | /// on the wire just yet | ||
| 27 | pub struct WriteInterface<'a, 'bus, 'c, B: UsbBus, T: SerialState<'bus, 'c, B> + ClassSet<B>> { | ||
| 28 | // Don't you dare moving out `PeripheralMutex` | ||
| 29 | pub(crate) inner: &'a RefCell<PeripheralMutex<State<'bus, B, T>>>, | ||
| 30 | pub(crate) _buf_lifetime: PhantomData<&'c T>, | ||
| 31 | } | ||
| 32 | |||
| 33 | impl<'a, 'bus, 'c, B, T> AsyncBufRead for ReadInterface<'a, 'bus, 'c, B, T> | ||
| 34 | where | ||
| 35 | B: UsbBus, | ||
| 36 | T: SerialState<'bus, 'c, B> + ClassSet<B>, | ||
| 37 | { | ||
| 38 | fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> { | ||
| 39 | let this = self.get_mut(); | ||
| 40 | let mut mutex = this.inner.borrow_mut(); | ||
| 41 | let mutex = unsafe { Pin::new_unchecked(&mut *mutex) }; | ||
| 42 | mutex.with(|state, _irq| { | ||
| 43 | let serial = state.classes.get_serial(); | ||
| 44 | let serial = Pin::new(serial); | ||
| 45 | |||
| 46 | match serial.poll_fill_buf(cx) { | ||
| 47 | Poll::Ready(Ok(buf)) => { | ||
| 48 | let buf: &[u8] = buf; | ||
| 49 | let buf: &[u8] = unsafe { core::mem::transmute(buf) }; | ||
| 50 | Poll::Ready(Ok(buf)) | ||
| 51 | } | ||
| 52 | Poll::Ready(Err(_)) => Poll::Ready(Err(io::Error::Other)), | ||
| 53 | Poll::Pending => Poll::Pending, | ||
| 54 | } | ||
| 55 | }) | ||
| 56 | } | ||
| 57 | |||
| 58 | fn consume(self: Pin<&mut Self>, amt: usize) { | ||
| 59 | let this = self.get_mut(); | ||
| 60 | let mut mutex = this.inner.borrow_mut(); | ||
| 61 | let mutex = unsafe { Pin::new_unchecked(&mut *mutex) }; | ||
| 62 | mutex.with(|state, _irq| { | ||
| 63 | let serial = state.classes.get_serial(); | ||
| 64 | let serial = Pin::new(serial); | ||
| 65 | |||
| 66 | serial.consume(amt); | ||
| 67 | }) | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | impl<'a, 'bus, 'c, B, T> AsyncWrite for WriteInterface<'a, 'bus, 'c, B, T> | ||
| 72 | where | ||
| 73 | B: UsbBus, | ||
| 74 | T: SerialState<'bus, 'c, B> + ClassSet<B>, | ||
| 75 | { | ||
| 76 | fn poll_write( | ||
| 77 | self: Pin<&mut Self>, | ||
| 78 | cx: &mut Context<'_>, | ||
| 79 | buf: &[u8], | ||
| 80 | ) -> Poll<io::Result<usize>> { | ||
| 81 | let this = self.get_mut(); | ||
| 82 | let mut mutex = this.inner.borrow_mut(); | ||
| 83 | let mutex = unsafe { Pin::new_unchecked(&mut *mutex) }; | ||
| 84 | mutex.with(|state, _irq| { | ||
| 85 | let serial = state.classes.get_serial(); | ||
| 86 | let serial = Pin::new(serial); | ||
| 87 | |||
| 88 | serial.poll_write(cx, buf) | ||
| 89 | }) | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | pub struct UsbSerial<'bus, 'a, B: UsbBus> { | ||
| 94 | inner: CdcAcmClass<'bus, B>, | ||
| 95 | read_buf: RingBuffer<'a>, | ||
| 96 | write_buf: RingBuffer<'a>, | ||
| 97 | read_waker: WakerRegistration, | ||
| 98 | write_waker: WakerRegistration, | ||
| 99 | write_state: WriteState, | ||
| 100 | read_error: bool, | ||
| 101 | write_error: bool, | ||
| 102 | } | ||
| 103 | |||
| 104 | impl<'bus, 'a, B: UsbBus> AsyncBufRead for UsbSerial<'bus, 'a, B> { | ||
| 105 | fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> { | ||
| 106 | let this = self.get_mut(); | ||
| 107 | |||
| 108 | if this.read_error { | ||
| 109 | this.read_error = false; | ||
| 110 | return Poll::Ready(Err(io::Error::Other)); | ||
| 111 | } | ||
| 112 | |||
| 113 | let buf = this.read_buf.pop_buf(); | ||
| 114 | if buf.is_empty() { | ||
| 115 | this.read_waker.register(cx.waker()); | ||
| 116 | return Poll::Pending; | ||
| 117 | } | ||
| 118 | Poll::Ready(Ok(buf)) | ||
| 119 | } | ||
| 120 | |||
| 121 | fn consume(self: Pin<&mut Self>, amt: usize) { | ||
| 122 | self.get_mut().read_buf.pop(amt); | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | impl<'bus, 'a, B: UsbBus> AsyncWrite for UsbSerial<'bus, 'a, B> { | ||
| 127 | fn poll_write( | ||
| 128 | self: Pin<&mut Self>, | ||
| 129 | cx: &mut Context<'_>, | ||
| 130 | buf: &[u8], | ||
| 131 | ) -> Poll<io::Result<usize>> { | ||
| 132 | let this = self.get_mut(); | ||
| 133 | |||
| 134 | if this.write_error { | ||
| 135 | this.write_error = false; | ||
| 136 | return Poll::Ready(Err(io::Error::Other)); | ||
| 137 | } | ||
| 138 | |||
| 139 | let write_buf = this.write_buf.push_buf(); | ||
| 140 | if write_buf.is_empty() { | ||
| 141 | this.write_waker.register(cx.waker()); | ||
| 142 | return Poll::Pending; | ||
| 143 | } | ||
| 144 | |||
| 145 | let count = write_buf.len().min(buf.len()); | ||
| 146 | write_buf[..count].copy_from_slice(&buf[..count]); | ||
| 147 | this.write_buf.push(count); | ||
| 148 | |||
| 149 | this.flush_write(); | ||
| 150 | Poll::Ready(Ok(count)) | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | /// Keeps track of the type of the last written packet. | ||
| 155 | enum WriteState { | ||
| 156 | /// No packets in-flight | ||
| 157 | Idle, | ||
| 158 | |||
| 159 | /// Short packet currently in-flight | ||
| 160 | Short, | ||
| 161 | |||
| 162 | /// Full packet current in-flight. A full packet must be followed by a short packet for the host | ||
| 163 | /// OS to see the transaction. The data is the number of subsequent full packets sent so far. A | ||
| 164 | /// short packet is forced every SHORT_PACKET_INTERVAL packets so that the OS sees data in a | ||
| 165 | /// timely manner. | ||
| 166 | Full(usize), | ||
| 167 | } | ||
| 168 | |||
| 169 | impl<'bus, 'a, B: UsbBus> UsbSerial<'bus, 'a, B> { | ||
| 170 | pub fn new( | ||
| 171 | alloc: &'bus UsbBusAllocator<B>, | ||
| 172 | read_buf: &'a mut [u8], | ||
| 173 | write_buf: &'a mut [u8], | ||
| 174 | ) -> Self { | ||
| 175 | Self { | ||
| 176 | inner: CdcAcmClass::new(alloc, 64), | ||
| 177 | read_buf: RingBuffer::new(read_buf), | ||
| 178 | write_buf: RingBuffer::new(write_buf), | ||
| 179 | read_waker: WakerRegistration::new(), | ||
| 180 | write_waker: WakerRegistration::new(), | ||
| 181 | write_state: WriteState::Idle, | ||
| 182 | read_error: false, | ||
| 183 | write_error: false, | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | fn flush_write(&mut self) { | ||
| 188 | /// If this many full size packets have been sent in a row, a short packet will be sent so that the | ||
| 189 | /// host sees the data in a timely manner. | ||
| 190 | const SHORT_PACKET_INTERVAL: usize = 10; | ||
| 191 | |||
| 192 | let full_size_packets = match self.write_state { | ||
| 193 | WriteState::Full(c) => c, | ||
| 194 | _ => 0, | ||
| 195 | }; | ||
| 196 | |||
| 197 | let ep_size = self.inner.max_packet_size() as usize; | ||
| 198 | let max_size = if full_size_packets > SHORT_PACKET_INTERVAL { | ||
| 199 | ep_size - 1 | ||
| 200 | } else { | ||
| 201 | ep_size | ||
| 202 | }; | ||
| 203 | |||
| 204 | let buf = { | ||
| 205 | let buf = self.write_buf.pop_buf(); | ||
| 206 | if buf.len() > max_size { | ||
| 207 | &buf[..max_size] | ||
| 208 | } else { | ||
| 209 | buf | ||
| 210 | } | ||
| 211 | }; | ||
| 212 | |||
| 213 | if !buf.is_empty() { | ||
| 214 | let count = match self.inner.write_packet(buf) { | ||
| 215 | Ok(c) => c, | ||
| 216 | Err(UsbError::WouldBlock) => 0, | ||
| 217 | Err(_) => { | ||
| 218 | self.write_error = true; | ||
| 219 | return; | ||
| 220 | } | ||
| 221 | }; | ||
| 222 | |||
| 223 | if buf.len() == ep_size { | ||
| 224 | self.write_state = WriteState::Full(full_size_packets + 1); | ||
| 225 | } else { | ||
| 226 | self.write_state = WriteState::Short; | ||
| 227 | } | ||
| 228 | self.write_buf.pop(count); | ||
| 229 | } else if full_size_packets > 0 { | ||
| 230 | if let Err(e) = self.inner.write_packet(&[]) { | ||
| 231 | if !matches!(e, UsbError::WouldBlock) { | ||
| 232 | self.write_error = true; | ||
| 233 | } | ||
| 234 | return; | ||
| 235 | } | ||
| 236 | self.write_state = WriteState::Idle; | ||
| 237 | } | ||
| 238 | } | ||
| 239 | } | ||
| 240 | |||
| 241 | impl<B> UsbClass<B> for UsbSerial<'_, '_, B> | ||
| 242 | where | ||
| 243 | B: UsbBus, | ||
| 244 | { | ||
| 245 | fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<(), UsbError> { | ||
| 246 | self.inner.get_configuration_descriptors(writer) | ||
| 247 | } | ||
| 248 | |||
| 249 | fn reset(&mut self) { | ||
| 250 | self.inner.reset(); | ||
| 251 | self.read_buf.clear(); | ||
| 252 | self.write_buf.clear(); | ||
| 253 | self.write_state = WriteState::Idle; | ||
| 254 | } | ||
| 255 | |||
| 256 | fn endpoint_in_complete(&mut self, addr: EndpointAddress) { | ||
| 257 | if addr == self.inner.write_ep_address() { | ||
| 258 | self.write_waker.wake(); | ||
| 259 | |||
| 260 | self.flush_write(); | ||
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 264 | fn endpoint_out(&mut self, addr: EndpointAddress) { | ||
| 265 | if addr == self.inner.read_ep_address() { | ||
| 266 | let buf = self.read_buf.push_buf(); | ||
| 267 | let count = match self.inner.read_packet(buf) { | ||
| 268 | Ok(c) => c, | ||
| 269 | Err(UsbError::WouldBlock) => 0, | ||
| 270 | Err(_) => { | ||
| 271 | self.read_error = true; | ||
| 272 | return; | ||
| 273 | } | ||
| 274 | }; | ||
| 275 | |||
| 276 | if count > 0 { | ||
| 277 | self.read_buf.push(count); | ||
| 278 | self.read_waker.wake(); | ||
| 279 | } | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 283 | fn control_in(&mut self, xfer: ControlIn<B>) { | ||
| 284 | self.inner.control_in(xfer); | ||
| 285 | } | ||
| 286 | |||
| 287 | fn control_out(&mut self, xfer: ControlOut<B>) { | ||
| 288 | self.inner.control_out(xfer); | ||
| 289 | } | ||
| 290 | } | ||
diff --git a/embassy-stm32f4/src/util/mod.rs b/embassy-stm32f4/src/util/mod.rs new file mode 100644 index 000000000..cf3306545 --- /dev/null +++ b/embassy-stm32f4/src/util/mod.rs | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | pub mod peripheral; | ||
| 2 | pub mod ring_buffer; | ||
| 3 | |||
| 4 | /// Low power blocking wait loop using WFE/SEV. | ||
| 5 | pub fn low_power_wait_until(mut condition: impl FnMut() -> bool) { | ||
| 6 | while !condition() { | ||
| 7 | // WFE might "eat" an event that would have otherwise woken the executor. | ||
| 8 | cortex_m::asm::wfe(); | ||
| 9 | } | ||
| 10 | // Retrigger an event to be transparent to the executor. | ||
| 11 | cortex_m::asm::sev(); | ||
| 12 | } | ||
diff --git a/embassy-stm32f4/src/util/peripheral.rs b/embassy-stm32f4/src/util/peripheral.rs new file mode 100644 index 000000000..f2c7912ff --- /dev/null +++ b/embassy-stm32f4/src/util/peripheral.rs | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | use core::cell::UnsafeCell; | ||
| 2 | use core::marker::{PhantomData, PhantomPinned}; | ||
| 3 | use core::pin::Pin; | ||
| 4 | use core::sync::atomic::{compiler_fence, Ordering}; | ||
| 5 | |||
| 6 | use crate::interrupt::OwnedInterrupt; | ||
| 7 | |||
| 8 | pub trait PeripheralState { | ||
| 9 | type Interrupt: OwnedInterrupt; | ||
| 10 | fn on_interrupt(&mut self); | ||
| 11 | } | ||
| 12 | |||
| 13 | pub struct PeripheralMutex<S: PeripheralState> { | ||
| 14 | inner: Option<(UnsafeCell<S>, S::Interrupt)>, | ||
| 15 | _not_send: PhantomData<*mut ()>, | ||
| 16 | _pinned: PhantomPinned, | ||
| 17 | } | ||
| 18 | |||
| 19 | impl<S: PeripheralState> PeripheralMutex<S> { | ||
| 20 | pub fn new(state: S, irq: S::Interrupt) -> Self { | ||
| 21 | Self { | ||
| 22 | inner: Some((UnsafeCell::new(state), irq)), | ||
| 23 | _not_send: PhantomData, | ||
| 24 | _pinned: PhantomPinned, | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | pub fn with<R>(self: Pin<&mut Self>, f: impl FnOnce(&mut S, &mut S::Interrupt) -> R) -> R { | ||
| 29 | let this = unsafe { self.get_unchecked_mut() }; | ||
| 30 | let (state, irq) = unwrap!(this.inner.as_mut()); | ||
| 31 | |||
| 32 | irq.disable(); | ||
| 33 | compiler_fence(Ordering::SeqCst); | ||
| 34 | |||
| 35 | irq.set_handler( | ||
| 36 | |p| { | ||
| 37 | // Safety: it's OK to get a &mut to the state, since | ||
| 38 | // - We're in the IRQ, no one else can't preempt us | ||
| 39 | // - We can't have preempted a with() call because the irq is disabled during it. | ||
| 40 | let state = unsafe { &mut *(p as *mut S) }; | ||
| 41 | state.on_interrupt(); | ||
| 42 | }, | ||
| 43 | state.get() as *mut (), | ||
| 44 | ); | ||
| 45 | |||
| 46 | // Safety: it's OK to get a &mut to the state, since the irq is disabled. | ||
| 47 | let state = unsafe { &mut *state.get() }; | ||
| 48 | |||
| 49 | let r = f(state, irq); | ||
| 50 | |||
| 51 | compiler_fence(Ordering::SeqCst); | ||
| 52 | irq.enable(); | ||
| 53 | |||
| 54 | r | ||
| 55 | } | ||
| 56 | |||
| 57 | pub fn try_free(self: Pin<&mut Self>) -> Option<(S, S::Interrupt)> { | ||
| 58 | let this = unsafe { self.get_unchecked_mut() }; | ||
| 59 | this.inner.take().map(|(state, irq)| { | ||
| 60 | irq.disable(); | ||
| 61 | irq.remove_handler(); | ||
| 62 | (state.into_inner(), irq) | ||
| 63 | }) | ||
| 64 | } | ||
| 65 | |||
| 66 | pub fn free(self: Pin<&mut Self>) -> (S, S::Interrupt) { | ||
| 67 | unwrap!(self.try_free()) | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | impl<S: PeripheralState> Drop for PeripheralMutex<S> { | ||
| 72 | fn drop(&mut self) { | ||
| 73 | if let Some((_state, irq)) = &mut self.inner { | ||
| 74 | irq.disable(); | ||
| 75 | irq.remove_handler(); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | } | ||
diff --git a/embassy-stm32f4/src/util/ring_buffer.rs b/embassy-stm32f4/src/util/ring_buffer.rs new file mode 100644 index 000000000..0ef66f00a --- /dev/null +++ b/embassy-stm32f4/src/util/ring_buffer.rs | |||
| @@ -0,0 +1,86 @@ | |||
| 1 | use crate::fmt::{assert, *}; | ||
| 2 | |||
| 3 | pub struct RingBuffer<'a> { | ||
| 4 | buf: &'a mut [u8], | ||
| 5 | start: usize, | ||
| 6 | end: usize, | ||
| 7 | empty: bool, | ||
| 8 | } | ||
| 9 | |||
| 10 | impl<'a> RingBuffer<'a> { | ||
| 11 | pub fn new(buf: &'a mut [u8]) -> Self { | ||
| 12 | Self { | ||
| 13 | buf, | ||
| 14 | start: 0, | ||
| 15 | end: 0, | ||
| 16 | empty: true, | ||
| 17 | } | ||
| 18 | } | ||
| 19 | |||
| 20 | pub fn push_buf(&mut self) -> &mut [u8] { | ||
| 21 | if self.start == self.end && !self.empty { | ||
| 22 | trace!(" ringbuf: push_buf empty"); | ||
| 23 | return &mut self.buf[..0]; | ||
| 24 | } | ||
| 25 | |||
| 26 | let n = if self.start <= self.end { | ||
| 27 | self.buf.len() - self.end | ||
| 28 | } else { | ||
| 29 | self.start - self.end | ||
| 30 | }; | ||
| 31 | |||
| 32 | trace!(" ringbuf: push_buf {:?}..{:?}", self.end, self.end + n); | ||
| 33 | &mut self.buf[self.end..self.end + n] | ||
| 34 | } | ||
| 35 | |||
| 36 | pub fn push(&mut self, n: usize) { | ||
| 37 | trace!(" ringbuf: push {:?}", n); | ||
| 38 | if n == 0 { | ||
| 39 | return; | ||
| 40 | } | ||
| 41 | |||
| 42 | self.end = self.wrap(self.end + n); | ||
| 43 | self.empty = false; | ||
| 44 | } | ||
| 45 | |||
| 46 | pub fn pop_buf(&mut self) -> &mut [u8] { | ||
| 47 | if self.empty { | ||
| 48 | trace!(" ringbuf: pop_buf empty"); | ||
| 49 | return &mut self.buf[..0]; | ||
| 50 | } | ||
| 51 | |||
| 52 | let n = if self.end <= self.start { | ||
| 53 | self.buf.len() - self.start | ||
| 54 | } else { | ||
| 55 | self.end - self.start | ||
| 56 | }; | ||
| 57 | |||
| 58 | trace!(" ringbuf: pop_buf {:?}..{:?}", self.start, self.start + n); | ||
| 59 | &mut self.buf[self.start..self.start + n] | ||
| 60 | } | ||
| 61 | |||
| 62 | pub fn pop(&mut self, n: usize) { | ||
| 63 | trace!(" ringbuf: pop {:?}", n); | ||
| 64 | if n == 0 { | ||
| 65 | return; | ||
| 66 | } | ||
| 67 | |||
| 68 | self.start = self.wrap(self.start + n); | ||
| 69 | self.empty = self.start == self.end; | ||
| 70 | } | ||
| 71 | |||
| 72 | pub fn clear(&mut self) { | ||
| 73 | self.start = 0; | ||
| 74 | self.end = 0; | ||
| 75 | self.empty = true; | ||
| 76 | } | ||
| 77 | |||
| 78 | fn wrap(&self, n: usize) -> usize { | ||
| 79 | assert!(n <= self.buf.len()); | ||
| 80 | if n == self.buf.len() { | ||
| 81 | 0 | ||
| 82 | } else { | ||
| 83 | n | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
