Add SAI/I2S audio support with Audio PLL and SGTL5000 examples#183
Add SAI/I2S audio support with Audio PLL and SGTL5000 examples#183tacertain wants to merge 1 commit intomciantyre:masterfrom
Conversation
Enable SAI1-3 clock gates, configure Audio PLL (PLL4) for 44.1 kHz sample rates, expose SAI peripherals in the BSP, and add two working audio examples for the Teensy 4.1 + Audio Shield Rev D (SGTL5000): - rtic_sai_poll_tone: interrupt-driven FIFO polling (no DMA) - rtic_sai_dma_tone: DMA-driven I2S transfers Also updates dependencies to imxrt-hal v0.6 / imxrt-ral v0.6 and fixes LPSPI PCS0 removal and usb-device v0.3 API changes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Here's what I did to enable the audio shield. Obviously, this isn't mergeable with the current Cargo.toml - just putting it out there to let you know the path I'm on |
| // ── Static DMA buffer ──────────────────────────────────────────── | ||
| // Place in OCRAM (.uninit region) which is non-cached and | ||
| // DMA-accessible. Using .uninit means the section is NOLOAD so it | ||
| // won't bloat the flash image / hex file. The buffer is filled | ||
| // before every DMA transfer, so uninitialised contents are fine. | ||
| #[link_section = ".uninit.dmabuffers"] | ||
| static mut DMA_BUF: core::mem::MaybeUninit<[u32; DMA_BUF_LEN]> = | ||
| core::mem::MaybeUninit::uninit(); | ||
|
|
||
| // ── Init ───────────────────────────────────────────────────────── | ||
|
|
||
| #[init] |
There was a problem hiding this comment.
| // ── Static DMA buffer ──────────────────────────────────────────── | |
| // Place in OCRAM (.uninit region) which is non-cached and | |
| // DMA-accessible. Using .uninit means the section is NOLOAD so it | |
| // won't bloat the flash image / hex file. The buffer is filled | |
| // before every DMA transfer, so uninitialised contents are fine. | |
| #[link_section = ".uninit.dmabuffers"] | |
| static mut DMA_BUF: core::mem::MaybeUninit<[u32; DMA_BUF_LEN]> = | |
| core::mem::MaybeUninit::uninit(); | |
| // ── Init ───────────────────────────────────────────────────────── | |
| #[init] | |
| #[init(local = [ | |
| dma_buf: [u32; DMA_BUF_LEN] = [0; DMA_BUF_LEN] | |
| ])] |
RTIC's init can initialize large objects in global memory. We're provided a &'static mut [u32; _] which we can safely pass around through Shared or Local. Once implemented, I don't think we'll need any unsafe code to access the buffer.
Learn more here. You can also study the examples in this repository, like this one:
teensy4-rs/examples/rtic_defmt_usb_log.rs
Lines 94 to 105 in 0d6b3de
| unsafe { | ||
| let gpr = ral::iomuxc_gpr::IOMUXC_GPR::instance(); | ||
| ral::modify_reg!(ral::iomuxc_gpr, gpr, GPR1, SAI1_MCLK_DIR: 1); | ||
| } |
There was a problem hiding this comment.
The IOMUXC_GPR instance should be returned through board::Resources. I appreciate that we did that for the SAI instances. Keep following that pattern.
| channel::set_source_linear_buffer(&mut *dma_chan, buf); | ||
| dma_chan.set_transfer_iterations(DMA_BUF_LEN as u16); |
There was a problem hiding this comment.
set_source_linear_buffer instructs the DMA channel to return to the buffer's start address once the transfer completes. See here. And typically, the transfer iterations are unchanged in the DMA channel, too.
Since hardware already achieves the two objectives of these calls, we could remove them. This makes the interrupt a littler faster, and the code a little smaller.
| channel::set_source_linear_buffer(&mut *dma_chan, buf); | |
| dma_chan.set_transfer_iterations(DMA_BUF_LEN as u16); |
| /// The register block for SAI1 (I2S audio). | ||
| /// | ||
| /// SAI1 is the primary audio interface used by the Teensy Audio Shield. | ||
| pub sai1: ral::sai::SAI1, | ||
| /// The register block for SAI2. | ||
| pub sai2: ral::sai::SAI2, | ||
| /// The register block for SAI3. | ||
| pub sai3: ral::sai::SAI3, |
There was a problem hiding this comment.
#182 would appreciate if we broke this out into a separate commit, along with the SAI clock setup.
There was a problem hiding this comment.
See other comments regarding #182 support. This isolated change is worthy of its own commit.
| } | ||
|
|
||
| /// Configure SAI1 clock root to derive from Audio PLL. | ||
| fn setup_sai1_clk(ccm: &mut ral::ccm::CCM) { |
There was a problem hiding this comment.
What about SAI2 and / or SAI3?
If my interpretation of the 4.0 pinout is correct, the official documentation is advertising, at least, SAI2 capabilities. It doesn't seem that SAI3 is usefully broken out, at least on the common pins.
The SAI root clocks are distinct, but it looks like we can mux them all the same. I'm looking at figure 14-2 in the reference manual (rev 3). Picking the same audio PLL would seem useful.
| imxrt-hal = { path = "../imxrt-hal", features = ["imxrt1060"] } | ||
| imxrt-iomuxc = { version = "0.3.0", features = ["imxrt1060"] } | ||
| imxrt-log = { path = "../imxrt-hal/logging", default-features = false } | ||
| imxrt-ral = { path = "../imxrt-ral", features = ["imxrt1062"] } |
There was a problem hiding this comment.
If the unreleased packages were expressed as [patch] directives pointing to git repositories, we might have an easier time getting this code building in CI / other folk's development environments. The change would look like
| imxrt-hal = { path = "../imxrt-hal", features = ["imxrt1060"] } | |
| imxrt-iomuxc = { version = "0.3.0", features = ["imxrt1060"] } | |
| imxrt-log = { path = "../imxrt-hal/logging", default-features = false } | |
| imxrt-ral = { path = "../imxrt-ral", features = ["imxrt1062"] } | |
| imxrt-hal = { version = "0.6.0", features = ["imxrt1060"] } | |
| imxrt-iomuxc = { version = "0.3.0", features = ["imxrt1060"] } | |
| imxrt-log = { version = "0.2" } | |
| imxrt-ral = { version = "0.6", features = ["imxrt1062"] } |
with supplements like
[patch.crates-io.imxrt-ral]
git = "https://github.com/imxrt-rs/imxrt-ral"
[patch.crates-io.imxrt-iomuxc]
git = "https://github.com/imxrt-rs/imxrt-iomuxc"
# etc...|
Thanks so much for all the thoughtful comments. I'll get on these. Top priority will be to get as much as I can committed without version changes. |
|
New PRs incoming.... |
Enable SAI1-3 clock gates, configure Audio PLL (PLL4) for 44.1 kHz sample rates, expose SAI peripherals in the BSP, and add two working audio examples for the Teensy 4.1 + Audio Shield Rev D (SGTL5000):
Also updates dependencies to imxrt-hal v0.6 / imxrt-ral v0.6 and fixes LPSPI PCS0 removal and usb-device v0.3 API changes.