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

SAI: audio pll, sai peripheral driver, and small rtic synthesizing sample #143

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .helix/languages.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[language-server.rust-analyzer.config]
cargo = { features = ["imxrt1010"], target = "thumbv7em-none-eabihf" }
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,14 @@ members = ["board", "logging"]

[workspace.dependencies]
imxrt-dma = "0.1"
imxrt-iomuxc = "0.2.1"
imxrt-iomuxc = "0.2.4"
imxrt-hal = { version = "0.5", path = "." }
imxrt-log = { path = "logging", default-features = false, features = [
"log",
"lpuart",
"usbd",
] }
imxrt-ral = "0.5"
imxrt-ral = { version = "0.6", git = "https://github.com/imxrt-rs/imxrt-ral.git", rev = "a2383c53a1537c152e0aed4880402edb16cc6577" }
imxrt-rt = "0.1"
imxrt-usbd = "0.2"

Expand Down Expand Up @@ -142,6 +142,7 @@ pin-utils = "0.1"
usb-device = { version = "0.2", features = ["test-class-high-speed"] }
usbd-serial = "0.1"
usbd-hid = "0.6"
wm8960 = "0.1.0"

[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dev-dependencies]
board = { path = "board" }
Expand Down
1 change: 1 addition & 0 deletions board/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ PROVIDE(BOARD_PWM = DefaultHandler);
PROVIDE(BOARD_DMA_A = DefaultHandler);
PROVIDE(BOARD_DMA_B = DefaultHandler);
PROVIDE(BOARD_PIT = DefaultHandler);
PROVIDE(BOARD_SAI1 = DefaultHandler);
PROVIDE(BOARD_GPT1 = DefaultHandler);
PROVIDE(BOARD_GPT2 = DefaultHandler);
PROVIDE(BOARD_USB1 = DefaultHandler);
Expand Down
24 changes: 24 additions & 0 deletions board/src/imxrt1010evk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ pub type Console = hal::lpuart::Lpuart<ConsolePins, 1>;
/// host and the MCU.
pub type ConsolePins = crate::hal::lpuart::Pins<iomuxc::gpio::GPIO_10, iomuxc::gpio::GPIO_09>;

pub type Sai1MclkPin = iomuxc::gpio::GPIO_08;

pub type Sai1TxPins =
hal::sai::Pins<iomuxc::gpio::GPIO_07, iomuxc::gpio::GPIO_06, iomuxc::gpio::GPIO_04>;

pub type Sai1RxPins =
hal::sai::Pins<iomuxc::gpio::GPIO_02, iomuxc::gpio::GPIO_01, iomuxc::gpio::GPIO_03>;

pub type Sai1 = hal::sai::Sai<1, Sai1MclkPin, Sai1TxPins, ()>;

pub type SpiPins = hal::lpspi::Pins<
iomuxc::gpio_ad::GPIO_AD_04, // SDO, J57_8
iomuxc::gpio_ad::GPIO_AD_03, // SDI, J57_10
Expand Down Expand Up @@ -146,6 +156,7 @@ pub struct Specifics {
pub console: Console,
pub spi: Spi,
pub i2c: I2c,
pub sai1: Sai1,
pub pwm: Pwm,
pub tp34: Tp34,
pub tp31: Tp31,
Expand Down Expand Up @@ -180,6 +191,16 @@ impl Specifics {
console.set_parity(None);
});

let sai1 = {
let sai1 = unsafe { ral::sai::SAI1::instance() };
let pins = Sai1TxPins {
sync: iomuxc.gpio.p07,
bclk: iomuxc.gpio.p06,
data: iomuxc.gpio.p04,
};
Sai1::from_tx(sai1, iomuxc.gpio.p08, pins)
};

#[cfg(feature = "spi")]
let spi = {
let lpspi1 = unsafe { ral::lpspi::LPSPI1::instance() };
Expand Down Expand Up @@ -248,6 +269,7 @@ impl Specifics {
console,
spi,
i2c,
sai1,
pwm,
tp34: iomuxc.gpio_sd.p02,
tp31: iomuxc.gpio_sd.p01,
Expand Down Expand Up @@ -344,6 +366,7 @@ pub mod interrupt {
pub const BOARD_DMA_A: Interrupt = Interrupt::DMA7;
pub const BOARD_DMA_B: Interrupt = Interrupt::DMA11;
pub const BOARD_PIT: Interrupt = Interrupt::PIT;
pub const BOARD_SAI1: Interrupt = Interrupt::SAI1;
pub const BOARD_GPT1: Interrupt = Interrupt::GPT1;
pub const BOARD_GPT2: Interrupt = Interrupt::GPT2;
pub const BOARD_USB1: Interrupt = Interrupt::USB_OTG1;
Expand All @@ -357,6 +380,7 @@ pub mod interrupt {
(BOARD_DMA_A, syms::BOARD_DMA_A),
(BOARD_DMA_B, syms::BOARD_DMA_B),
(BOARD_PIT, syms::BOARD_PIT),
(BOARD_SAI1, syms::BOARD_SAI1),
(BOARD_GPT1, syms::BOARD_GPT1),
(BOARD_GPT2, syms::BOARD_GPT2),
(BOARD_USB1, syms::BOARD_USB1),
Expand Down
2 changes: 2 additions & 0 deletions board/src/imxrt1060evk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ pub mod interrupt {
pub const BOARD_DMA_A: Interrupt = Interrupt::DMA7_DMA23;
pub const BOARD_DMA_B: Interrupt = Interrupt::DMA11_DMA27;
pub const BOARD_PIT: Interrupt = Interrupt::PIT;
pub const BOARD_SAI1: Interrupt = Interrupt::SAI1;
pub const BOARD_GPT1: Interrupt = Interrupt::GPT1;
pub const BOARD_GPT2: Interrupt = Interrupt::GPT2;
pub const BOARD_USB1: Interrupt = Interrupt::USB_OTG1;
Expand All @@ -315,6 +316,7 @@ pub mod interrupt {
(BOARD_DMA_A, syms::BOARD_DMA_A),
(BOARD_DMA_B, syms::BOARD_DMA_B),
(BOARD_PIT, syms::BOARD_PIT),
(BOARD_SAI1, syms::BOARD_SAI1),
(BOARD_GPT1, syms::BOARD_GPT1),
(BOARD_GPT2, syms::BOARD_GPT2),
(BOARD_USB1, syms::BOARD_USB1),
Expand Down
15 changes: 15 additions & 0 deletions board/src/imxrt10xx/clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,18 @@ fn prepare_clock_tree(
clock_tree::configure_lpspi(RUN_MODE, ccm);
clock_tree::configure_perclk(RUN_MODE, ccm);
clock_tree::configure_uart(RUN_MODE, ccm);
clock_tree::configure_sai(RUN_MODE, ccm);
ccm::analog::pll3::restart(ccm_analog);
//clock output settings for the audio pll
//24000000*(30 + 72/100)/1 = 737.28MHz
//sai mclk settings then are
//(737280000/5)/6 = 24.576MHz
//sai bclk (for 48kHz 16bit stereo)
//24576kHz/16 = 1536000.000 Hz
//Ideal bclk is (48000*2*16) = 1536000
//24576kHz/512 = 48kHz
//Ideal frame sync is 48000
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commit message additionally suggests that this might be WM8960 specific.
Fortunately this is AFAICT usable much more generally. 24.576MHz can often be used for various 48kHz based sampling rates with 16 or 32bit. AKM has nice tables showing this in their datasheets.E.g. AK4452 Tables 3+4/6+7 show this is usable up to hex speed. BICK requirements probably further limit it to 16bit @ 384kHz.

ccm::analog::pll4::reconfigure(ccm_analog, 30, 72, 100, ccm::analog::pll4::PostDivider::U1);
}

use hal::ccm::clock_gate;
Expand All @@ -53,6 +64,10 @@ const COMMON_CLOCK_GATES: &[clock_gate::Locator] = &[
clock_gate::dma(),
clock_gate::usb(),
clock_gate::trng(),
clock_gate::sai::<1>(),
//#[cfg(not(feature = "imxrt1010"))]
//clock_gate::sai::<2>(),
//clock_gate::sai::<3>(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this unfinished?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this commented code would work. The board crate can't detect an imxrt-hal crate feature called imxrt1010, since they're different crates. And as I write this, board doesn't have its own imxrt1010 feature.

I have two ideas that might work:

  1. Each board has its own clock gate collection (one example, another example). Since the SAI instance used by a board depends on the board, we can put the clock gate locator there. Board configuration considers the combination of these common clocks and the board-specific clocks, so it's equivalent to what we're going for.
  2. Change each 10xx board to expose a constant, like SAI_INSTANCE, that's used like clock_gate::sai::<{board_impl::SAI_INSTANCE}>() in this collection of common clock gate locators.

I prefer the first approach. That should make it easier to introduce a board that doesn't immediately support an SAI example, and there's precedent.

clock_gate::snvs_lp(),
clock_gate::snvs_hp(),
];
Expand Down
39 changes: 38 additions & 1 deletion board/src/imxrt10xx/clock_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
pub(crate) use super::ahb::{ahb_frequency, configure_ahb_ipg};
use crate::{
hal::ccm::{
analog, clock_gate, lpi2c_clk, lpspi_clk, perclk_clk, uart_clk, XTAL_OSCILLATOR_HZ,
analog, clock_gate, lpi2c_clk, lpspi_clk, perclk_clk, sai_clk, uart_clk, XTAL_OSCILLATOR_HZ,
},
ral::ccm::CCM,
RunMode,
Expand Down Expand Up @@ -117,6 +117,27 @@ pub const fn lpi2c_frequency(run_mode: RunMode) -> u32 {

const _: () = assert!(lpi2c_frequency(RunMode::Overdrive) == 8_000_000); // Max is 66MHz.

/// Specify the SAIn clock divider for a given run mode.
const fn sai_divider(_n: u8, run_mode: RunMode) -> u32 {
match run_mode {
RunMode::Overdrive => 6,
}
}

/// Specify the SAIn clock pre-divider for a given run mode.
const fn sai_pre_divider(_n: u8, run_mode: RunMode) -> u32 {
match run_mode {
RunMode::Overdrive => 5,
}
}

/// Specify the source clock for SAIn to be the Audio PLL
const fn sai_selection(_n: u8, run_mode: RunMode) -> sai_clk::Selection {
match run_mode {
RunMode::Overdrive => sai_clk::Selection::Pll4,
}
}

/// Configure the PERCLK root clock.
///
/// When this call returns, the PERCLK clock frequency match the values
Expand Down Expand Up @@ -162,6 +183,22 @@ pub fn configure_uart(run_mode: RunMode, ccm: &mut CCM) {
uart_clk::set_divider(ccm, uart_divider(run_mode));
}

/// Configure the SAI root clock.
///
/// When this call returns, the SAI clock frequency match the values
/// returned by the [`sai_frequency()`] function.
///
/// This function will disable the clock gates for various peripherals. It
/// may leave these clock gates disabled.
pub fn configure_sai(run_mode: RunMode, ccm: &mut CCM) {
clock_gate::SAI_CLOCK_GATES
.iter()
.for_each(|locator| locator.set(ccm, clock_gate::OFF));
sai_clk::set_selection::<1>(ccm, sai_selection(1, run_mode));
sai_clk::set_pre_divider::<1>(ccm, sai_pre_divider(1, run_mode));
sai_clk::set_divider::<1>(ccm, sai_divider(1, run_mode));
}

/// Configure the LPI2C root clock.
///
/// When this call returns, the LPI2C clock frequency match the values
Expand Down
1 change: 1 addition & 0 deletions board/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ mod board_interrupts {
pub fn BOARD_DMA_A();
pub fn BOARD_DMA_B();
pub fn BOARD_PIT();
pub fn BOARD_SAI1();
pub fn BOARD_GPT1();
pub fn BOARD_GPT2();
pub fn BOARD_USB1();
Expand Down
2 changes: 2 additions & 0 deletions board/src/teensy4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ pub mod interrupt {
pub const BOARD_DMA_A: Interrupt = Interrupt::DMA7_DMA23;
pub const BOARD_DMA_B: Interrupt = Interrupt::DMA11_DMA27;
pub const BOARD_PIT: Interrupt = Interrupt::PIT;
pub const BOARD_SAI1: Interrupt = Interrupt::SAI1;
pub const BOARD_GPT1: Interrupt = Interrupt::GPT1;
pub const BOARD_GPT2: Interrupt = Interrupt::GPT2;
pub const BOARD_USB1: Interrupt = Interrupt::USB_OTG1;
Expand All @@ -296,6 +297,7 @@ pub mod interrupt {
(BOARD_DMA_A, syms::BOARD_DMA_A),
(BOARD_DMA_B, syms::BOARD_DMA_B),
(BOARD_PIT, syms::BOARD_PIT),
(BOARD_SAI1, syms::BOARD_SAI1),
(BOARD_GPT1, syms::BOARD_GPT1),
(BOARD_GPT2, syms::BOARD_GPT2),
(BOARD_USB1, syms::BOARD_USB1),
Expand Down