Skip to main content

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}