Skip to content

Commit 88bc4ca

Browse files
Merge pull request #201 from kalkyl/i2s
Add I2S module
2 parents 63697e5 + 0d609fb commit 88bc4ca

File tree

9 files changed

+1011
-0
lines changed

9 files changed

+1011
-0
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ members = [
1616
"examples/lpcomp-demo",
1717
"examples/qdec-demo",
1818
"examples/comp-demo",
19+
"examples/i2s-controller-demo",
20+
"examples/i2s-peripheral-demo",
1921
"examples/pwm-demo",
2022
"examples/twim-demo",
2123
"examples/twis-demo",
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[package]
2+
name = "i2s-controller-demo"
3+
version = "0.1.0"
4+
authors = ["Henrik Alsér"]
5+
edition = "2018"
6+
publish = false
7+
8+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
9+
10+
[dependencies]
11+
cortex-m = "0.6.2"
12+
cortex-m-rtic = "0.5.3"
13+
rtt-target = {version = "0.2.0", features = ["cortex-m"] }
14+
nrf52840-hal = { features = ["rt"], path = "../../nrf52840-hal" }
15+
heapless = "0.5.5"
16+
small_morse = "0.1.0"
17+
18+
[dependencies.embedded-hal]
19+
version = "0.2.3"
20+
features = ["unproven"]
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[default.probe]
2+
protocol = "Swd"
3+
4+
[default.flashing]
5+
enabled = true
6+
halt_afterwards = false
7+
restore_unwritten_bytes = false
8+
9+
[default.general]
10+
chip = "nRF52840"
11+
chip_descriptions = []
12+
log_level = "Warn"
13+
14+
[default.rtt]
15+
enabled = true
16+
channels = []
17+
timeout = 3000
18+
show_timestamps = true
19+
20+
[default.gdb]
21+
enabled = false
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
// I2S `controller mode` demo
5+
// Generates Morse code audio signals for text from UART, playing back over I2S
6+
// Tested with nRF52840-DK and a UDA1334a DAC
7+
8+
use embedded_hal::digital::v2::{InputPin, OutputPin};
9+
use heapless::{
10+
consts::*,
11+
spsc::{Consumer, Producer, Queue},
12+
};
13+
use small_morse::{encode, State};
14+
use {
15+
core::{
16+
panic::PanicInfo,
17+
sync::atomic::{compiler_fence, Ordering},
18+
},
19+
hal::{
20+
gpio::{Input, Level, Output, Pin, PullUp, PushPull},
21+
gpiote::*,
22+
i2s::*,
23+
pac::{TIMER0, UARTE0},
24+
timer::Timer,
25+
uarte::*,
26+
},
27+
nrf52840_hal as hal,
28+
rtic::cyccnt::U32Ext,
29+
rtt_target::{rprintln, rtt_init_print},
30+
};
31+
32+
#[rtic::app(device = crate::hal::pac, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)]
33+
const APP: () = {
34+
struct Resources {
35+
i2s: hal::i2s::I2S,
36+
#[init([0; 32])]
37+
signal_buf: [i16; 32],
38+
#[init([0; 32])]
39+
mute_buf: [i16; 32],
40+
#[init(None)]
41+
queue: Option<Queue<State, U256>>,
42+
producer: Producer<'static, State, U256>,
43+
consumer: Consumer<'static, State, U256>,
44+
#[init(5_000_000)]
45+
speed: u32,
46+
uarte: Uarte<UARTE0>,
47+
uarte_timer: Timer<TIMER0>,
48+
gpiote: Gpiote,
49+
btn1: Pin<Input<PullUp>>,
50+
btn2: Pin<Input<PullUp>>,
51+
led: Pin<Output<PushPull>>,
52+
}
53+
54+
#[init(resources = [queue, signal_buf, mute_buf], spawn = [tick])]
55+
fn init(mut ctx: init::Context) -> init::LateResources {
56+
let _clocks = hal::clocks::Clocks::new(ctx.device.CLOCK).enable_ext_hfosc();
57+
// Enable the monotonic timer (CYCCNT)
58+
ctx.core.DCB.enable_trace();
59+
ctx.core.DWT.enable_cycle_counter();
60+
rtt_init_print!();
61+
62+
let p0 = hal::gpio::p0::Parts::new(ctx.device.P0);
63+
64+
// Configure I2S controller
65+
let mck_pin = p0.p0_28.into_push_pull_output(Level::Low).degrade();
66+
let sck_pin = p0.p0_29.into_push_pull_output(Level::Low).degrade();
67+
let lrck_pin = p0.p0_31.into_push_pull_output(Level::Low).degrade();
68+
let sdout_pin = p0.p0_30.into_push_pull_output(Level::Low).degrade();
69+
70+
let i2s = I2S::new_controller(
71+
ctx.device.I2S,
72+
Some(&mck_pin),
73+
&sck_pin,
74+
&lrck_pin,
75+
None,
76+
Some(&sdout_pin),
77+
);
78+
i2s.tx_buffer(&ctx.resources.mute_buf[..]).ok();
79+
i2s.enable().start();
80+
81+
// Fill signal buffer with triangle waveform, 2 channels interleaved
82+
let signal_buf = ctx.resources.signal_buf;
83+
let len = signal_buf.len() / 2;
84+
for x in 0..len {
85+
signal_buf[2 * x] = triangle_wave(x as i32, len, 2048, 0, 1) as i16;
86+
signal_buf[2 * x + 1] = triangle_wave(x as i32, len, 2048, 0, 1) as i16;
87+
}
88+
89+
// Configure buttons
90+
let btn1 = p0.p0_11.into_pullup_input().degrade();
91+
let btn2 = p0.p0_12.into_pullup_input().degrade();
92+
let gpiote = Gpiote::new(ctx.device.GPIOTE);
93+
gpiote.port().input_pin(&btn1).low();
94+
gpiote.port().input_pin(&btn2).low();
95+
gpiote.port().enable_interrupt();
96+
97+
// Configure the onboard USB CDC UARTE
98+
let uarte = Uarte::new(
99+
ctx.device.UARTE0,
100+
Pins {
101+
txd: p0.p0_06.into_push_pull_output(Level::High).degrade(),
102+
rxd: p0.p0_08.into_floating_input().degrade(),
103+
cts: None,
104+
rts: None,
105+
},
106+
Parity::EXCLUDED,
107+
Baudrate::BAUD115200,
108+
);
109+
110+
*ctx.resources.queue = Some(Queue::new());
111+
let (producer, consumer) = ctx.resources.queue.as_mut().unwrap().split();
112+
113+
rprintln!("Morse code generator");
114+
rprintln!("Send me text over UART @ 115_200 baud");
115+
rprintln!("Press button 1 to slow down or button 2 to speed up");
116+
117+
ctx.spawn.tick().ok();
118+
119+
init::LateResources {
120+
i2s,
121+
producer,
122+
consumer,
123+
gpiote,
124+
btn1,
125+
btn2,
126+
led: p0.p0_13.into_push_pull_output(Level::High).degrade(),
127+
uarte,
128+
uarte_timer: Timer::new(ctx.device.TIMER0),
129+
}
130+
}
131+
132+
#[idle(resources=[uarte, uarte_timer, producer])]
133+
fn idle(ctx: idle::Context) -> ! {
134+
let idle::Resources {
135+
uarte,
136+
uarte_timer,
137+
producer,
138+
} = ctx.resources;
139+
let uarte_rx_buf = &mut [0u8; 64][..];
140+
loop {
141+
match uarte.read_timeout(uarte_rx_buf, uarte_timer, 100_000) {
142+
Ok(_) => {
143+
if let Ok(msg) = core::str::from_utf8(&uarte_rx_buf[..]) {
144+
rprintln!("{}", msg);
145+
for action in encode(msg) {
146+
for _ in 0..action.duration {
147+
producer.enqueue(action.state).ok();
148+
}
149+
}
150+
}
151+
}
152+
Err(hal::uarte::Error::Timeout(n)) if n > 0 => {
153+
if let Ok(msg) = core::str::from_utf8(&uarte_rx_buf[0..n]) {
154+
rprintln!("{}", msg);
155+
for action in encode(msg) {
156+
for _ in 0..action.duration {
157+
producer.enqueue(action.state).ok();
158+
}
159+
}
160+
}
161+
}
162+
_ => {}
163+
}
164+
}
165+
}
166+
167+
#[task(resources = [consumer, i2s, signal_buf, mute_buf, led, speed], schedule = [tick])]
168+
fn tick(ctx: tick::Context) {
169+
let i2s = ctx.resources.i2s;
170+
match ctx.resources.consumer.dequeue() {
171+
Some(State::On) => {
172+
// Move TX pointer to signal buffer (sound ON)
173+
i2s.tx_buffer(&ctx.resources.signal_buf[..]).ok();
174+
ctx.resources.led.set_low().ok();
175+
}
176+
_ => {
177+
// Move TX pointer to silent buffer (sound OFF)
178+
i2s.tx_buffer(&ctx.resources.mute_buf[..]).ok();
179+
ctx.resources.led.set_high().ok();
180+
}
181+
}
182+
ctx.schedule
183+
.tick(ctx.scheduled + ctx.resources.speed.cycles())
184+
.ok();
185+
}
186+
187+
#[task(binds = GPIOTE, resources = [gpiote, speed], schedule = [debounce])]
188+
fn on_gpiote(ctx: on_gpiote::Context) {
189+
ctx.resources.gpiote.reset_events();
190+
ctx.schedule.debounce(ctx.start + 3_000_000.cycles()).ok();
191+
}
192+
193+
#[task(resources = [btn1, btn2, i2s, speed])]
194+
fn debounce(ctx: debounce::Context) {
195+
if ctx.resources.btn1.is_low().unwrap() {
196+
rprintln!("Go slower");
197+
*ctx.resources.speed += 600_000;
198+
}
199+
if ctx.resources.btn2.is_low().unwrap() {
200+
rprintln!("Go faster");
201+
*ctx.resources.speed -= 600_000;
202+
}
203+
}
204+
205+
extern "C" {
206+
fn SWI0_EGU0();
207+
fn SWI1_EGU1();
208+
}
209+
};
210+
211+
fn triangle_wave(x: i32, length: usize, amplitude: i32, phase: i32, periods: i32) -> i32 {
212+
let length = length as i32;
213+
amplitude
214+
- ((2 * periods * (x + phase + length / (4 * periods)) * amplitude / length)
215+
% (2 * amplitude)
216+
- amplitude)
217+
.abs()
218+
- amplitude / 2
219+
}
220+
221+
#[inline(never)]
222+
#[panic_handler]
223+
fn panic(info: &PanicInfo) -> ! {
224+
cortex_m::interrupt::disable();
225+
rprintln!("{}", info);
226+
loop {
227+
compiler_fence(Ordering::SeqCst);
228+
}
229+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[package]
2+
name = "i2s-peripheral-demo"
3+
version = "0.1.0"
4+
authors = ["Henrik Alsér"]
5+
edition = "2018"
6+
publish = false
7+
8+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
9+
10+
[dependencies]
11+
cortex-m = "0.6.2"
12+
cortex-m-rtic = "0.5.3"
13+
rtt-target = {version = "0.2.0", features = ["cortex-m"] }
14+
nrf52840-hal = { features = ["rt"], path = "../../nrf52840-hal" }
15+
small_morse = "0.1.0"
16+
m = "0.1.1"
17+
18+
[dependencies.embedded-hal]
19+
version = "0.2.3"
20+
features = ["unproven"]
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[default.probe]
2+
protocol = "Swd"
3+
4+
[default.flashing]
5+
enabled = true
6+
halt_afterwards = false
7+
restore_unwritten_bytes = false
8+
9+
[default.general]
10+
chip = "nRF52840"
11+
chip_descriptions = []
12+
log_level = "Warn"
13+
14+
[default.rtt]
15+
enabled = true
16+
channels = []
17+
timeout = 3000
18+
show_timestamps = true
19+
20+
[default.gdb]
21+
enabled = false

0 commit comments

Comments
 (0)