aboutsummaryrefslogtreecommitdiff
path: root/examples/stm32l5/src
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2022-05-30 00:36:30 +0200
committerDario Nieuwenhuis <[email protected]>2022-06-07 03:29:00 +0200
commit3e4bead32161604c08e2dcae1acea695db851f34 (patch)
tree41b0334cad6fef5a54e28789ec0320f21000b2ac /examples/stm32l5/src
parent0aa73f58e2f71f4578ff23f79f3b1a2c9d6d9098 (diff)
stm32: add USB driver.
Diffstat (limited to 'examples/stm32l5/src')
-rw-r--r--examples/stm32l5/src/bin/button_exti.rs28
-rw-r--r--examples/stm32l5/src/bin/rng.rs34
-rw-r--r--examples/stm32l5/src/bin/usb_ethernet.rs290
-rw-r--r--examples/stm32l5/src/bin/usb_hid_mouse.rs136
-rw-r--r--examples/stm32l5/src/bin/usb_serial.rs112
5 files changed, 600 insertions, 0 deletions
diff --git a/examples/stm32l5/src/bin/button_exti.rs b/examples/stm32l5/src/bin/button_exti.rs
new file mode 100644
index 000000000..304ce0a8a
--- /dev/null
+++ b/examples/stm32l5/src/bin/button_exti.rs
@@ -0,0 +1,28 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use defmt_rtt as _; // global logger
7use embassy::executor::Spawner;
8use embassy_stm32::exti::ExtiInput;
9use embassy_stm32::gpio::{Input, Pull};
10use embassy_stm32::Peripherals;
11use panic_probe as _;
12
13#[embassy::main]
14async fn main(_spawner: Spawner, p: Peripherals) {
15 info!("Hello World!");
16
17 let button = Input::new(p.PC13, Pull::Down);
18 let mut button = ExtiInput::new(button, p.EXTI13);
19
20 info!("Press the USER button...");
21
22 loop {
23 button.wait_for_falling_edge().await;
24 info!("Pressed!");
25 button.wait_for_rising_edge().await;
26 info!("Released!");
27 }
28}
diff --git a/examples/stm32l5/src/bin/rng.rs b/examples/stm32l5/src/bin/rng.rs
new file mode 100644
index 000000000..5f75c1ff1
--- /dev/null
+++ b/examples/stm32l5/src/bin/rng.rs
@@ -0,0 +1,34 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use defmt_rtt as _; // global logger
7use embassy::executor::Spawner;
8use embassy_stm32::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv};
9use embassy_stm32::rng::Rng;
10use embassy_stm32::{Config, Peripherals};
11use panic_probe as _;
12
13fn config() -> Config {
14 let mut config = Config::default();
15 config.rcc.mux = ClockSrc::PLL(
16 PLLSource::HSI16,
17 PLLClkDiv::Div2,
18 PLLSrcDiv::Div1,
19 PLLMul::Mul8,
20 Some(PLLClkDiv::Div2),
21 );
22 config
23}
24
25#[embassy::main(config = "config()")]
26async fn main(_spawner: Spawner, p: Peripherals) {
27 info!("Hello World!");
28
29 let mut rng = Rng::new(p.RNG);
30
31 let mut buf = [0u8; 16];
32 unwrap!(rng.async_fill_bytes(&mut buf).await);
33 info!("random bytes: {:02x}", buf);
34}
diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs
new file mode 100644
index 000000000..fa445eece
--- /dev/null
+++ b/examples/stm32l5/src/bin/usb_ethernet.rs
@@ -0,0 +1,290 @@
1#![no_std]
2#![no_main]
3#![feature(generic_associated_types)]
4#![feature(type_alias_impl_trait)]
5
6use core::sync::atomic::{AtomicBool, Ordering};
7use core::task::Waker;
8use defmt::*;
9use defmt_rtt as _; // global logger
10use embassy::blocking_mutex::raw::ThreadModeRawMutex;
11use embassy::channel::Channel;
12use embassy::executor::Spawner;
13use embassy::util::Forever;
14use embassy_net::tcp::TcpSocket;
15use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources};
16use embassy_stm32::interrupt;
17use embassy_stm32::rcc::*;
18use embassy_stm32::rng::Rng;
19use embassy_stm32::time::Hertz;
20use embassy_stm32::usb::Driver;
21use embassy_stm32::{Config, Peripherals};
22use embassy_usb::{Builder, UsbDevice};
23use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State};
24use panic_probe as _;
25
26use defmt_rtt as _;
27use embedded_io::asynch::{Read, Write};
28// global logger
29use panic_probe as _;
30use rand_core::RngCore;
31
32type MyDriver = Driver<'static, embassy_stm32::peripherals::USB>;
33
34macro_rules! forever {
35 ($val:expr) => {{
36 type T = impl Sized;
37 static FOREVER: Forever<T> = Forever::new();
38 FOREVER.put_with(move || $val)
39 }};
40}
41
42#[embassy::task]
43async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! {
44 device.run().await
45}
46
47#[embassy::task]
48async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) {
49 loop {
50 warn!("WAITING for connection");
51 LINK_UP.store(false, Ordering::Relaxed);
52
53 class.wait_connection().await.unwrap();
54
55 warn!("Connected");
56 LINK_UP.store(true, Ordering::Relaxed);
57
58 loop {
59 let mut p = unwrap!(PacketBox::new(embassy_net::Packet::new()));
60 let n = match class.read_packet(&mut p[..]).await {
61 Ok(n) => n,
62 Err(e) => {
63 warn!("error reading packet: {:?}", e);
64 break;
65 }
66 };
67
68 let buf = p.slice(0..n);
69 if RX_CHANNEL.try_send(buf).is_err() {
70 warn!("Failed pushing rx'd packet to channel.");
71 }
72 }
73 }
74}
75
76#[embassy::task]
77async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) {
78 loop {
79 let pkt = TX_CHANNEL.recv().await;
80 if let Err(e) = class.write_packet(&pkt[..]).await {
81 warn!("Failed to TX packet: {:?}", e);
82 }
83 }
84}
85
86#[embassy::task]
87async fn net_task(stack: &'static Stack<Device>) -> ! {
88 stack.run().await
89}
90
91fn config() -> Config {
92 let mut config = Config::default();
93 config.rcc.mux = ClockSrc::HSE(Hertz(16_000_000));
94
95 config.rcc.mux = ClockSrc::PLL(
96 PLLSource::HSI16,
97 PLLClkDiv::Div2,
98 PLLSrcDiv::Div1,
99 PLLMul::Mul10,
100 None,
101 );
102 config.rcc.hsi48 = true;
103
104 config
105}
106
107#[embassy::main(config = "config()")]
108async fn main(spawner: Spawner, p: Peripherals) {
109 // Create the driver, from the HAL.
110 let irq = interrupt::take!(USB_FS);
111 let driver = Driver::new(p.USB, irq, p.PA12, p.PA11);
112
113 // Create embassy-usb Config
114 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
115 config.manufacturer = Some("Embassy");
116 config.product = Some("USB-Ethernet example");
117 config.serial_number = Some("12345678");
118 config.max_power = 100;
119 config.max_packet_size_0 = 64;
120
121 // Required for Windows support.
122 config.composite_with_iads = true;
123 config.device_class = 0xEF;
124 config.device_sub_class = 0x02;
125 config.device_protocol = 0x01;
126
127 struct Resources {
128 device_descriptor: [u8; 256],
129 config_descriptor: [u8; 256],
130 bos_descriptor: [u8; 256],
131 control_buf: [u8; 128],
132 serial_state: State<'static>,
133 }
134 let res: &mut Resources = forever!(Resources {
135 device_descriptor: [0; 256],
136 config_descriptor: [0; 256],
137 bos_descriptor: [0; 256],
138 control_buf: [0; 128],
139 serial_state: State::new(),
140 });
141
142 // Create embassy-usb DeviceBuilder using the driver and config.
143 let mut builder = Builder::new(
144 driver,
145 config,
146 &mut res.device_descriptor,
147 &mut res.config_descriptor,
148 &mut res.bos_descriptor,
149 &mut res.control_buf,
150 None,
151 );
152
153 // WARNINGS for Android ethernet tethering:
154 // - On Pixel 4a, it refused to work on Android 11, worked on Android 12.
155 // - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte),
156 // it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled.
157 // This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417
158 // and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757
159
160 // Our MAC addr.
161 let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC];
162 // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has.
163 let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88];
164
165 // Create classes on the builder.
166 let class = CdcNcmClass::new(&mut builder, &mut res.serial_state, host_mac_addr, 64);
167
168 // Build the builder.
169 let usb = builder.build();
170
171 unwrap!(spawner.spawn(usb_task(usb)));
172
173 let (tx, rx) = class.split();
174 unwrap!(spawner.spawn(usb_ncm_rx_task(rx)));
175 unwrap!(spawner.spawn(usb_ncm_tx_task(tx)));
176
177 let config = embassy_net::ConfigStrategy::Dhcp;
178 //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config {
179 // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),
180 // dns_servers: Vec::new(),
181 // gateway: Some(Ipv4Address::new(10, 42, 0, 1)),
182 //});
183
184 // Generate random seed
185 let mut rng = Rng::new(p.RNG);
186 let seed = rng.next_u64();
187
188 // Init network stack
189 let device = Device {
190 mac_addr: our_mac_addr,
191 };
192 let stack = &*forever!(Stack::new(
193 device,
194 config,
195 forever!(StackResources::<1, 2, 8>::new()),
196 seed
197 ));
198
199 unwrap!(spawner.spawn(net_task(stack)));
200
201 // And now we can use it!
202
203 let mut rx_buffer = [0; 4096];
204 let mut tx_buffer = [0; 4096];
205 let mut buf = [0; 4096];
206
207 loop {
208 let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
209 socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10)));
210
211 info!("Listening on TCP:1234...");
212 if let Err(e) = socket.accept(1234).await {
213 warn!("accept error: {:?}", e);
214 continue;
215 }
216
217 info!("Received connection from {:?}", socket.remote_endpoint());
218
219 loop {
220 let n = match socket.read(&mut buf).await {
221 Ok(0) => {
222 warn!("read EOF");
223 break;
224 }
225 Ok(n) => n,
226 Err(e) => {
227 warn!("read error: {:?}", e);
228 break;
229 }
230 };
231
232 info!("rxd {:02x}", &buf[..n]);
233
234 match socket.write_all(&buf[..n]).await {
235 Ok(()) => {}
236 Err(e) => {
237 warn!("write error: {:?}", e);
238 break;
239 }
240 };
241 }
242 }
243}
244
245static TX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new();
246static RX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new();
247static LINK_UP: AtomicBool = AtomicBool::new(false);
248
249struct Device {
250 mac_addr: [u8; 6],
251}
252
253impl embassy_net::Device for Device {
254 fn register_waker(&mut self, waker: &Waker) {
255 // loopy loopy wakey wakey
256 waker.wake_by_ref()
257 }
258
259 fn link_state(&mut self) -> embassy_net::LinkState {
260 match LINK_UP.load(Ordering::Relaxed) {
261 true => embassy_net::LinkState::Up,
262 false => embassy_net::LinkState::Down,
263 }
264 }
265
266 fn capabilities(&self) -> embassy_net::DeviceCapabilities {
267 let mut caps = embassy_net::DeviceCapabilities::default();
268 caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header
269 caps.medium = embassy_net::Medium::Ethernet;
270 caps
271 }
272
273 fn is_transmit_ready(&mut self) -> bool {
274 true
275 }
276
277 fn transmit(&mut self, pkt: PacketBuf) {
278 if TX_CHANNEL.try_send(pkt).is_err() {
279 warn!("TX failed")
280 }
281 }
282
283 fn receive<'a>(&mut self) -> Option<PacketBuf> {
284 RX_CHANNEL.try_recv().ok()
285 }
286
287 fn ethernet_address(&self) -> [u8; 6] {
288 self.mac_addr
289 }
290}
diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs
new file mode 100644
index 000000000..d275aba36
--- /dev/null
+++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs
@@ -0,0 +1,136 @@
1#![no_std]
2#![no_main]
3#![feature(generic_associated_types)]
4#![feature(type_alias_impl_trait)]
5
6use defmt::*;
7use embassy::executor::Spawner;
8use embassy::time::{Duration, Timer};
9use embassy_stm32::interrupt;
10use embassy_stm32::rcc::*;
11use embassy_stm32::time::Hertz;
12use embassy_stm32::usb::Driver;
13use embassy_stm32::{Config, Peripherals};
14use embassy_usb::control::OutResponse;
15use embassy_usb::Builder;
16use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State};
17use futures::future::join;
18use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
19
20use defmt_rtt as _; // global logger
21use panic_probe as _;
22
23fn config() -> Config {
24 let mut config = Config::default();
25 config.rcc.mux = ClockSrc::HSE(Hertz(16_000_000));
26
27 config.rcc.mux = ClockSrc::PLL(
28 PLLSource::HSI16,
29 PLLClkDiv::Div2,
30 PLLSrcDiv::Div1,
31 PLLMul::Mul10,
32 None,
33 );
34 config.rcc.hsi48 = true;
35
36 config
37}
38
39#[embassy::main(config = "config()")]
40async fn main(_spawner: Spawner, p: Peripherals) {
41 // Create the driver, from the HAL.
42 let irq = interrupt::take!(USB_FS);
43 let driver = Driver::new(p.USB, irq, p.PA12, p.PA11);
44
45 // Create embassy-usb Config
46 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
47 config.manufacturer = Some("Embassy");
48 config.product = Some("HID mouse example");
49 config.serial_number = Some("12345678");
50 config.max_power = 100;
51 config.max_packet_size_0 = 64;
52
53 // Create embassy-usb DeviceBuilder using the driver and config.
54 // It needs some buffers for building the descriptors.
55 let mut device_descriptor = [0; 256];
56 let mut config_descriptor = [0; 256];
57 let mut bos_descriptor = [0; 256];
58 let mut control_buf = [0; 64];
59 let request_handler = MyRequestHandler {};
60
61 let mut state = State::new();
62
63 let mut builder = Builder::new(
64 driver,
65 config,
66 &mut device_descriptor,
67 &mut config_descriptor,
68 &mut bos_descriptor,
69 &mut control_buf,
70 None,
71 );
72
73 // Create classes on the builder.
74 let config = embassy_usb_hid::Config {
75 report_descriptor: MouseReport::desc(),
76 request_handler: Some(&request_handler),
77 poll_ms: 60,
78 max_packet_size: 8,
79 };
80
81 let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config);
82
83 // Build the builder.
84 let mut usb = builder.build();
85
86 // Run the USB device.
87 let usb_fut = usb.run();
88
89 // Do stuff with the class!
90 let hid_fut = async {
91 let mut y: i8 = 5;
92 loop {
93 Timer::after(Duration::from_millis(500)).await;
94
95 y = -y;
96 let report = MouseReport {
97 buttons: 0,
98 x: 0,
99 y,
100 wheel: 0,
101 pan: 0,
102 };
103 match writer.write_serialize(&report).await {
104 Ok(()) => {}
105 Err(e) => warn!("Failed to send report: {:?}", e),
106 }
107 }
108 };
109
110 // Run everything concurrently.
111 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
112 join(usb_fut, hid_fut).await;
113}
114
115struct MyRequestHandler {}
116
117impl RequestHandler for MyRequestHandler {
118 fn get_report(&self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {
119 info!("Get report for {:?}", id);
120 None
121 }
122
123 fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse {
124 info!("Set report for {:?}: {=[u8]}", id, data);
125 OutResponse::Accepted
126 }
127
128 fn set_idle(&self, id: Option<ReportId>, dur: Duration) {
129 info!("Set idle rate for {:?} to {:?}", id, dur);
130 }
131
132 fn get_idle(&self, id: Option<ReportId>) -> Option<Duration> {
133 info!("Get idle rate for {:?}", id);
134 None
135 }
136}
diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs
new file mode 100644
index 000000000..987f1b692
--- /dev/null
+++ b/examples/stm32l5/src/bin/usb_serial.rs
@@ -0,0 +1,112 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::panic;
6use defmt::*;
7use defmt_rtt as _; // global logger
8use embassy::executor::Spawner;
9use embassy_stm32::interrupt;
10use embassy_stm32::rcc::*;
11use embassy_stm32::time::Hertz;
12use embassy_stm32::usb::{Driver, Instance};
13use embassy_stm32::{Config, Peripherals};
14use embassy_usb::driver::EndpointError;
15use embassy_usb::Builder;
16use embassy_usb_serial::{CdcAcmClass, State};
17use futures::future::join;
18use panic_probe as _;
19
20fn config() -> Config {
21 let mut config = Config::default();
22 config.rcc.mux = ClockSrc::HSE(Hertz(16_000_000));
23
24 config.rcc.mux = ClockSrc::PLL(
25 PLLSource::HSI16,
26 PLLClkDiv::Div2,
27 PLLSrcDiv::Div1,
28 PLLMul::Mul10,
29 None,
30 );
31 config.rcc.hsi48 = true;
32
33 config
34}
35
36#[embassy::main(config = "config()")]
37async fn main(_spawner: Spawner, p: Peripherals) {
38 info!("Hello World!");
39
40 // Create the driver, from the HAL.
41 let irq = interrupt::take!(USB_FS);
42 let driver = Driver::new(p.USB, irq, p.PA12, p.PA11);
43
44 // Create embassy-usb Config
45 let config = embassy_usb::Config::new(0xc0de, 0xcafe);
46 //config.max_packet_size_0 = 64;
47
48 // Create embassy-usb DeviceBuilder using the driver and config.
49 // It needs some buffers for building the descriptors.
50 let mut device_descriptor = [0; 256];
51 let mut config_descriptor = [0; 256];
52 let mut bos_descriptor = [0; 256];
53 let mut control_buf = [0; 7];
54
55 let mut state = State::new();
56
57 let mut builder = Builder::new(
58 driver,
59 config,
60 &mut device_descriptor,
61 &mut config_descriptor,
62 &mut bos_descriptor,
63 &mut control_buf,
64 None,
65 );
66
67 // Create classes on the builder.
68 let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
69
70 // Build the builder.
71 let mut usb = builder.build();
72
73 // Run the USB device.
74 let usb_fut = usb.run();
75
76 // Do stuff with the class!
77 let echo_fut = async {
78 loop {
79 class.wait_connection().await;
80 info!("Connected");
81 let _ = echo(&mut class).await;
82 info!("Disconnected");
83 }
84 };
85
86 // Run everything concurrently.
87 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
88 join(usb_fut, echo_fut).await;
89}
90
91struct Disconnected {}
92
93impl From<EndpointError> for Disconnected {
94 fn from(val: EndpointError) -> Self {
95 match val {
96 EndpointError::BufferOverflow => panic!("Buffer overflow"),
97 EndpointError::Disabled => Disconnected {},
98 }
99 }
100}
101
102async fn echo<'d, T: Instance + 'd>(
103 class: &mut CdcAcmClass<'d, Driver<'d, T>>,
104) -> Result<(), Disconnected> {
105 let mut buf = [0; 64];
106 loop {
107 let n = class.read_packet(&mut buf).await?;
108 let data = &buf[..n];
109 info!("data: {:x}", data);
110 class.write_packet(data).await?;
111 }
112}