Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 1974 add sai driver #1975

Merged
merged 12 commits into from
Sep 30, 2023

Conversation

tyler-gilbert
Copy link
Contributor

@tyler-gilbert tyler-gilbert commented Sep 30, 2023

This adds an SAI transmitter driver. The receiver is straightforward to add. It just requires some new_ functions and setting up the IO appropriately.

This is working on the STM32F429.

Closes #1974.

@tyler-gilbert
Copy link
Contributor Author

tyler-gilbert commented Sep 30, 2023

@Dirbaio It isn't clear to me what is causing ci/build to fail. Any ideas? It was the white space formatting.

@xoviat xoviat added this pull request to the merge queue Sep 30, 2023
Merged via the queue into embassy-rs:main with commit 7bc57ca Sep 30, 2023
@phoracek
Copy link

phoracek commented May 5, 2024

Hello @tyler-gilbert, do you by any chance have any public examples of this being used? I would like to give it a go in my project, and having a reference would help me a lot. Thanks

@tyler-gilbert
Copy link
Contributor Author

Hello @tyler-gilbert, do you by any chance have any public examples of this being used? I would like to give it a go in my project, and having a reference would help me a lot. Thanks

I have a private project I am using:

use embassy_stm32::sai::{Config, FifoThreshold, MasterClockDivider, StereoMono};

        info!("Creating SAI");

        let (tx_peripherals, tx_buffer, rx_peripherals, rx_buffer) = {
            let s = singleton::get_mut();
            (
                s.peripherals.sai_transmitter.take().unwrap(),
                s.sai_dma_tx_buffer.as_mut_slice(),
                s.peripherals.sai_receiver.take().unwrap(),
                s.sai_dma_rx_buffer.as_mut_slice(),
            )
        };

        #[cfg(feature = "stm32u585ai")]
        let (sub_block_transmitter, sub_block_receiver) = sai::split_subblocks(tx_peripherals.sai);

        #[cfg(not(feature = "stm32u585ai"))]
        let (sub_block_receiver, sub_block_transmitter) = sai::split_subblocks(tx_peripherals.sai);

        //The NAU8315 has some odd bring up requirements. The EN may need to be toggled after cycling the power.
        //The EN or power may also need to be cycles after the SCLK/FS signals are available.

        let (tx_config, transmitter) = {
            let tx_config = {
                let mut config = Config::default();
                config.mode = Mode::Master;
                config.tx_rx = TxRx::Transmitter;
                config.mute_detection_counter = embassy_stm32::dma::word::U5(0);
                config.master_clock_divider = MasterClockDivider::Div12;
                config.fifo_threshold = FifoThreshold::Empty;
                config.sync_output = true;
                config.stereo_mono = StereoMono::Mono;

                // NAU8315 datasheet claims min bitrate clock with 8KHz FS clock is 2048KHz
                // On the STM32U545, this works with and without the following lines of code
                // On the STM32F429, without the following lines of code, there are a lot of pops and snaps
                config.frame_length = sai::MAX_FRAME_LENGTH;
                config.frame_sync_active_level_length = embassy_stm32::dma::word::U7(128);

                config
            };

            (
                tx_config,
                sai::Sai::new_asynchronous(
                    sub_block_transmitter,
                    tx_peripherals.sck,
                    tx_peripherals.sd,
                    tx_peripherals.fs,
                    tx_peripherals.dma,
                    tx_buffer,
                    tx_config,
                ),
            )
        };

        let receiver = {
            let rx_config = {
                let mut config = tx_config.clone();
                config.tx_rx = TxRx::Receiver;

                // This doesn't work even if the transmitter is running. It may need to use the same FS and BCLK
                // Getting the mic/speaker sync'd is important for echo cancellation
                //config.mode = Mode::Slave;
                //config.sync_input = SyncInput::Internal;
                config.data_size = DataSize::Data32;
                config.frame_length = 64;
                config.frame_sync_active_level_length = embassy_stm32::dma::word::U7(32);
                config
            };

            sai::Sai::new_asynchronous(
                sub_block_receiver,
                rx_peripherals.sck,
                rx_peripherals.sd,
                rx_peripherals.fs,
                rx_peripherals.dma,
                rx_buffer,
                rx_config,
            )
        };

        // When syncing the mic to the speaker, speaker needs to be started for data to flow.
        // But it still isn't working with separate FS and BCLK.
        //transmitter.start();

        Self {
            codec_mutex: Mutex::new(codec::AudioCodec::new(AudioCodecConfig::None)),
            input: RefCell::new(input::Input::new(receiver)),
            output: RefCell::new(output::Output::new(transmitter)),
            window: RemoveNoise::new_window(),
            mic_channel: MicChannel::new(),
            speaker_channel: SpeakerChannel::new(),
            speaker_to_mic_channel: SpeakerToMicChannel::new(),
            is_cancel_echo: RefCell::new(false),
        }

Some snippets on reading and writing:

    pub async fn write(&self, samples: &[audio::Sample]) -> Result<(), sai::Error> {
        let mut io = self.io.borrow_mut();
        io.transmitter.write(Self::samples_as_slice(samples)).await
    }

        let result = io.receiver.read(read_frame.as_mut_slice()).await;

I haven't committed the changes that are needed to use it with GPDMA (STM32U5 and other new chips) and a circular buffer. But I have that working locally.

@phoracek
Copy link

phoracek commented May 6, 2024

@tyler-gilbert thanks a lot, that helps

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add STM32 SAI Driver
3 participants