esp_idf_hal/rmt/sync_manager.rs
1use core::ptr;
2
3use esp_idf_sys::*;
4
5use crate::rmt::{RmtChannel, TxChannelDriver};
6
7/// In some real-time control applications (e.g., to make two robotic arms move simultaneously),
8/// you do not want any time drift between different channels. The RMT driver can help to manage
9/// this by creating a so-called Sync Manager.
10///
11/// The procedure of RMT sync transmission is shown [here](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/rmt.html#multiple-channels-simultaneous-transmission).
12pub struct SyncManager<'d, const N: usize> {
13 handle: rmt_sync_manager_handle_t,
14 // It is possible to convert a SyncManager back into its channels,
15 // which would require taking ownership of them.
16 //
17 // Because it implements Drop, rust forbids moving fields out of the struct,
18 // to workaround this, the channels are wrapped in an Option that always
19 // contains a value, except when dropped where it might be None.
20 channels: Option<[TxChannelDriver<'d>; N]>,
21}
22
23impl<'d, const N: usize> SyncManager<'d, N> {
24 /// Create a synchronization manager for multiple TX channels,
25 /// so that the managed channel can start transmitting at the same time.
26 ///
27 /// # Note
28 ///
29 /// All the channels managed by the sync manager should be enabled before creating
30 /// the sync manager.
31 ///
32 /// # Errors
33 ///
34 /// - `ESP_ERR_INVALID_ARG`: Create sync manager failed because of invalid argument
35 /// - `ESP_ERR_NOT_SUPPORTED`: Create sync manager failed because it is not supported by hardware
36 /// - `ESP_ERR_INVALID_STATE`: Create sync manager failed because not all channels are enabled
37 /// - `ESP_ERR_NO_MEM`: Create sync manager failed because out of memory
38 /// - `ESP_ERR_NOT_FOUND`: Create sync manager failed because all sync controllers are used up and no more free one
39 /// - `ESP_FAIL`: Create sync manager failed because of other error
40 pub fn new(channels: [TxChannelDriver<'d>; N]) -> Result<Self, EspError> {
41 let mut this = Self {
42 handle: ptr::null_mut(),
43 channels: Some(channels),
44 };
45
46 // SAFETY:
47 // rmt_new_sync_manager copies the array of channel handles, therefore it is safe to reference a
48 // temporary variable here.
49 let mut iter = this.channels_mut().iter().map(TxChannelDriver::handle);
50 let handles = [(); N].map(|_| iter.next().unwrap());
51
52 let sys_config = rmt_sync_manager_config_t {
53 tx_channel_array: handles.as_ptr(),
54 array_size: handles.len(),
55 };
56
57 esp!(unsafe { rmt_new_sync_manager(&sys_config, &mut this.handle) })?;
58
59 Ok(this)
60 }
61
62 /// Reset synchronization manager.
63 ///
64 /// # Errors
65 ///
66 /// - `ESP_ERR_INVALID_ARG`: Reset the synchronization manager failed because of invalid argument
67 /// - `ESP_FAIL`: Reset the synchronization manager failed because of other error
68 pub fn reset(&mut self) -> Result<(), EspError> {
69 esp!(unsafe { rmt_sync_reset(self.handle) })
70 }
71
72 /// Returns a mutable reference to the managed TX channels.
73 pub fn channels_mut(&mut self) -> &mut [TxChannelDriver<'d>; N] {
74 // SAFETY: Channels are always Some except when dropped.
75 unsafe { self.channels.as_mut().unwrap_unchecked() }
76 }
77
78 /// Returns a reference to the managed TX channels.
79 pub fn channels(&self) -> &[TxChannelDriver<'d>; N] {
80 // SAFETY: Channels are always Some except when dropped.
81 unsafe { self.channels.as_ref().unwrap_unchecked() }
82 }
83
84 /// Consumes the sync manager and returns the managed TX channels.
85 pub fn into_channels(mut self) -> [TxChannelDriver<'d>; N] {
86 unsafe { self.channels.take().unwrap_unchecked() }
87 }
88}
89
90impl<'d, const N: usize> Drop for SyncManager<'d, N> {
91 fn drop(&mut self) {
92 unsafe { rmt_del_sync_manager(self.handle) };
93 }
94}