aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2024-11-23 23:51:44 +0000
committerGitHub <[email protected]>2024-11-23 23:51:44 +0000
commitb9408f051080398f38e03f7d0d20bba860213064 (patch)
treedc29a8fa2839518af739d9028a0c2f5554f44585
parentbc7372d7011c36157e4d55e05d0a3c5a82ba6f1c (diff)
parentcc4b5ae9cb4d79f9fa378b2a06073eb7ae96d369 (diff)
Merge pull request #3212 from elagil/feat_usb_prepare_for_uac
feat(usb): Prepare `embassy-usb` for USB Audio, and add USB Audio Class 1.0 (playback only)
-rw-r--r--embassy-usb/src/class/mod.rs1
-rw-r--r--embassy-usb/src/class/uac1/class_codes.rs151
-rw-r--r--embassy-usb/src/class/uac1/mod.rs134
-rw-r--r--embassy-usb/src/class/uac1/speaker.rs778
-rw-r--r--embassy-usb/src/class/uac1/terminal_type.rs50
-rw-r--r--examples/stm32f4/Cargo.toml1
-rw-r--r--examples/stm32f4/src/bin/usb_uac_speaker.rs387
-rw-r--r--examples/stm32h5/src/bin/usb_uac_speaker.rs378
8 files changed, 1880 insertions, 0 deletions
diff --git a/embassy-usb/src/class/mod.rs b/embassy-usb/src/class/mod.rs
index b883ed4e5..4bd89eb66 100644
--- a/embassy-usb/src/class/mod.rs
+++ b/embassy-usb/src/class/mod.rs
@@ -3,4 +3,5 @@ pub mod cdc_acm;
3pub mod cdc_ncm; 3pub mod cdc_ncm;
4pub mod hid; 4pub mod hid;
5pub mod midi; 5pub mod midi;
6pub mod uac1;
6pub mod web_usb; 7pub mod web_usb;
diff --git a/embassy-usb/src/class/uac1/class_codes.rs b/embassy-usb/src/class/uac1/class_codes.rs
new file mode 100644
index 000000000..3f6956771
--- /dev/null
+++ b/embassy-usb/src/class/uac1/class_codes.rs
@@ -0,0 +1,151 @@
1//! Audio Device Class Codes as defined in Universal Serial Bus Device Class
2//! Definition for Audio Devices, Release 1.0, Appendix A and Universal Serial
3//! Bus Device Class Definition for Audio Data Formats, Release 1.0, Appendix
4//! A.1.1 (Audio Data Format Type I Codes)
5#![allow(dead_code)]
6
7/// The current version of the ADC specification (1.0)
8pub const ADC_VERSION: u16 = 0x0100;
9
10/// The current version of the USB device (1.0)
11pub const DEVICE_VERSION: u16 = 0x0100;
12
13/// Audio Interface Class Code
14pub const USB_AUDIO_CLASS: u8 = 0x01;
15
16// Audio Interface Subclass Codes
17pub const USB_UNDEFINED_SUBCLASS: u8 = 0x00;
18pub const USB_AUDIOCONTROL_SUBCLASS: u8 = 0x01;
19pub const USB_AUDIOSTREAMING_SUBCLASS: u8 = 0x02;
20pub const USB_MIDISTREAMING_SUBCLASS: u8 = 0x03;
21
22// Audio Protocol Code
23pub const PROTOCOL_NONE: u8 = 0x00;
24
25// Audio Class-Specific Descriptor Types
26pub const CS_UNDEFINED: u8 = 0x20;
27pub const CS_DEVICE: u8 = 0x21;
28pub const CS_CONFIGURATION: u8 = 0x22;
29pub const CS_STRING: u8 = 0x23;
30pub const CS_INTERFACE: u8 = 0x24;
31pub const CS_ENDPOINT: u8 = 0x25;
32
33// Descriptor Subtype
34pub const AC_DESCRIPTOR_UNDEFINED: u8 = 0x00;
35pub const HEADER_SUBTYPE: u8 = 0x01;
36pub const INPUT_TERMINAL: u8 = 0x02;
37pub const OUTPUT_TERMINAL: u8 = 0x03;
38pub const MIXER_UNIT: u8 = 0x04;
39pub const SELECTOR_UNIT: u8 = 0x05;
40pub const FEATURE_UNIT: u8 = 0x06;
41pub const PROCESSING_UNIT: u8 = 0x07;
42pub const EXTENSION_UNIT: u8 = 0x08;
43
44// Audio Class-Specific AS Interface Descriptor Subtypes
45pub const AS_DESCRIPTOR_UNDEFINED: u8 = 0x00;
46pub const AS_GENERAL: u8 = 0x01;
47pub const FORMAT_TYPE: u8 = 0x02;
48pub const FORMAT_SPECIFIC: u8 = 0x03;
49
50// Processing Unit Process Types
51pub const PROCESS_UNDEFINED: u16 = 0x00;
52pub const UP_DOWNMIX_PROCESS: u16 = 0x01;
53pub const DOLBY_PROLOGIC_PROCESS: u16 = 0x02;
54pub const DDD_STEREO_EXTENDER_PROCESS: u16 = 0x03;
55pub const REVERBERATION_PROCESS: u16 = 0x04;
56pub const CHORUS_PROCESS: u16 = 0x05;
57pub const DYN_RANGE_COMP_PROCESS: u16 = 0x06;
58
59// Audio Class-Specific Endpoint Descriptor Subtypes
60pub const EP_DESCRIPTOR_UNDEFINED: u8 = 0x00;
61pub const EP_GENERAL: u8 = 0x01;
62
63// Audio Class-Specific Request Codes
64pub const REQUEST_CODE_UNDEFINED: u8 = 0x00;
65pub const SET_CUR: u8 = 0x01;
66pub const GET_CUR: u8 = 0x81;
67pub const SET_MIN: u8 = 0x02;
68pub const GET_MIN: u8 = 0x82;
69pub const SET_MAX: u8 = 0x03;
70pub const GET_MAX: u8 = 0x83;
71pub const SET_RES: u8 = 0x04;
72pub const GET_RES: u8 = 0x84;
73pub const SET_MEM: u8 = 0x05;
74pub const GET_MEM: u8 = 0x85;
75pub const GET_STAT: u8 = 0xFF;
76
77// Terminal Control Selectors
78pub const TE_CONTROL_UNDEFINED: u8 = 0x00;
79pub const COPY_PROTECT_CONTROL: u8 = 0x01;
80
81// Feature Unit Control Selectors
82pub const FU_CONTROL_UNDEFINED: u8 = 0x00;
83pub const MUTE_CONTROL: u8 = 0x01;
84pub const VOLUME_CONTROL: u8 = 0x02;
85pub const BASS_CONTROL: u8 = 0x03;
86pub const MID_CONTROL: u8 = 0x04;
87pub const TREBLE_CONTROL: u8 = 0x05;
88pub const GRAPHIC_EQUALIZER_CONTROL: u8 = 0x06;
89pub const AUTOMATIC_GAIN_CONTROL: u8 = 0x07;
90pub const DELAY_CONTROL: u8 = 0x08;
91pub const BASS_BOOST_CONTROL: u8 = 0x09;
92pub const LOUDNESS_CONTROL: u8 = 0x0A;
93
94// Up/Down-mix Processing Unit Control Selectors
95pub const UD_CONTROL_UNDEFINED: u8 = 0x00;
96pub const UD_ENABLE_CONTROL: u8 = 0x01;
97pub const UD_MODE_SELECT_CONTROL: u8 = 0x02;
98
99// Dolby Prologic Processing Unit Control Selectors
100pub const DP_CONTROL_UNDEFINED: u8 = 0x00;
101pub const DP_ENABLE_CONTROL: u8 = 0x01;
102pub const DP_MODE_SELECT_CONTROL: u8 = 0x2;
103
104// 3D Stereo Extender Processing Unit Control Selectors
105pub const DDD_CONTROL_UNDEFINED: u8 = 0x00;
106pub const DDD_ENABLE_CONTROL: u8 = 0x01;
107pub const DDD_SPACIOUSNESS_CONTROL: u8 = 0x03;
108
109// Reverberation Processing Unit Control Selectors
110pub const RV_CONTROL_UNDEFINED: u8 = 0x00;
111pub const RV_ENABLE_CONTROL: u8 = 0x01;
112pub const REVERB_LEVEL_CONTROL: u8 = 0x02;
113pub const REVERB_TIME_CONTROL: u8 = 0x03;
114pub const REVERB_FEEDBACK_CONTROL: u8 = 0x04;
115
116// Chorus Processing Unit Control Selectors
117pub const CH_CONTROL_UNDEFINED: u8 = 0x00;
118pub const CH_ENABLE_CONTROL: u8 = 0x01;
119pub const CHORUS_LEVEL_CONTROL: u8 = 0x02;
120pub const CHORUS_RATE_CONTROL: u8 = 0x03;
121pub const CHORUS_DEPTH_CONTROL: u8 = 0x04;
122
123// Dynamic Range Compressor Processing Unit Control Selectors
124pub const DR_CONTROL_UNDEFINED: u8 = 0x00;
125pub const DR_ENABLE_CONTROL: u8 = 0x01;
126pub const COMPRESSION_RATE_CONTROL: u8 = 0x02;
127pub const MAXAMPL_CONTROL: u8 = 0x03;
128pub const THRESHOLD_CONTROL: u8 = 0x04;
129pub const ATTACK_TIME: u8 = 0x05;
130pub const RELEASE_TIME: u8 = 0x06;
131
132// Extension Unit Control Selectors
133pub const XU_CONTROL_UNDEFINED: u16 = 0x00;
134pub const XU_ENABLE_CONTROL: u16 = 0x01;
135
136// Endpoint Control Selectors
137pub const EP_CONTROL_UNDEFINED: u8 = 0x00;
138pub const SAMPLING_FREQ_CONTROL: u8 = 0x01;
139pub const PITCH_CONTROL: u8 = 0x02;
140
141// Format Type Codes
142pub const FORMAT_TYPE_UNDEFINED: u8 = 0x00;
143pub const FORMAT_TYPE_I: u8 = 0x01;
144
145// Audio Data Format Type I Codes
146pub const TYPE_I_UNDEFINED: u16 = 0x0000;
147pub const PCM: u16 = 0x0001;
148pub const PCM8: u16 = 0x0002;
149pub const IEEE_FLOAT: u16 = 0x0003;
150pub const ALAW: u16 = 0x0004;
151pub const MULAW: u16 = 0x0005;
diff --git a/embassy-usb/src/class/uac1/mod.rs b/embassy-usb/src/class/uac1/mod.rs
new file mode 100644
index 000000000..3d5f4e524
--- /dev/null
+++ b/embassy-usb/src/class/uac1/mod.rs
@@ -0,0 +1,134 @@
1//! USB Audio Class 1.0 implementations for different applications.
2//!
3//! Contains:
4//! - The `speaker` class with a single audio streaming interface (host to device)
5
6pub mod speaker;
7
8mod class_codes;
9mod terminal_type;
10
11/// The maximum supported audio channel index (corresponds to `Top`).
12/// FIXME: Use `core::mem::variant_count(...)` when stabilized.
13const MAX_AUDIO_CHANNEL_INDEX: usize = 12;
14
15/// The maximum number of supported audio channels.
16///
17/// Includes all twelve channels from `Channel`, plus the Master channel.
18const MAX_AUDIO_CHANNEL_COUNT: usize = MAX_AUDIO_CHANNEL_INDEX + 1;
19
20/// USB Audio Channel
21#[derive(Debug, Clone, Copy, PartialEq)]
22#[allow(missing_docs)]
23#[cfg_attr(feature = "defmt", derive(defmt::Format))]
24pub enum Channel {
25 LeftFront,
26 RightFront,
27 CenterFront,
28 Lfe,
29 LeftSurround,
30 RightSurround,
31 LeftOfCenter,
32 RightOfCenter,
33 Surround,
34 SideLeft,
35 SideRight,
36 Top,
37}
38
39impl Channel {
40 /// Map a `Channel` to its corresponding USB Audio `ChannelConfig`.
41 fn get_channel_config(&self) -> ChannelConfig {
42 match self {
43 Channel::LeftFront => ChannelConfig::LeftFront,
44 Channel::RightFront => ChannelConfig::RightFront,
45 Channel::CenterFront => ChannelConfig::CenterFront,
46 Channel::Lfe => ChannelConfig::Lfe,
47 Channel::LeftSurround => ChannelConfig::LeftSurround,
48 Channel::RightSurround => ChannelConfig::RightSurround,
49 Channel::LeftOfCenter => ChannelConfig::LeftOfCenter,
50 Channel::RightOfCenter => ChannelConfig::RightOfCenter,
51 Channel::Surround => ChannelConfig::Surround,
52 Channel::SideLeft => ChannelConfig::SideLeft,
53 Channel::SideRight => ChannelConfig::SideRight,
54 Channel::Top => ChannelConfig::Top,
55 }
56 }
57}
58
59/// USB Audio Channel configuration
60#[repr(u16)]
61#[non_exhaustive]
62// #[derive(Copy, Clone, Eq, PartialEq, Debug)]
63enum ChannelConfig {
64 None = 0x0000,
65 LeftFront = 0x0001,
66 RightFront = 0x0002,
67 CenterFront = 0x0004,
68 Lfe = 0x0008,
69 LeftSurround = 0x0010,
70 RightSurround = 0x0020,
71 LeftOfCenter = 0x0040,
72 RightOfCenter = 0x0080,
73 Surround = 0x0100,
74 SideLeft = 0x0200,
75 SideRight = 0x0400,
76 Top = 0x0800,
77}
78
79impl From<ChannelConfig> for u16 {
80 fn from(t: ChannelConfig) -> u16 {
81 t as u16
82 }
83}
84
85/// Feedback period adjustment `bRefresh` [UAC 3.7.2.2]
86///
87/// From the specification: "A new Ff value is available every 2^(10 – P) frames with P ranging from 1 to 9. The
88/// bRefresh field of the synch standard endpoint descriptor is used to report the exponent (10-P) to the Host."
89///
90/// This means:
91/// - 512 ms (2^9 frames) to 2 ms (2^1 frames) for USB full-speed
92/// - 64 ms (2^9 microframes) to 0.25 ms (2^1 microframes) for USB high-speed
93#[repr(u8)]
94#[allow(missing_docs)]
95#[derive(Clone, Copy)]
96pub enum FeedbackRefresh {
97 Period2Frames = 1,
98 Period4Frames = 2,
99 Period8Frames = 3,
100 Period16Frames = 4,
101 Period32Frames = 5,
102 Period64Frames = 6,
103 Period128Frames = 7,
104 Period256Frames = 8,
105 Period512Frames = 9,
106}
107
108impl FeedbackRefresh {
109 /// Gets the number of frames, after which a new feedback frame is returned.
110 pub const fn frame_count(&self) -> usize {
111 1 << (*self as usize)
112 }
113}
114
115/// Audio sample width.
116///
117/// Stored in number of bytes per sample.
118#[repr(u8)]
119#[derive(Clone, Copy)]
120pub enum SampleWidth {
121 /// 16 bit audio
122 Width2Byte = 2,
123 /// 24 bit audio
124 Width3Byte = 3,
125 /// 32 bit audio
126 Width4Byte = 4,
127}
128
129impl SampleWidth {
130 /// Get the audio sample resolution in number of bit.
131 pub const fn in_bit(self) -> usize {
132 8 * self as usize
133 }
134}
diff --git a/embassy-usb/src/class/uac1/speaker.rs b/embassy-usb/src/class/uac1/speaker.rs
new file mode 100644
index 000000000..96456d94a
--- /dev/null
+++ b/embassy-usb/src/class/uac1/speaker.rs
@@ -0,0 +1,778 @@
1//! USB Audio Class 1.0 - Speaker device
2//!
3//! Provides a class with a single audio streaming interface (host to device),
4//! that advertises itself as a speaker. Includes explicit sample rate feedback.
5//!
6//! Various aspects of the audio stream can be configured, for example:
7//! - sample rate
8//! - sample resolution
9//! - audio channel count and assignment
10//!
11//! The class provides volume and mute controls for each channel.
12
13use core::cell::{Cell, RefCell};
14use core::future::poll_fn;
15use core::marker::PhantomData;
16use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
17use core::task::Poll;
18
19use embassy_sync::blocking_mutex::CriticalSectionMutex;
20use embassy_sync::waitqueue::WakerRegistration;
21use heapless::Vec;
22
23use super::class_codes::*;
24use super::terminal_type::TerminalType;
25use super::{Channel, ChannelConfig, FeedbackRefresh, SampleWidth, MAX_AUDIO_CHANNEL_COUNT, MAX_AUDIO_CHANNEL_INDEX};
26use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType};
27use crate::descriptor::{SynchronizationType, UsageType};
28use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut, EndpointType};
29use crate::types::InterfaceNumber;
30use crate::{Builder, Handler};
31
32/// Maximum allowed sampling rate (3 bytes) in Hz.
33const MAX_SAMPLE_RATE_HZ: u32 = 0x7FFFFF;
34
35/// Arbitrary unique identifier for the input unit.
36const INPUT_UNIT_ID: u8 = 0x01;
37
38/// Arbitrary unique identifier for the feature unit.
39const FEATURE_UNIT_ID: u8 = 0x02;
40
41/// Arbitrary unique identifier for the output unit.
42const OUTPUT_UNIT_ID: u8 = 0x03;
43
44// Volume settings go from -25600 to 0, in steps of 256.
45// Therefore, the volume settings are 8q8 values in units of dB.
46const VOLUME_STEPS_PER_DB: i16 = 256;
47const MIN_VOLUME_DB: i16 = -100;
48const MAX_VOLUME_DB: i16 = 0;
49
50// Maximum number of supported discrete sample rates.
51const MAX_SAMPLE_RATE_COUNT: usize = 10;
52
53/// The volume of an audio channel.
54#[derive(Debug, Clone, Copy)]
55#[cfg_attr(feature = "defmt", derive(defmt::Format))]
56pub enum Volume {
57 /// The channel is muted.
58 Muted,
59 /// The channel volume in dB. Ranges from `MIN_VOLUME_DB` (quietest) to `MAX_VOLUME_DB` (loudest).
60 DeciBel(f32),
61}
62
63/// Internal state for the USB Audio Class.
64pub struct State<'d> {
65 control: Option<Control<'d>>,
66 shared: SharedControl<'d>,
67}
68
69impl<'d> Default for State<'d> {
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75impl<'d> State<'d> {
76 /// Create a new `State`.
77 pub fn new() -> Self {
78 Self {
79 control: None,
80 shared: SharedControl::default(),
81 }
82 }
83}
84
85/// Implementation of the USB audio class 1.0.
86pub struct Speaker<'d, D: Driver<'d>> {
87 phantom: PhantomData<&'d D>,
88}
89
90impl<'d, D: Driver<'d>> Speaker<'d, D> {
91 /// Creates a new [`Speaker`] device, split into a stream, feedback, and a control change notifier.
92 ///
93 /// The packet size should be chosen, based on the expected transfer size of samples per (micro)frame.
94 /// For example, a stereo stream at 32 bit resolution and 48 kHz sample rate yields packets of 384 byte for
95 /// full-speed USB (1 ms frame interval) or 48 byte for high-speed USB (125 us microframe interval).
96 /// When using feedback, the packet size varies and thus, the `max_packet_size` should be increased (e.g. to double).
97 ///
98 /// # Arguments
99 ///
100 /// * `builder` - The builder for the class.
101 /// * `state` - The internal state of the class.
102 /// * `max_packet_size` - The maximum packet size per (micro)frame.
103 /// * `resolution` - The audio sample resolution.
104 /// * `sample_rates_hz` - The supported sample rates in Hz.
105 /// * `channels` - The advertised audio channels (up to 12). Entries must be unique, or this function panics.
106 /// * `feedback_refresh_period` - The refresh period for the feedback value.
107 pub fn new(
108 builder: &mut Builder<'d, D>,
109 state: &'d mut State<'d>,
110 max_packet_size: u16,
111 resolution: SampleWidth,
112 sample_rates_hz: &[u32],
113 channels: &'d [Channel],
114 feedback_refresh_period: FeedbackRefresh,
115 ) -> (Stream<'d, D>, Feedback<'d, D>, ControlMonitor<'d>) {
116 // The class and subclass fields of the IAD aren't required to match the class and subclass fields of
117 // the interfaces in the interface collection that the IAD describes. Microsoft recommends that
118 // the first interface of the collection has class and subclass fields that match the class and
119 // subclass fields of the IAD.
120 let mut func = builder.function(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE);
121
122 // Audio control interface (mandatory) [UAC 4.3.1]
123 let mut interface = func.interface();
124 let control_interface = interface.interface_number().into();
125 let streaming_interface = u8::from(control_interface) + 1;
126 let mut alt = interface.alt_setting(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE, None);
127
128 // Terminal topology:
129 // Input terminal (receives audio stream) -> Feature Unit (mute and volume) -> Output terminal (e.g. towards speaker)
130
131 // =======================================
132 // Input Terminal Descriptor [UAC 3.3.2.1]
133 // Audio input
134 let terminal_type: u16 = TerminalType::UsbStreaming.into();
135
136 // Assemble channel configuration field
137 let mut channel_config: u16 = ChannelConfig::None.into();
138 for channel in channels {
139 let channel: u16 = channel.get_channel_config().into();
140
141 if channel_config & channel != 0 {
142 panic!("Invalid channel config, duplicate channel {}.", channel);
143 }
144 channel_config |= channel;
145 }
146
147 let input_terminal_descriptor = [
148 INPUT_TERMINAL, // bDescriptorSubtype
149 INPUT_UNIT_ID, // bTerminalID
150 terminal_type as u8,
151 (terminal_type >> 8) as u8, // wTerminalType
152 0x00, // bAssocTerminal (none)
153 channels.len() as u8, // bNrChannels
154 channel_config as u8,
155 (channel_config >> 8) as u8, // wChannelConfig
156 0x00, // iChannelNames (none)
157 0x00, // iTerminal (none)
158 ];
159
160 // ========================================
161 // Output Terminal Descriptor [UAC 4.3.2.2]
162 // Speaker output
163 let terminal_type: u16 = TerminalType::OutSpeaker.into();
164 let output_terminal_descriptor = [
165 OUTPUT_TERMINAL, // bDescriptorSubtype
166 OUTPUT_UNIT_ID, // bTerminalID
167 terminal_type as u8,
168 (terminal_type >> 8) as u8, // wTerminalType
169 0x00, // bAssocTerminal (none)
170 FEATURE_UNIT_ID, // bSourceID (the feature unit)
171 0x00, // iTerminal (none)
172 ];
173
174 // =====================================
175 // Feature Unit Descriptor [UAC 4.3.2.5]
176 // Mute and volume control
177 let controls = MUTE_CONTROL | VOLUME_CONTROL;
178
179 const FEATURE_UNIT_DESCRIPTOR_SIZE: usize = 5;
180 let mut feature_unit_descriptor: Vec<u8, { FEATURE_UNIT_DESCRIPTOR_SIZE + MAX_AUDIO_CHANNEL_COUNT + 1 }> =
181 Vec::from_slice(&[
182 FEATURE_UNIT, // bDescriptorSubtype (Feature Unit)
183 FEATURE_UNIT_ID, // bUnitID
184 INPUT_UNIT_ID, // bSourceID
185 1, // bControlSize (one byte per control)
186 FU_CONTROL_UNDEFINED, // Master controls (disabled, use only per-channel control)
187 ])
188 .unwrap();
189
190 // Add per-channel controls
191 for _channel in channels {
192 feature_unit_descriptor.push(controls).unwrap();
193 }
194 feature_unit_descriptor.push(0x00).unwrap(); // iFeature (none)
195
196 // ===============================================
197 // Format desciptor [UAC 4.5.3]
198 // Used later, for operational streaming interface
199 let mut format_descriptor: Vec<u8, { 6 + 3 * MAX_SAMPLE_RATE_COUNT }> = Vec::from_slice(&[
200 FORMAT_TYPE, // bDescriptorSubtype
201 FORMAT_TYPE_I, // bFormatType
202 channels.len() as u8, // bNrChannels
203 resolution as u8, // bSubframeSize
204 resolution.in_bit() as u8, // bBitResolution
205 ])
206 .unwrap();
207
208 format_descriptor.push(sample_rates_hz.len() as u8).unwrap();
209
210 for sample_rate_hz in sample_rates_hz {
211 assert!(*sample_rate_hz <= MAX_SAMPLE_RATE_HZ);
212 format_descriptor.push((sample_rate_hz & 0xFF) as u8).unwrap();
213 format_descriptor.push(((sample_rate_hz >> 8) & 0xFF) as u8).unwrap();
214 format_descriptor.push(((sample_rate_hz >> 16) & 0xFF) as u8).unwrap();
215 }
216
217 // ==================================================
218 // Class-specific AC Interface Descriptor [UAC 4.3.2]
219 const DESCRIPTOR_HEADER_SIZE: usize = 2;
220 const INTERFACE_DESCRIPTOR_SIZE: usize = 7;
221
222 let mut total_descriptor_length = 0;
223
224 for size in [
225 INTERFACE_DESCRIPTOR_SIZE,
226 input_terminal_descriptor.len(),
227 feature_unit_descriptor.len(),
228 output_terminal_descriptor.len(),
229 ] {
230 total_descriptor_length += size + DESCRIPTOR_HEADER_SIZE;
231 }
232
233 let interface_descriptor: [u8; INTERFACE_DESCRIPTOR_SIZE] = [
234 HEADER_SUBTYPE, // bDescriptorSubtype (Header)
235 ADC_VERSION as u8,
236 (ADC_VERSION >> 8) as u8, // bcdADC
237 total_descriptor_length as u8,
238 (total_descriptor_length >> 8) as u8, // wTotalLength
239 0x01, // bInCollection (1 streaming interface)
240 streaming_interface, // baInterfaceNr
241 ];
242
243 alt.descriptor(CS_INTERFACE, &interface_descriptor);
244 alt.descriptor(CS_INTERFACE, &input_terminal_descriptor);
245 alt.descriptor(CS_INTERFACE, &feature_unit_descriptor);
246 alt.descriptor(CS_INTERFACE, &output_terminal_descriptor);
247
248 // =====================================================
249 // Audio streaming interface, zero-bandwidth [UAC 4.5.1]
250 let mut interface = func.interface();
251 let alt = interface.alt_setting(USB_AUDIO_CLASS, USB_AUDIOSTREAMING_SUBCLASS, PROTOCOL_NONE, None);
252 drop(alt);
253
254 // ==================================================
255 // Audio streaming interface, operational [UAC 4.5.1]
256 let mut alt = interface.alt_setting(USB_AUDIO_CLASS, USB_AUDIOSTREAMING_SUBCLASS, PROTOCOL_NONE, None);
257
258 alt.descriptor(
259 CS_INTERFACE,
260 &[
261 AS_GENERAL, // bDescriptorSubtype
262 INPUT_UNIT_ID, // bTerminalLink
263 0x00, // bDelay (none)
264 PCM as u8,
265 (PCM >> 8) as u8, // wFormatTag (PCM format)
266 ],
267 );
268
269 alt.descriptor(CS_INTERFACE, &format_descriptor);
270
271 let streaming_endpoint = alt.alloc_endpoint_out(EndpointType::Isochronous, max_packet_size, 1);
272 let feedback_endpoint = alt.alloc_endpoint_in(
273 EndpointType::Isochronous,
274 4, // Feedback packets are 24 bit (10.14 format).
275 1,
276 );
277
278 // Write the descriptor for the streaming endpoint, after knowing the address of the feedback endpoint.
279 alt.endpoint_descriptor(
280 streaming_endpoint.info(),
281 SynchronizationType::Asynchronous,
282 UsageType::DataEndpoint,
283 &[
284 0x00, // bRefresh (0)
285 feedback_endpoint.info().addr.into(), // bSynchAddress (the feedback endpoint)
286 ],
287 );
288
289 alt.descriptor(
290 CS_ENDPOINT,
291 &[
292 AS_GENERAL, // bDescriptorSubtype (General)
293 SAMPLING_FREQ_CONTROL, // bmAttributes (support sampling frequency control)
294 0x02, // bLockDelayUnits (PCM)
295 0x0000 as u8,
296 (0x0000 >> 8) as u8, // wLockDelay (0)
297 ],
298 );
299
300 // Write the feedback endpoint descriptor after the streaming endpoint descriptor
301 // This is demanded by the USB audio class specification.
302 alt.endpoint_descriptor(
303 feedback_endpoint.info(),
304 SynchronizationType::NoSynchronization,
305 UsageType::FeedbackEndpoint,
306 &[
307 feedback_refresh_period as u8, // bRefresh
308 0x00, // bSynchAddress (none)
309 ],
310 );
311
312 // Free up the builder.
313 drop(func);
314
315 // Store channel information
316 state.shared.channels = channels;
317
318 state.control = Some(Control {
319 shared: &state.shared,
320 streaming_endpoint_address: streaming_endpoint.info().addr.into(),
321 control_interface_number: control_interface,
322 });
323
324 builder.handler(state.control.as_mut().unwrap());
325
326 let control = &state.shared;
327
328 (
329 Stream { streaming_endpoint },
330 Feedback { feedback_endpoint },
331 ControlMonitor { shared: control },
332 )
333 }
334}
335
336/// Audio settings for the feature unit.
337///
338/// Contains volume and mute control.
339#[derive(Clone, Copy, Debug)]
340#[cfg_attr(feature = "defmt", derive(defmt::Format))]
341pub struct AudioSettings {
342 /// Channel mute states.
343 muted: [bool; MAX_AUDIO_CHANNEL_COUNT],
344 /// Channel volume levels in 8.8 format (in dB).
345 volume_8q8_db: [i16; MAX_AUDIO_CHANNEL_COUNT],
346}
347
348impl Default for AudioSettings {
349 fn default() -> Self {
350 AudioSettings {
351 muted: [true; MAX_AUDIO_CHANNEL_COUNT],
352 volume_8q8_db: [MAX_VOLUME_DB * VOLUME_STEPS_PER_DB; MAX_AUDIO_CHANNEL_COUNT],
353 }
354 }
355}
356
357struct Control<'d> {
358 control_interface_number: InterfaceNumber,
359 streaming_endpoint_address: u8,
360 shared: &'d SharedControl<'d>,
361}
362
363/// Shared data between [`Control`] and the [`Speaker`] class.
364struct SharedControl<'d> {
365 /// The collection of audio settings (volumes, mute states).
366 audio_settings: CriticalSectionMutex<Cell<AudioSettings>>,
367
368 /// Channel assignments.
369 channels: &'d [Channel],
370
371 /// The audio sample rate in Hz.
372 sample_rate_hz: AtomicU32,
373
374 // Notification mechanism.
375 waker: RefCell<WakerRegistration>,
376 changed: AtomicBool,
377}
378
379impl<'d> Default for SharedControl<'d> {
380 fn default() -> Self {
381 SharedControl {
382 audio_settings: CriticalSectionMutex::new(Cell::new(AudioSettings::default())),
383 channels: &[],
384 sample_rate_hz: AtomicU32::new(0),
385 waker: RefCell::new(WakerRegistration::new()),
386 changed: AtomicBool::new(false),
387 }
388 }
389}
390
391impl<'d> SharedControl<'d> {
392 async fn changed(&self) {
393 poll_fn(|context| {
394 if self.changed.load(Ordering::Relaxed) {
395 self.changed.store(false, Ordering::Relaxed);
396 Poll::Ready(())
397 } else {
398 self.waker.borrow_mut().register(context.waker());
399 Poll::Pending
400 }
401 })
402 .await;
403 }
404}
405
406/// Used for reading audio frames.
407pub struct Stream<'d, D: Driver<'d>> {
408 streaming_endpoint: D::EndpointOut,
409}
410
411impl<'d, D: Driver<'d>> Stream<'d, D> {
412 /// Reads a single packet from the OUT endpoint
413 pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> {
414 self.streaming_endpoint.read(data).await
415 }
416
417 /// Waits for the USB host to enable this interface
418 pub async fn wait_connection(&mut self) {
419 self.streaming_endpoint.wait_enabled().await;
420 }
421}
422
423/// Used for writing sample rate information over the feedback endpoint.
424pub struct Feedback<'d, D: Driver<'d>> {
425 feedback_endpoint: D::EndpointIn,
426}
427
428impl<'d, D: Driver<'d>> Feedback<'d, D> {
429 /// Writes a single packet into the IN endpoint.
430 pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {
431 self.feedback_endpoint.write(data).await
432 }
433
434 /// Waits for the USB host to enable this interface.
435 pub async fn wait_connection(&mut self) {
436 self.feedback_endpoint.wait_enabled().await;
437 }
438}
439
440/// Control status change monitor
441///
442/// Await [`ControlMonitor::changed`] for being notified of configuration changes. Afterwards, the updated
443/// configuration settings can be read with [`ControlMonitor::volume`] and [`ControlMonitor::sample_rate_hz`].
444pub struct ControlMonitor<'d> {
445 shared: &'d SharedControl<'d>,
446}
447
448impl<'d> ControlMonitor<'d> {
449 fn audio_settings(&self) -> AudioSettings {
450 let audio_settings = self.shared.audio_settings.lock(|x| x.get());
451
452 audio_settings
453 }
454
455 fn get_logical_channel(&self, search_channel: Channel) -> Option<usize> {
456 let index = self.shared.channels.iter().position(|&c| c == search_channel)?;
457
458 // The logical channels start at one (zero is the master channel).
459 Some(index + 1)
460 }
461
462 /// Get the volume of a selected channel.
463 pub fn volume(&self, channel: Channel) -> Option<Volume> {
464 let channel_index = self.get_logical_channel(channel)?;
465
466 if self.audio_settings().muted[channel_index] {
467 return Some(Volume::Muted);
468 }
469
470 Some(Volume::DeciBel(
471 (self.audio_settings().volume_8q8_db[channel_index] as f32) / 256.0f32,
472 ))
473 }
474
475 /// Get the streaming endpoint's sample rate in Hz.
476 pub fn sample_rate_hz(&self) -> u32 {
477 self.shared.sample_rate_hz.load(Ordering::Relaxed)
478 }
479
480 /// Return a future for when the control settings change.
481 pub async fn changed(&self) {
482 self.shared.changed().await;
483 }
484}
485
486impl<'d> Control<'d> {
487 fn changed(&mut self) {
488 self.shared.changed.store(true, Ordering::Relaxed);
489 self.shared.waker.borrow_mut().wake();
490 }
491
492 fn interface_set_mute_state(
493 &mut self,
494 audio_settings: &mut AudioSettings,
495 channel_index: u8,
496 data: &[u8],
497 ) -> OutResponse {
498 let mute_state = data[0] != 0;
499
500 match channel_index as usize {
501 ..=MAX_AUDIO_CHANNEL_INDEX => {
502 audio_settings.muted[channel_index as usize] = mute_state;
503 }
504 _ => {
505 debug!("Failed to set channel {} mute state: {}", channel_index, mute_state);
506 return OutResponse::Rejected;
507 }
508 }
509
510 debug!("Set channel {} mute state: {}", channel_index, mute_state);
511 OutResponse::Accepted
512 }
513
514 fn interface_set_volume(
515 &mut self,
516 audio_settings: &mut AudioSettings,
517 channel_index: u8,
518 data: &[u8],
519 ) -> OutResponse {
520 let volume = i16::from_ne_bytes(data[..2].try_into().expect("Failed to read volume."));
521
522 match channel_index as usize {
523 ..=MAX_AUDIO_CHANNEL_INDEX => {
524 audio_settings.volume_8q8_db[channel_index as usize] = volume;
525 }
526 _ => {
527 debug!("Failed to set channel {} volume: {}", channel_index, volume);
528 return OutResponse::Rejected;
529 }
530 }
531
532 debug!("Set channel {} volume: {}", channel_index, volume);
533 OutResponse::Accepted
534 }
535
536 fn interface_set_request(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> {
537 let interface_number = req.index as u8;
538 let entity_index = (req.index >> 8) as u8;
539 let channel_index = req.value as u8;
540 let control_unit = (req.value >> 8) as u8;
541
542 if interface_number != self.control_interface_number.into() {
543 debug!("Unhandled interface set request for interface {}", interface_number);
544 return None;
545 }
546
547 if entity_index != FEATURE_UNIT_ID {
548 debug!("Unsupported interface set request for entity {}", entity_index);
549 return Some(OutResponse::Rejected);
550 }
551
552 if req.request != SET_CUR {
553 debug!("Unsupported interface set request type {}", req.request);
554 return Some(OutResponse::Rejected);
555 }
556
557 let mut audio_settings = self.shared.audio_settings.lock(|x| x.get());
558 let response = match control_unit {
559 MUTE_CONTROL => self.interface_set_mute_state(&mut audio_settings, channel_index, data),
560 VOLUME_CONTROL => self.interface_set_volume(&mut audio_settings, channel_index, data),
561 _ => OutResponse::Rejected,
562 };
563
564 if response == OutResponse::Rejected {
565 return Some(response);
566 }
567
568 // Store updated settings
569 self.shared.audio_settings.lock(|x| x.set(audio_settings));
570
571 self.changed();
572
573 Some(OutResponse::Accepted)
574 }
575
576 fn endpoint_set_request(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> {
577 let control_selector = (req.value >> 8) as u8;
578 let endpoint_address = req.index as u8;
579
580 if endpoint_address != self.streaming_endpoint_address {
581 debug!(
582 "Unhandled endpoint set request for endpoint {} and control {} with data {}",
583 endpoint_address, control_selector, data
584 );
585 return None;
586 }
587
588 if control_selector != SAMPLING_FREQ_CONTROL {
589 debug!(
590 "Unsupported endpoint set request for control selector {}",
591 control_selector
592 );
593 return Some(OutResponse::Rejected);
594 }
595
596 let sample_rate_hz: u32 = (data[0] as u32) | (data[1] as u32) << 8 | (data[2] as u32) << 16;
597 self.shared.sample_rate_hz.store(sample_rate_hz, Ordering::Relaxed);
598
599 debug!("Set endpoint {} sample rate to {} Hz", endpoint_address, sample_rate_hz);
600
601 self.changed();
602
603 Some(OutResponse::Accepted)
604 }
605
606 fn interface_get_request<'r>(&'r mut self, req: Request, buf: &'r mut [u8]) -> Option<InResponse<'r>> {
607 let interface_number = req.index as u8;
608 let entity_index = (req.index >> 8) as u8;
609 let channel_index = req.value as u8;
610 let control_unit = (req.value >> 8) as u8;
611
612 if interface_number != self.control_interface_number.into() {
613 debug!("Unhandled interface get request for interface {}.", interface_number);
614 return None;
615 }
616
617 if entity_index != FEATURE_UNIT_ID {
618 // Only this function unit can be handled at the moment.
619 debug!("Unsupported interface get request for entity {}.", entity_index);
620 return Some(InResponse::Rejected);
621 }
622
623 let audio_settings = self.shared.audio_settings.lock(|x| x.get());
624
625 match req.request {
626 GET_CUR => match control_unit {
627 VOLUME_CONTROL => {
628 let volume: i16;
629
630 match channel_index as usize {
631 ..=MAX_AUDIO_CHANNEL_INDEX => volume = audio_settings.volume_8q8_db[channel_index as usize],
632 _ => return Some(InResponse::Rejected),
633 }
634
635 buf[0] = volume as u8;
636 buf[1] = (volume >> 8) as u8;
637
638 debug!("Got channel {} volume: {}.", channel_index, volume);
639 return Some(InResponse::Accepted(&buf[..2]));
640 }
641 MUTE_CONTROL => {
642 let mute_state: bool;
643
644 match channel_index as usize {
645 ..=MAX_AUDIO_CHANNEL_INDEX => mute_state = audio_settings.muted[channel_index as usize],
646 _ => return Some(InResponse::Rejected),
647 }
648
649 buf[0] = mute_state.into();
650 debug!("Got channel {} mute state: {}.", channel_index, mute_state);
651 return Some(InResponse::Accepted(&buf[..1]));
652 }
653 _ => return Some(InResponse::Rejected),
654 },
655 GET_MIN => match control_unit {
656 VOLUME_CONTROL => {
657 let min_volume = MIN_VOLUME_DB * VOLUME_STEPS_PER_DB;
658 buf[0] = min_volume as u8;
659 buf[1] = (min_volume >> 8) as u8;
660 return Some(InResponse::Accepted(&buf[..2]));
661 }
662 _ => return Some(InResponse::Rejected),
663 },
664 GET_MAX => match control_unit {
665 VOLUME_CONTROL => {
666 let max_volume = MAX_VOLUME_DB * VOLUME_STEPS_PER_DB;
667 buf[0] = max_volume as u8;
668 buf[1] = (max_volume >> 8) as u8;
669 return Some(InResponse::Accepted(&buf[..2]));
670 }
671 _ => return Some(InResponse::Rejected),
672 },
673 GET_RES => match control_unit {
674 VOLUME_CONTROL => {
675 buf[0] = VOLUME_STEPS_PER_DB as u8;
676 buf[1] = (VOLUME_STEPS_PER_DB >> 8) as u8;
677 return Some(InResponse::Accepted(&buf[..2]));
678 }
679 _ => return Some(InResponse::Rejected),
680 },
681 _ => return Some(InResponse::Rejected),
682 }
683 }
684
685 fn endpoint_get_request<'r>(&'r mut self, req: Request, buf: &'r mut [u8]) -> Option<InResponse<'r>> {
686 let control_selector = (req.value >> 8) as u8;
687 let endpoint_address = req.index as u8;
688
689 if endpoint_address != self.streaming_endpoint_address {
690 debug!("Unhandled endpoint get request for endpoint {}.", endpoint_address);
691 return None;
692 }
693
694 if control_selector != SAMPLING_FREQ_CONTROL as u8 {
695 debug!(
696 "Unsupported endpoint get request for control selector {}.",
697 control_selector
698 );
699 return Some(InResponse::Rejected);
700 }
701
702 let sample_rate_hz = self.shared.sample_rate_hz.load(Ordering::Relaxed);
703
704 buf[0] = (sample_rate_hz & 0xFF) as u8;
705 buf[1] = ((sample_rate_hz >> 8) & 0xFF) as u8;
706 buf[2] = ((sample_rate_hz >> 16) & 0xFF) as u8;
707
708 Some(InResponse::Accepted(&buf[..3]))
709 }
710}
711
712impl<'d> Handler for Control<'d> {
713 /// Called when the USB device has been enabled or disabled.
714 fn enabled(&mut self, enabled: bool) {
715 debug!("USB device enabled: {}", enabled);
716 }
717
718 /// Called when the host has set the address of the device to `addr`.
719 fn addressed(&mut self, addr: u8) {
720 debug!("Host set address to: {}", addr);
721 }
722
723 /// Called when the host has enabled or disabled the configuration of the device.
724 fn configured(&mut self, configured: bool) {
725 debug!("USB device configured: {}", configured);
726 }
727
728 /// Called when remote wakeup feature is enabled or disabled.
729 fn remote_wakeup_enabled(&mut self, enabled: bool) {
730 debug!("USB remote wakeup enabled: {}", enabled);
731 }
732
733 /// Called when a "set alternate setting" control request is done on the interface.
734 fn set_alternate_setting(&mut self, iface: InterfaceNumber, alternate_setting: u8) {
735 debug!(
736 "USB set interface number {} to alt setting {}.",
737 iface, alternate_setting
738 );
739 }
740
741 /// Called after a USB reset after the bus reset sequence is complete.
742 fn reset(&mut self) {
743 let shared = self.shared;
744 shared.audio_settings.lock(|x| x.set(AudioSettings::default()));
745
746 shared.changed.store(true, Ordering::Relaxed);
747 shared.waker.borrow_mut().wake();
748 }
749
750 /// Called when the bus has entered or exited the suspend state.
751 fn suspended(&mut self, suspended: bool) {
752 debug!("USB device suspended: {}", suspended);
753 }
754
755 // Handle control set requests.
756 fn control_out(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> {
757 match req.request_type {
758 RequestType::Class => match req.recipient {
759 Recipient::Interface => self.interface_set_request(req, data),
760 Recipient::Endpoint => self.endpoint_set_request(req, data),
761 _ => Some(OutResponse::Rejected),
762 },
763 _ => None,
764 }
765 }
766
767 // Handle control get requests.
768 fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {
769 match req.request_type {
770 RequestType::Class => match req.recipient {
771 Recipient::Interface => self.interface_get_request(req, buf),
772 Recipient::Endpoint => self.endpoint_get_request(req, buf),
773 _ => None,
774 },
775 _ => None,
776 }
777 }
778}
diff --git a/embassy-usb/src/class/uac1/terminal_type.rs b/embassy-usb/src/class/uac1/terminal_type.rs
new file mode 100644
index 000000000..65474a714
--- /dev/null
+++ b/embassy-usb/src/class/uac1/terminal_type.rs
@@ -0,0 +1,50 @@
1//! USB Audio Terminal Types from Universal Serial Bus Device Class Definition
2//! for Terminal Types, Release 1.0
3
4/// USB Audio Terminal Types from "Universal Serial Bus Device Class Definition
5/// for Terminal Types, Release 1.0"
6#[repr(u16)]
7#[non_exhaustive]
8#[derive(Copy, Clone, Eq, PartialEq, Debug)]
9#[allow(missing_docs)]
10pub enum TerminalType {
11 // USB Terminal Types
12 UsbUndefined = 0x0100,
13 UsbStreaming = 0x0101,
14 UsbVendor = 0x01ff,
15
16 // Input Terminal Types
17 InUndefined = 0x0200,
18 InMicrophone = 0x0201,
19 InDesktopMicrophone = 0x0202,
20 InPersonalMicrophone = 0x0203,
21 InOmniDirectionalMicrophone = 0x0204,
22 InMicrophoneArray = 0x0205,
23 InProcessingMicrophoneArray = 0x0206,
24
25 // Output Terminal Types
26 OutUndefined = 0x0300,
27 OutSpeaker = 0x0301,
28 OutHeadphones = 0x0302,
29 OutHeadMountedDisplayAudio = 0x0303,
30 OutDesktopSpeaker = 0x0304,
31 OutRoomSpeaker = 0x0305,
32 OutCommunicationSpeaker = 0x0306,
33 OutLowFrequencyEffectsSpeaker = 0x0307,
34
35 // External Terminal Types
36 ExtUndefined = 0x0600,
37 ExtAnalogConnector = 0x0601,
38 ExtDigitalAudioInterface = 0x0602,
39 ExtLineConnector = 0x0603,
40 ExtLegacyAudioConnector = 0x0604,
41 ExtSpdifConnector = 0x0605,
42 Ext1394DaStream = 0x0606,
43 Ext1394DvStreamSoundtrack = 0x0607,
44}
45
46impl From<TerminalType> for u16 {
47 fn from(t: TerminalType) -> u16 {
48 t as u16
49 }
50}
diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml
index 75e315e82..435b0b43c 100644
--- a/examples/stm32f4/Cargo.toml
+++ b/examples/stm32f4/Cargo.toml
@@ -27,6 +27,7 @@ embedded-io-async = { version = "0.6.1" }
27panic-probe = { version = "0.3", features = ["print-defmt"] } 27panic-probe = { version = "0.3", features = ["print-defmt"] }
28futures-util = { version = "0.3.30", default-features = false } 28futures-util = { version = "0.3.30", default-features = false }
29heapless = { version = "0.8", default-features = false } 29heapless = { version = "0.8", default-features = false }
30critical-section = "1.1"
30nb = "1.0.0" 31nb = "1.0.0"
31embedded-storage = "0.3.1" 32embedded-storage = "0.3.1"
32micromath = "2.0.0" 33micromath = "2.0.0"
diff --git a/examples/stm32f4/src/bin/usb_uac_speaker.rs b/examples/stm32f4/src/bin/usb_uac_speaker.rs
new file mode 100644
index 000000000..8d83afd1a
--- /dev/null
+++ b/examples/stm32f4/src/bin/usb_uac_speaker.rs
@@ -0,0 +1,387 @@
1#![no_std]
2#![no_main]
3
4use core::cell::RefCell;
5
6use defmt::{panic, *};
7use embassy_executor::Spawner;
8use embassy_stm32::time::Hertz;
9use embassy_stm32::{bind_interrupts, interrupt, peripherals, timer, usb, Config};
10use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};
11use embassy_sync::blocking_mutex::Mutex;
12use embassy_sync::signal::Signal;
13use embassy_sync::zerocopy_channel;
14use embassy_usb::class::uac1;
15use embassy_usb::class::uac1::speaker::{self, Speaker};
16use embassy_usb::driver::EndpointError;
17use heapless::Vec;
18use micromath::F32Ext;
19use static_cell::StaticCell;
20use {defmt_rtt as _, panic_probe as _};
21
22bind_interrupts!(struct Irqs {
23 OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>;
24});
25
26static TIMER: Mutex<CriticalSectionRawMutex, RefCell<Option<timer::low_level::Timer<peripherals::TIM2>>>> =
27 Mutex::new(RefCell::new(None));
28
29// A counter signal that is written by the feedback timer, once every `FEEDBACK_REFRESH_PERIOD`.
30// At that point, a feedback value is sent to the host.
31pub static FEEDBACK_SIGNAL: Signal<CriticalSectionRawMutex, u32> = Signal::new();
32
33// Stereo input
34pub const INPUT_CHANNEL_COUNT: usize = 2;
35
36// This example uses a fixed sample rate of 48 kHz.
37pub const SAMPLE_RATE_HZ: u32 = 48_000;
38pub const FEEDBACK_COUNTER_TICK_RATE: u32 = 42_000_000;
39
40// Use 32 bit samples, which allow for a lot of (software) volume adjustment without degradation of quality.
41pub const SAMPLE_WIDTH: uac1::SampleWidth = uac1::SampleWidth::Width4Byte;
42pub const SAMPLE_WIDTH_BIT: usize = SAMPLE_WIDTH.in_bit();
43pub const SAMPLE_SIZE: usize = SAMPLE_WIDTH as usize;
44pub const SAMPLE_SIZE_PER_S: usize = (SAMPLE_RATE_HZ as usize) * INPUT_CHANNEL_COUNT * SAMPLE_SIZE;
45
46// Size of audio samples per 1 ms - for the full-speed USB frame period of 1 ms.
47pub const USB_FRAME_SIZE: usize = SAMPLE_SIZE_PER_S.div_ceil(1000);
48
49// Select front left and right audio channels.
50pub const AUDIO_CHANNELS: [uac1::Channel; INPUT_CHANNEL_COUNT] = [uac1::Channel::LeftFront, uac1::Channel::RightFront];
51
52// Factor of two as a margin for feedback (this is an excessive amount)
53pub const USB_MAX_PACKET_SIZE: usize = 2 * USB_FRAME_SIZE;
54pub const USB_MAX_SAMPLE_COUNT: usize = USB_MAX_PACKET_SIZE / SAMPLE_SIZE;
55
56// The data type that is exchanged via the zero-copy channel (a sample vector).
57pub type SampleBlock = Vec<u32, USB_MAX_SAMPLE_COUNT>;
58
59// Feedback is provided in 10.14 format for full-speed endpoints.
60pub const FEEDBACK_REFRESH_PERIOD: uac1::FeedbackRefresh = uac1::FeedbackRefresh::Period8Frames;
61const FEEDBACK_SHIFT: usize = 14;
62
63const TICKS_PER_SAMPLE: f32 = (FEEDBACK_COUNTER_TICK_RATE as f32) / (SAMPLE_RATE_HZ as f32);
64
65struct Disconnected {}
66
67impl From<EndpointError> for Disconnected {
68 fn from(val: EndpointError) -> Self {
69 match val {
70 EndpointError::BufferOverflow => panic!("Buffer overflow"),
71 EndpointError::Disabled => Disconnected {},
72 }
73 }
74}
75
76/// Sends feedback messages to the host.
77async fn feedback_handler<'d, T: usb::Instance + 'd>(
78 feedback: &mut speaker::Feedback<'d, usb::Driver<'d, T>>,
79 feedback_factor: f32,
80) -> Result<(), Disconnected> {
81 let mut packet: Vec<u8, 4> = Vec::new();
82
83 // Collects the fractional component of the feedback value that is lost by rounding.
84 let mut rest = 0.0_f32;
85
86 loop {
87 let counter = FEEDBACK_SIGNAL.wait().await;
88
89 packet.clear();
90
91 let raw_value = counter as f32 * feedback_factor + rest;
92 let value = raw_value.round();
93 rest = raw_value - value;
94
95 let value = value as u32;
96 packet.push(value as u8).unwrap();
97 packet.push((value >> 8) as u8).unwrap();
98 packet.push((value >> 16) as u8).unwrap();
99
100 feedback.write_packet(&packet).await?;
101 }
102}
103
104/// Handles streaming of audio data from the host.
105async fn stream_handler<'d, T: usb::Instance + 'd>(
106 stream: &mut speaker::Stream<'d, usb::Driver<'d, T>>,
107 sender: &mut zerocopy_channel::Sender<'static, NoopRawMutex, SampleBlock>,
108) -> Result<(), Disconnected> {
109 loop {
110 let mut usb_data = [0u8; USB_MAX_PACKET_SIZE];
111 let data_size = stream.read_packet(&mut usb_data).await?;
112
113 let word_count = data_size / SAMPLE_SIZE;
114
115 if word_count * SAMPLE_SIZE == data_size {
116 // Obtain a buffer from the channel
117 let samples = sender.send().await;
118 samples.clear();
119
120 for w in 0..word_count {
121 let byte_offset = w * SAMPLE_SIZE;
122 let sample = u32::from_le_bytes(usb_data[byte_offset..byte_offset + SAMPLE_SIZE].try_into().unwrap());
123
124 // Fill the sample buffer with data.
125 samples.push(sample).unwrap();
126 }
127
128 sender.send_done();
129 } else {
130 debug!("Invalid USB buffer size of {}, skipped.", data_size);
131 }
132 }
133}
134
135/// Receives audio samples from the USB streaming task and can play them back.
136#[embassy_executor::task]
137async fn audio_receiver_task(mut usb_audio_receiver: zerocopy_channel::Receiver<'static, NoopRawMutex, SampleBlock>) {
138 loop {
139 let _samples = usb_audio_receiver.receive().await;
140 // Use the samples, for example play back via the SAI peripheral.
141
142 // Notify the channel that the buffer is now ready to be reused
143 usb_audio_receiver.receive_done();
144 }
145}
146
147/// Receives audio samples from the host.
148#[embassy_executor::task]
149async fn usb_streaming_task(
150 mut stream: speaker::Stream<'static, usb::Driver<'static, peripherals::USB_OTG_FS>>,
151 mut sender: zerocopy_channel::Sender<'static, NoopRawMutex, SampleBlock>,
152) {
153 loop {
154 stream.wait_connection().await;
155 _ = stream_handler(&mut stream, &mut sender).await;
156 }
157}
158
159/// Sends sample rate feedback to the host.
160///
161/// The `feedback_factor` scales the feedback timer's counter value so that the result is the number of samples that
162/// this device played back or "consumed" during one SOF period (1 ms) - in 10.14 format.
163///
164/// Ideally, the `feedback_factor` that is calculated below would be an integer for avoiding numerical errors.
165/// This is achieved by having `TICKS_PER_SAMPLE` be a power of two. For audio applications at a sample rate of 48 kHz,
166/// 24.576 MHz would be one such option.
167///
168/// A good choice for the STM32F4, which also has to generate a 48 MHz clock from its HSE (e.g. running at 8 MHz)
169/// for USB, is to clock the feedback timer from the MCLK output of the SAI peripheral. The SAI peripheral then uses an
170/// external clock. In that case, wiring the MCLK output to the timer clock input is required.
171///
172/// This simple example just uses the internal clocks for supplying the feedback timer,
173/// and does not even set up a SAI peripheral.
174#[embassy_executor::task]
175async fn usb_feedback_task(mut feedback: speaker::Feedback<'static, usb::Driver<'static, peripherals::USB_OTG_FS>>) {
176 let feedback_factor =
177 ((1 << FEEDBACK_SHIFT) as f32 / TICKS_PER_SAMPLE) / FEEDBACK_REFRESH_PERIOD.frame_count() as f32;
178
179 // Should be 2.3405714285714287...
180 info!("Using a feedback factor of {}.", feedback_factor);
181
182 loop {
183 feedback.wait_connection().await;
184 _ = feedback_handler(&mut feedback, feedback_factor).await;
185 }
186}
187
188#[embassy_executor::task]
189async fn usb_task(mut usb_device: embassy_usb::UsbDevice<'static, usb::Driver<'static, peripherals::USB_OTG_FS>>) {
190 usb_device.run().await;
191}
192
193/// Checks for changes on the control monitor of the class.
194///
195/// In this case, monitor changes of volume or mute state.
196#[embassy_executor::task]
197async fn usb_control_task(control_monitor: speaker::ControlMonitor<'static>) {
198 loop {
199 control_monitor.changed().await;
200
201 for channel in AUDIO_CHANNELS {
202 let volume = control_monitor.volume(channel).unwrap();
203 info!("Volume changed to {} on channel {}.", volume, channel);
204 }
205 }
206}
207
208/// Feedback value measurement and calculation
209///
210/// Used for measuring/calculating the number of samples that were received from the host during the
211/// `FEEDBACK_REFRESH_PERIOD`.
212///
213/// Configured in this example with
214/// - a refresh period of 8 ms, and
215/// - a tick rate of 42 MHz.
216///
217/// This gives an (ideal) counter value of 336.000 for every update of the `FEEDBACK_SIGNAL`.
218#[interrupt]
219fn TIM2() {
220 static mut LAST_TICKS: u32 = 0;
221 static mut FRAME_COUNT: usize = 0;
222
223 critical_section::with(|cs| {
224 // Read timer counter.
225 let timer = TIMER.borrow(cs).borrow().as_ref().unwrap().regs_gp32();
226
227 let status = timer.sr().read();
228
229 const CHANNEL_INDEX: usize = 0;
230 if status.ccif(CHANNEL_INDEX) {
231 let ticks = timer.ccr(CHANNEL_INDEX).read();
232
233 *FRAME_COUNT += 1;
234 if *FRAME_COUNT >= FEEDBACK_REFRESH_PERIOD.frame_count() {
235 *FRAME_COUNT = 0;
236 FEEDBACK_SIGNAL.signal(ticks.wrapping_sub(*LAST_TICKS));
237 *LAST_TICKS = ticks;
238 }
239 };
240
241 // Clear trigger interrupt flag.
242 timer.sr().modify(|r| r.set_tif(false));
243 });
244}
245
246// If you are trying this and your USB device doesn't connect, the most
247// common issues are the RCC config and vbus_detection
248//
249// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure
250// for more information.
251#[embassy_executor::main]
252async fn main(spawner: Spawner) {
253 info!("Hello World!");
254
255 let mut config = Config::default();
256 {
257 use embassy_stm32::rcc::*;
258 config.rcc.hse = Some(Hse {
259 freq: Hertz(8_000_000),
260 mode: HseMode::Bypass,
261 });
262 config.rcc.pll_src = PllSource::HSE;
263 config.rcc.pll = Some(Pll {
264 prediv: PllPreDiv::DIV4,
265 mul: PllMul::MUL168,
266 divp: Some(PllPDiv::DIV2), // ((8 MHz / 4) * 168) / 2 = 168 Mhz.
267 divq: Some(PllQDiv::DIV7), // ((8 MHz / 4) * 168) / 7 = 48 Mhz.
268 divr: None,
269 });
270 config.rcc.ahb_pre = AHBPrescaler::DIV1;
271 config.rcc.apb1_pre = APBPrescaler::DIV4;
272 config.rcc.apb2_pre = APBPrescaler::DIV2;
273 config.rcc.sys = Sysclk::PLL1_P;
274 config.rcc.mux.clk48sel = mux::Clk48sel::PLL1_Q;
275 }
276 let p = embassy_stm32::init(config);
277
278 // Configure all required buffers in a static way.
279 debug!("USB packet size is {} byte", USB_MAX_PACKET_SIZE);
280 static CONFIG_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new();
281 let config_descriptor = CONFIG_DESCRIPTOR.init([0; 256]);
282
283 static BOS_DESCRIPTOR: StaticCell<[u8; 32]> = StaticCell::new();
284 let bos_descriptor = BOS_DESCRIPTOR.init([0; 32]);
285
286 const CONTROL_BUF_SIZE: usize = 64;
287 static CONTROL_BUF: StaticCell<[u8; CONTROL_BUF_SIZE]> = StaticCell::new();
288 let control_buf = CONTROL_BUF.init([0; CONTROL_BUF_SIZE]);
289
290 const FEEDBACK_BUF_SIZE: usize = 4;
291 static EP_OUT_BUFFER: StaticCell<[u8; FEEDBACK_BUF_SIZE + CONTROL_BUF_SIZE + USB_MAX_PACKET_SIZE]> =
292 StaticCell::new();
293 let ep_out_buffer = EP_OUT_BUFFER.init([0u8; FEEDBACK_BUF_SIZE + CONTROL_BUF_SIZE + USB_MAX_PACKET_SIZE]);
294
295 static STATE: StaticCell<speaker::State> = StaticCell::new();
296 let state = STATE.init(speaker::State::new());
297
298 // Create the driver, from the HAL.
299 let mut usb_config = usb::Config::default();
300
301 // Do not enable vbus_detection. This is a safe default that works in all boards.
302 // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need
303 // to enable vbus_detection to comply with the USB spec. If you enable it, the board
304 // has to support it or USB won't work at all. See docs on `vbus_detection` for details.
305 usb_config.vbus_detection = false;
306
307 let usb_driver = usb::Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, ep_out_buffer, usb_config);
308
309 // Basic USB device configuration
310 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
311 config.manufacturer = Some("Embassy");
312 config.product = Some("USB-audio-speaker example");
313 config.serial_number = Some("12345678");
314
315 // Required for windows compatibility.
316 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
317 config.device_class = 0xEF;
318 config.device_sub_class = 0x02;
319 config.device_protocol = 0x01;
320 config.composite_with_iads = true;
321
322 let mut builder = embassy_usb::Builder::new(
323 usb_driver,
324 config,
325 config_descriptor,
326 bos_descriptor,
327 &mut [], // no msos descriptors
328 control_buf,
329 );
330
331 // Create the UAC1 Speaker class components
332 let (stream, feedback, control_monitor) = Speaker::new(
333 &mut builder,
334 state,
335 USB_MAX_PACKET_SIZE as u16,
336 uac1::SampleWidth::Width4Byte,
337 &[SAMPLE_RATE_HZ],
338 &AUDIO_CHANNELS,
339 FEEDBACK_REFRESH_PERIOD,
340 );
341
342 // Create the USB device
343 let usb_device = builder.build();
344
345 // Establish a zero-copy channel for transferring received audio samples between tasks
346 static SAMPLE_BLOCKS: StaticCell<[SampleBlock; 2]> = StaticCell::new();
347 let sample_blocks = SAMPLE_BLOCKS.init([Vec::new(), Vec::new()]);
348
349 static CHANNEL: StaticCell<zerocopy_channel::Channel<'_, NoopRawMutex, SampleBlock>> = StaticCell::new();
350 let channel = CHANNEL.init(zerocopy_channel::Channel::new(sample_blocks));
351 let (sender, receiver) = channel.split();
352
353 // Run a timer for counting between SOF interrupts.
354 let mut tim2 = timer::low_level::Timer::new(p.TIM2);
355 tim2.set_tick_freq(Hertz(FEEDBACK_COUNTER_TICK_RATE));
356 tim2.set_trigger_source(timer::low_level::TriggerSource::ITR1); // The USB SOF signal.
357
358 const TIMER_CHANNEL: timer::Channel = timer::Channel::Ch1;
359 tim2.set_input_ti_selection(TIMER_CHANNEL, timer::low_level::InputTISelection::TRC);
360 tim2.set_input_capture_prescaler(TIMER_CHANNEL, 0);
361 tim2.set_input_capture_filter(TIMER_CHANNEL, timer::low_level::FilterValue::FCK_INT_N2);
362
363 // Reset all interrupt flags.
364 tim2.regs_gp32().sr().write(|r| r.0 = 0);
365
366 // Enable routing of SOF to the timer.
367 tim2.regs_gp32().or().write(|r| *r = 0b10 << 10);
368
369 tim2.enable_channel(TIMER_CHANNEL, true);
370 tim2.enable_input_interrupt(TIMER_CHANNEL, true);
371
372 tim2.start();
373
374 TIMER.lock(|p| p.borrow_mut().replace(tim2));
375
376 // Unmask the TIM2 interrupt.
377 unsafe {
378 cortex_m::peripheral::NVIC::unmask(interrupt::TIM2);
379 }
380
381 // Launch USB audio tasks.
382 unwrap!(spawner.spawn(usb_control_task(control_monitor)));
383 unwrap!(spawner.spawn(usb_streaming_task(stream, sender)));
384 unwrap!(spawner.spawn(usb_feedback_task(feedback)));
385 unwrap!(spawner.spawn(usb_task(usb_device)));
386 unwrap!(spawner.spawn(audio_receiver_task(receiver)));
387}
diff --git a/examples/stm32h5/src/bin/usb_uac_speaker.rs b/examples/stm32h5/src/bin/usb_uac_speaker.rs
new file mode 100644
index 000000000..4fd4ccbbd
--- /dev/null
+++ b/examples/stm32h5/src/bin/usb_uac_speaker.rs
@@ -0,0 +1,378 @@
1#![no_std]
2#![no_main]
3
4use core::cell::RefCell;
5
6use defmt::{panic, *};
7use embassy_executor::Spawner;
8use embassy_stm32::time::Hertz;
9use embassy_stm32::{bind_interrupts, interrupt, peripherals, timer, usb, Config};
10use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};
11use embassy_sync::blocking_mutex::Mutex;
12use embassy_sync::signal::Signal;
13use embassy_sync::zerocopy_channel;
14use embassy_usb::class::uac1;
15use embassy_usb::class::uac1::speaker::{self, Speaker};
16use embassy_usb::driver::EndpointError;
17use heapless::Vec;
18use micromath::F32Ext;
19use static_cell::StaticCell;
20use {defmt_rtt as _, panic_probe as _};
21
22bind_interrupts!(struct Irqs {
23 USB_DRD_FS => usb::InterruptHandler<peripherals::USB>;
24});
25
26static TIMER: Mutex<CriticalSectionRawMutex, RefCell<Option<timer::low_level::Timer<peripherals::TIM5>>>> =
27 Mutex::new(RefCell::new(None));
28
29// A counter signal that is written by the feedback timer, once every `FEEDBACK_REFRESH_PERIOD`.
30// At that point, a feedback value is sent to the host.
31pub static FEEDBACK_SIGNAL: Signal<CriticalSectionRawMutex, u32> = Signal::new();
32
33// Stereo input
34pub const INPUT_CHANNEL_COUNT: usize = 2;
35
36// This example uses a fixed sample rate of 48 kHz.
37pub const SAMPLE_RATE_HZ: u32 = 48_000;
38pub const FEEDBACK_COUNTER_TICK_RATE: u32 = 31_250_000;
39
40// Use 32 bit samples, which allow for a lot of (software) volume adjustment without degradation of quality.
41pub const SAMPLE_WIDTH: uac1::SampleWidth = uac1::SampleWidth::Width4Byte;
42pub const SAMPLE_WIDTH_BIT: usize = SAMPLE_WIDTH.in_bit();
43pub const SAMPLE_SIZE: usize = SAMPLE_WIDTH as usize;
44pub const SAMPLE_SIZE_PER_S: usize = (SAMPLE_RATE_HZ as usize) * INPUT_CHANNEL_COUNT * SAMPLE_SIZE;
45
46// Size of audio samples per 1 ms - for the full-speed USB frame period of 1 ms.
47pub const USB_FRAME_SIZE: usize = SAMPLE_SIZE_PER_S.div_ceil(1000);
48
49// Select front left and right audio channels.
50pub const AUDIO_CHANNELS: [uac1::Channel; INPUT_CHANNEL_COUNT] = [uac1::Channel::LeftFront, uac1::Channel::RightFront];
51
52// Factor of two as a margin for feedback (this is an excessive amount)
53pub const USB_MAX_PACKET_SIZE: usize = 2 * USB_FRAME_SIZE;
54pub const USB_MAX_SAMPLE_COUNT: usize = USB_MAX_PACKET_SIZE / SAMPLE_SIZE;
55
56// The data type that is exchanged via the zero-copy channel (a sample vector).
57pub type SampleBlock = Vec<u32, USB_MAX_SAMPLE_COUNT>;
58
59// Feedback is provided in 10.14 format for full-speed endpoints.
60pub const FEEDBACK_REFRESH_PERIOD: uac1::FeedbackRefresh = uac1::FeedbackRefresh::Period8Frames;
61const FEEDBACK_SHIFT: usize = 14;
62
63const TICKS_PER_SAMPLE: f32 = (FEEDBACK_COUNTER_TICK_RATE as f32) / (SAMPLE_RATE_HZ as f32);
64
65struct Disconnected {}
66
67impl From<EndpointError> for Disconnected {
68 fn from(val: EndpointError) -> Self {
69 match val {
70 EndpointError::BufferOverflow => panic!("Buffer overflow"),
71 EndpointError::Disabled => Disconnected {},
72 }
73 }
74}
75
76/// Sends feedback messages to the host.
77async fn feedback_handler<'d, T: usb::Instance + 'd>(
78 feedback: &mut speaker::Feedback<'d, usb::Driver<'d, T>>,
79 feedback_factor: f32,
80) -> Result<(), Disconnected> {
81 let mut packet: Vec<u8, 4> = Vec::new();
82
83 // Collects the fractional component of the feedback value that is lost by rounding.
84 let mut rest = 0.0_f32;
85
86 loop {
87 let counter = FEEDBACK_SIGNAL.wait().await;
88
89 packet.clear();
90
91 let raw_value = counter as f32 * feedback_factor + rest;
92 let value = raw_value.round();
93 rest = raw_value - value;
94
95 let value = value as u32;
96
97 debug!("Feedback value: {}", value);
98
99 packet.push(value as u8).unwrap();
100 packet.push((value >> 8) as u8).unwrap();
101 packet.push((value >> 16) as u8).unwrap();
102
103 feedback.write_packet(&packet).await?;
104 }
105}
106
107/// Handles streaming of audio data from the host.
108async fn stream_handler<'d, T: usb::Instance + 'd>(
109 stream: &mut speaker::Stream<'d, usb::Driver<'d, T>>,
110 sender: &mut zerocopy_channel::Sender<'static, NoopRawMutex, SampleBlock>,
111) -> Result<(), Disconnected> {
112 loop {
113 let mut usb_data = [0u8; USB_MAX_PACKET_SIZE];
114 let data_size = stream.read_packet(&mut usb_data).await?;
115
116 let word_count = data_size / SAMPLE_SIZE;
117
118 if word_count * SAMPLE_SIZE == data_size {
119 // Obtain a buffer from the channel
120 let samples = sender.send().await;
121 samples.clear();
122
123 for w in 0..word_count {
124 let byte_offset = w * SAMPLE_SIZE;
125 let sample = u32::from_le_bytes(usb_data[byte_offset..byte_offset + SAMPLE_SIZE].try_into().unwrap());
126
127 // Fill the sample buffer with data.
128 samples.push(sample).unwrap();
129 }
130
131 sender.send_done();
132 } else {
133 debug!("Invalid USB buffer size of {}, skipped.", data_size);
134 }
135 }
136}
137
138/// Receives audio samples from the USB streaming task and can play them back.
139#[embassy_executor::task]
140async fn audio_receiver_task(mut usb_audio_receiver: zerocopy_channel::Receiver<'static, NoopRawMutex, SampleBlock>) {
141 loop {
142 let _samples = usb_audio_receiver.receive().await;
143 // Use the samples, for example play back via the SAI peripheral.
144
145 // Notify the channel that the buffer is now ready to be reused
146 usb_audio_receiver.receive_done();
147 }
148}
149
150/// Receives audio samples from the host.
151#[embassy_executor::task]
152async fn usb_streaming_task(
153 mut stream: speaker::Stream<'static, usb::Driver<'static, peripherals::USB>>,
154 mut sender: zerocopy_channel::Sender<'static, NoopRawMutex, SampleBlock>,
155) {
156 loop {
157 stream.wait_connection().await;
158 info!("USB connected.");
159 _ = stream_handler(&mut stream, &mut sender).await;
160 info!("USB disconnected.");
161 }
162}
163
164/// Sends sample rate feedback to the host.
165///
166/// The `feedback_factor` scales the feedback timer's counter value so that the result is the number of samples that
167/// this device played back or "consumed" during one SOF period (1 ms) - in 10.14 format.
168///
169/// Ideally, the `feedback_factor` that is calculated below would be an integer for avoiding numerical errors.
170/// This is achieved by having `TICKS_PER_SAMPLE` be a power of two. For audio applications at a sample rate of 48 kHz,
171/// 24.576 MHz would be one such option.
172#[embassy_executor::task]
173async fn usb_feedback_task(mut feedback: speaker::Feedback<'static, usb::Driver<'static, peripherals::USB>>) {
174 let feedback_factor =
175 ((1 << FEEDBACK_SHIFT) as f32 / TICKS_PER_SAMPLE) / FEEDBACK_REFRESH_PERIOD.frame_count() as f32;
176
177 loop {
178 feedback.wait_connection().await;
179 _ = feedback_handler(&mut feedback, feedback_factor).await;
180 }
181}
182
183#[embassy_executor::task]
184async fn usb_task(mut usb_device: embassy_usb::UsbDevice<'static, usb::Driver<'static, peripherals::USB>>) {
185 usb_device.run().await;
186}
187
188/// Checks for changes on the control monitor of the class.
189///
190/// In this case, monitor changes of volume or mute state.
191#[embassy_executor::task]
192async fn usb_control_task(control_monitor: speaker::ControlMonitor<'static>) {
193 loop {
194 control_monitor.changed().await;
195
196 for channel in AUDIO_CHANNELS {
197 let volume = control_monitor.volume(channel).unwrap();
198 info!("Volume changed to {} on channel {}.", volume, channel);
199 }
200 }
201}
202
203/// Feedback value measurement and calculation
204///
205/// Used for measuring/calculating the number of samples that were received from the host during the
206/// `FEEDBACK_REFRESH_PERIOD`.
207///
208/// Configured in this example with
209/// - a refresh period of 8 ms, and
210/// - a tick rate of 42 MHz.
211///
212/// This gives an (ideal) counter value of 336.000 for every update of the `FEEDBACK_SIGNAL`.
213#[interrupt]
214fn TIM5() {
215 static mut LAST_TICKS: u32 = 0;
216 static mut FRAME_COUNT: usize = 0;
217
218 critical_section::with(|cs| {
219 // Read timer counter.
220 let timer = TIMER.borrow(cs).borrow().as_ref().unwrap().regs_gp32();
221
222 let status = timer.sr().read();
223
224 const CHANNEL_INDEX: usize = 0;
225 if status.ccif(CHANNEL_INDEX) {
226 let ticks = timer.ccr(CHANNEL_INDEX).read();
227
228 *FRAME_COUNT += 1;
229 if *FRAME_COUNT >= FEEDBACK_REFRESH_PERIOD.frame_count() {
230 *FRAME_COUNT = 0;
231 FEEDBACK_SIGNAL.signal(ticks.wrapping_sub(*LAST_TICKS));
232 *LAST_TICKS = ticks;
233 }
234 };
235
236 // Clear trigger interrupt flag.
237 timer.sr().modify(|r| r.set_tif(false));
238 });
239}
240
241// If you are trying this and your USB device doesn't connect, the most
242// common issues are the RCC config and vbus_detection
243//
244// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure
245// for more information.
246#[embassy_executor::main]
247async fn main(spawner: Spawner) {
248 let mut config = Config::default();
249 {
250 use embassy_stm32::rcc::*;
251 config.rcc.hsi = None;
252 config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB
253 config.rcc.hse = Some(Hse {
254 freq: Hertz(8_000_000),
255 mode: HseMode::BypassDigital,
256 });
257 config.rcc.pll1 = Some(Pll {
258 source: PllSource::HSE,
259 prediv: PllPreDiv::DIV2,
260 mul: PllMul::MUL125,
261 divp: Some(PllDiv::DIV2), // 250 Mhz
262 divq: None,
263 divr: None,
264 });
265 config.rcc.pll2 = Some(Pll {
266 source: PllSource::HSE,
267 prediv: PllPreDiv::DIV4,
268 mul: PllMul::MUL123,
269 divp: Some(PllDiv::DIV20), // 12.3 Mhz, close to 12.288 MHz for 48 kHz audio
270 divq: None,
271 divr: None,
272 });
273 config.rcc.ahb_pre = AHBPrescaler::DIV2;
274 config.rcc.apb1_pre = APBPrescaler::DIV4;
275 config.rcc.apb2_pre = APBPrescaler::DIV2;
276 config.rcc.apb3_pre = APBPrescaler::DIV4;
277 config.rcc.sys = Sysclk::PLL1_P;
278 config.rcc.voltage_scale = VoltageScale::Scale0;
279 config.rcc.mux.usbsel = mux::Usbsel::HSI48;
280 config.rcc.mux.sai2sel = mux::Saisel::PLL2_P;
281 }
282 let p = embassy_stm32::init(config);
283
284 info!("Hello World!");
285
286 // Configure all required buffers in a static way.
287 debug!("USB packet size is {} byte", USB_MAX_PACKET_SIZE);
288 static CONFIG_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new();
289 let config_descriptor = CONFIG_DESCRIPTOR.init([0; 256]);
290
291 static BOS_DESCRIPTOR: StaticCell<[u8; 32]> = StaticCell::new();
292 let bos_descriptor = BOS_DESCRIPTOR.init([0; 32]);
293
294 const CONTROL_BUF_SIZE: usize = 64;
295 static CONTROL_BUF: StaticCell<[u8; CONTROL_BUF_SIZE]> = StaticCell::new();
296 let control_buf = CONTROL_BUF.init([0; CONTROL_BUF_SIZE]);
297
298 static STATE: StaticCell<speaker::State> = StaticCell::new();
299 let state = STATE.init(speaker::State::new());
300
301 let usb_driver = usb::Driver::new(p.USB, Irqs, p.PA12, p.PA11);
302
303 // Basic USB device configuration
304 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
305 config.manufacturer = Some("Embassy");
306 config.product = Some("USB-audio-speaker example");
307 config.serial_number = Some("12345678");
308
309 // Required for windows compatibility.
310 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
311 config.device_class = 0xEF;
312 config.device_sub_class = 0x02;
313 config.device_protocol = 0x01;
314 config.composite_with_iads = true;
315
316 let mut builder = embassy_usb::Builder::new(
317 usb_driver,
318 config,
319 config_descriptor,
320 bos_descriptor,
321 &mut [], // no msos descriptors
322 control_buf,
323 );
324
325 // Create the UAC1 Speaker class components
326 let (stream, feedback, control_monitor) = Speaker::new(
327 &mut builder,
328 state,
329 USB_MAX_PACKET_SIZE as u16,
330 uac1::SampleWidth::Width4Byte,
331 &[SAMPLE_RATE_HZ],
332 &AUDIO_CHANNELS,
333 FEEDBACK_REFRESH_PERIOD,
334 );
335
336 // Create the USB device
337 let usb_device = builder.build();
338
339 // Establish a zero-copy channel for transferring received audio samples between tasks
340 static SAMPLE_BLOCKS: StaticCell<[SampleBlock; 2]> = StaticCell::new();
341 let sample_blocks = SAMPLE_BLOCKS.init([Vec::new(), Vec::new()]);
342
343 static CHANNEL: StaticCell<zerocopy_channel::Channel<'_, NoopRawMutex, SampleBlock>> = StaticCell::new();
344 let channel = CHANNEL.init(zerocopy_channel::Channel::new(sample_blocks));
345 let (sender, receiver) = channel.split();
346
347 // Run a timer for counting between SOF interrupts.
348 let mut tim5 = timer::low_level::Timer::new(p.TIM5);
349 tim5.set_tick_freq(Hertz(FEEDBACK_COUNTER_TICK_RATE));
350 tim5.set_trigger_source(timer::low_level::TriggerSource::ITR12); // The USB SOF signal.
351
352 const TIMER_CHANNEL: timer::Channel = timer::Channel::Ch1;
353 tim5.set_input_ti_selection(TIMER_CHANNEL, timer::low_level::InputTISelection::TRC);
354 tim5.set_input_capture_prescaler(TIMER_CHANNEL, 0);
355 tim5.set_input_capture_filter(TIMER_CHANNEL, timer::low_level::FilterValue::FCK_INT_N2);
356
357 // Reset all interrupt flags.
358 tim5.regs_gp32().sr().write(|r| r.0 = 0);
359
360 tim5.enable_channel(TIMER_CHANNEL, true);
361 tim5.enable_input_interrupt(TIMER_CHANNEL, true);
362
363 tim5.start();
364
365 TIMER.lock(|p| p.borrow_mut().replace(tim5));
366
367 // Unmask the TIM5 interrupt.
368 unsafe {
369 cortex_m::peripheral::NVIC::unmask(interrupt::TIM5);
370 }
371
372 // Launch USB audio tasks.
373 unwrap!(spawner.spawn(usb_control_task(control_monitor)));
374 unwrap!(spawner.spawn(usb_streaming_task(stream, sender)));
375 unwrap!(spawner.spawn(usb_feedback_task(feedback)));
376 unwrap!(spawner.spawn(usb_task(usb_device)));
377 unwrap!(spawner.spawn(audio_receiver_task(receiver)));
378}