forked from stm32-rs/stm32h7xx-hal
-
Notifications
You must be signed in to change notification settings - Fork 1
/
sai-i2s-passthru.rs
131 lines (112 loc) · 4.26 KB
/
sai-i2s-passthru.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// This demo code runs on the Electro Smith Daisy Seed board
// https://www.electro-smith.com/daisy
#![allow(unused_macros)]
#![deny(warnings)]
// #![deny(unsafe_code)]
#![no_main]
#![no_std]
use rtic::app;
use cortex_m::asm::nop;
use cortex_m::asm::delay as delay_cycles;
pub use stm32h7xx_hal::hal::digital::v2::OutputPin;
use stm32h7xx_hal::prelude::*;
use stm32h7xx_hal::rcc::rec::Sai1ClkSel;
use stm32h7xx_hal::sai::{
self, I2SChanConfig, I2SDataSize, I2SDir, I2SSync, Sai, SaiChannel,
SaiI2sExt, I2S,
};
use stm32h7xx_hal::stm32;
use stm32h7xx_hal::time::{Hertz, U32Ext};
use stm32h7xx_hal::traits::i2s::FullDuplex;
#[macro_use]
mod utilities;
use log::info;
pub const AUDIO_SAMPLE_HZ: Hertz = Hertz(48_000);
// Using PLL3_P for SAI1 clock
// The rate should be equal to sample rate * 256
// But not less than so targetting 257
const PLL3_P_HZ: Hertz = Hertz(AUDIO_SAMPLE_HZ.0 * 257);
#[app( device = stm32h7xx_hal::stm32, peripherals = true )]
const APP: () = {
struct Resources {
audio: Sai<stm32::SAI1, I2S>,
}
#[init]
fn init(mut ctx: init::Context) -> init::LateResources {
utilities::logger::init();
let pwr = ctx.device.PWR.constrain();
let vos = pwr.freeze();
// Clocks
let ccdr = ctx
.device
.RCC
.constrain()
.use_hse(16.mhz())
.sys_ck(400.mhz())
.pll3_p_ck(PLL3_P_HZ)
.freeze(vos, &ctx.device.SYSCFG);
let gpiob = ctx.device.GPIOB.split(ccdr.peripheral.GPIOB);
let gpioe = ctx.device.GPIOE.split(ccdr.peripheral.GPIOE);
let sai1_pins = (
gpioe.pe2.into_alternate_af6(), // MCLK_A
gpioe.pe5.into_alternate_af6(), // SCK_A
gpioe.pe4.into_alternate_af6(), // FS_A
gpioe.pe6.into_alternate_af6(), // SD_A
Some(gpioe.pe3.into_alternate_af6()), // SD_B
);
// Reset the codec chip
// Hold it low for ~1ms
let mut codec = gpiob.pb11.into_push_pull_output();
codec.set_low().unwrap();
delay_cycles(400_000);
codec.set_high().unwrap();
// Use PLL3_P for the SAI1 clock
let sai1_rec = ccdr.peripheral.SAI1.kernel_clk_mux(Sai1ClkSel::PLL3_P);
let master_config =
I2SChanConfig::new(I2SDir::Tx).set_frame_sync_active_high(true);
let slave_config = I2SChanConfig::new(I2SDir::Rx)
.set_sync_type(I2SSync::Internal)
.set_frame_sync_active_high(true);
let mut audio = ctx.device.SAI1.i2s_ch_a(
sai1_pins,
AUDIO_SAMPLE_HZ,
I2SDataSize::BITS_24,
sai1_rec,
&ccdr.clocks,
master_config,
Some(slave_config),
);
// Setup cache
// Sound breaks up without this enabled
ctx.core.SCB.enable_icache();
audio.listen(SaiChannel::ChannelB, sai::Event::Data);
audio.enable();
// Jump start audio
// Each of the audio blocks in the SAI are enabled by SAIEN bit in the SAI_xCR1 register.
// As soon as this bit is active, the transmitter or the receiver is sensitive
// to the activity on the clock line, data line and synchronization line in slave mode.
// In master TX mode, enabling the audio block immediately generates the bit clock for the
// external slaves even if there is no data in the FIFO, However FS signal generation
// is conditioned by the presence of data in the FIFO.
// After the FIFO receives the first data to transmit, this data is output to external slaves.
// If there is no data to transmit in the FIFO, 0 values are then sent in the audio frame
// with an underrun flag generation.
// From the reference manual (rev7 page 2259)
audio.try_send(0, 0).unwrap();
info!("Startup complete!");
init::LateResources { audio }
}
#[task( binds = SAI1, resources = [audio] )]
fn passthru(ctx: passthru::Context) {
if let Ok((left, right)) = ctx.resources.audio.try_read() {
ctx.resources.audio.try_send(left, right).unwrap();
}
}
#[idle]
fn idle(_cx: idle::Context) -> ! {
// Don't go to sleep
loop {
nop();
}
}
};