Skip to content

Commit 98d7bc1

Browse files
bors[bot]samcrow
andauthored
Merge #265
265: Add I2S communication using SPI peripherals r=therealprof a=samcrow # Introduction Welcome to my first large pull request, which adds support for I2S audio communication using supported SPI peripherals. Like the way we support CAN with the bxcan library, I am proposing to support I2S using my [stm32_i2s_v12x](https://crates.io/crates/stm32_i2s_v12x) library. Although stm32_i2s_v12x is in a separate repository, we can also talk about it here. # Notes * The I2S module is available if the `i2s` feature is enabled, in order to not increase compile time for applications that don't use I2S. * All four modes are supported: master transmit, master receive, slave transmit, and slave receive. * Basic support for DMA, interrupts, and error detection is provided. * All STM32F4 models are supported. * I added two examples that run on the STM32F411E-DISCO board and send audio to the on-board DAC. One of them uses DMA. These changes are not perfect, so criticism and suggestions are welcome. # Limitations * No support for full-duplex communication * I have tested master transmit mode fairly thoroughly, but have not tested the other modes. * No support for embedded-hal I2S traits * [The I2S pull request](rust-embedded/embedded-hal#204) is still under review. The proposed traits look compatible with this driver code. Until that pull request gets merged and released, people can still use this driver code directly. # Related work * SAI driver pull request in this repository: #248 * Another, less complete, pull request for I2S in this repository: #212 * also #145 * embedded-hal pull request for I2S traits: rust-embedded/embedded-hal#204 Co-authored-by: Sam Crow <[email protected]> Co-authored-by: Sam Crow <[email protected]>
2 parents 2090a28 + 441a7a9 commit 98d7bc1

File tree

9 files changed

+1058
-3
lines changed

9 files changed

+1058
-3
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,4 @@ jobs:
4646
- uses: actions-rs/cargo@v1
4747
with:
4848
command: check
49-
args: --features=${{ matrix.mcu }},rt,usb_fs,sdio,can --examples
49+
args: --features=${{ matrix.mcu }},rt,usb_fs,sdio,can,i2s --examples

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1616
- Update the sdio driver to match the changes in the PAC
1717
- Update README.md with current information
1818

19+
### Added
20+
21+
- Added support for I2S communication using SPI peripherals, and two examples [#265]
22+
23+
[#265]: https://github.com/stm32-rs/stm32f4xx-hal/pull/265
24+
1925
## [v0.9.0] - 2021-04-04
2026

2127
### Changed

Cargo.toml

+15-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ repository = "https://github.com/stm32-rs/stm32f4xx-hal"
2222
version = "0.9.0"
2323

2424
[package.metadata.docs.rs]
25-
features = ["stm32f429", "rt", "usb_fs", "can"]
25+
features = ["stm32f429", "rt", "usb_fs", "can", "i2s"]
2626
targets = ["thumbv7em-none-eabihf"]
2727

2828
[dependencies]
@@ -41,6 +41,10 @@ cast = { default-features = false, version = "0.2.2" }
4141
void = { default-features = false, version = "1.0.2" }
4242
embedded-hal = { features = ["unproven"], version = "0.2.3" }
4343

44+
[dependencies.stm32_i2s_v12x]
45+
version = "0.2.0"
46+
optional = true
47+
4448
[dev-dependencies]
4549
panic-semihosting = "0.5.3"
4650
cortex-m-semihosting = "0.3.3"
@@ -83,6 +87,8 @@ can = ["bxcan"]
8387

8488
sdio = ["sdio-host"]
8589

90+
i2s = ["stm32_i2s_v12x"]
91+
8692
[profile.dev]
8793
debug = true
8894
lto = true
@@ -136,6 +142,14 @@ required-features = ["rt", "stm32f411"]
136142
name = "can-send"
137143
required-features = ["can", "stm32f405"]
138144

145+
[[example]]
146+
name = "i2s-audio-out"
147+
required-features = ["stm32f411", "rt", "i2s"]
148+
149+
[[example]]
150+
name = "i2s-audio-out-dma"
151+
required-features = ["stm32f411", "rt", "i2s"]
152+
139153
[[example]]
140154
name = "rtic"
141155
required-features = ["rt", "stm32f407"]

examples/cs43l22/mod.rs

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
//! Bare-bones driver for configuring a CS43L22 digital-analog converter
2+
3+
use stm32f4xx_hal::hal::blocking::i2c::{Read, Write};
4+
5+
/// Interface to the I2C control port of a Cirrus Logic CS43L22 DAC
6+
pub struct Cs43L22<I> {
7+
/// I2C interface
8+
i2c: I,
9+
/// Address of DAC
10+
address: u8,
11+
}
12+
13+
impl<I> Cs43L22<I>
14+
where
15+
I: Write + Read,
16+
{
17+
pub fn new(i2c: I, address: u8) -> Self {
18+
Cs43L22 { i2c, address }
19+
}
20+
21+
/// Does basic configuration as specified in the datasheet
22+
pub fn basic_setup(&mut self) -> Result<(), <I as Write>::Error> {
23+
// Settings from section 4.11 of the datasheet
24+
self.write(Register::Magic00, 0x99)?;
25+
self.write(Register::Magic47, 0x80)?;
26+
self.write(Register::Magic32, 0x80)?;
27+
self.write(Register::Magic32, 0x00)?;
28+
self.write(Register::Magic00, 0x00)
29+
}
30+
31+
/// Writes the value of one register
32+
pub fn write(&mut self, register: Register, value: u8) -> Result<(), <I as Write>::Error> {
33+
// Set auto-increment bit
34+
let map = (register as u8) | 0x80;
35+
self.i2c.write(self.address, &[map, value])
36+
}
37+
38+
/// Reads the value of one register
39+
#[allow(dead_code)]
40+
pub fn read(
41+
&mut self,
42+
register: Register,
43+
) -> Result<u8, CombinedI2cError<<I as Read>::Error, <I as Write>::Error>> {
44+
let mut values = [0u8];
45+
self.read_multiple(register, &mut values)?;
46+
Ok(values[0])
47+
}
48+
/// Reads the values of zero or more consecutive registers
49+
#[allow(dead_code)]
50+
pub fn read_multiple(
51+
&mut self,
52+
register: Register,
53+
values: &mut [u8],
54+
) -> Result<(), CombinedI2cError<<I as Read>::Error, <I as Write>::Error>> {
55+
// Two transactions: set the memory address pointer, then read
56+
// An empty write sets the address
57+
// Set auto-increment bit
58+
let map = (register as u8) | 0x80;
59+
self.i2c
60+
.write(self.address, &[map])
61+
.map_err(CombinedI2cError::Write)?;
62+
self.i2c
63+
.read(self.address, values)
64+
.map_err(CombinedI2cError::Read)
65+
}
66+
}
67+
68+
#[derive(Debug)]
69+
pub enum CombinedI2cError<R, W> {
70+
Read(R),
71+
Write(W),
72+
}
73+
74+
/// CS43L22 registers
75+
#[allow(dead_code)]
76+
pub enum Register {
77+
/// This is used in the specified startup sequence, but its actual content is not documented.
78+
Magic00 = 0x00,
79+
Id = 0x01,
80+
PowerCtl1 = 0x02,
81+
PowerCtl2 = 0x04,
82+
ClockingCtl = 0x05,
83+
InterfaceCtl1 = 0x06,
84+
InterfaceCtl2 = 0x07,
85+
PassthroughASelect = 0x08,
86+
PassthroughBSelect = 0x09,
87+
AnalogZcSr = 0x0a,
88+
PassthroughGangCtl = 0x0c,
89+
PlaybackCtl1 = 0x0d,
90+
MiscCtl = 0x0e,
91+
PlaybackCtl2 = 0x0f,
92+
PassthroughAVol = 0x14,
93+
PassthroughBVol = 0x15,
94+
PcmAVol = 0x1a,
95+
PcmBVol = 0x1b,
96+
BeepFreqOnTime = 0x1c,
97+
BeepVolOffTime = 0x1d,
98+
BeepToneCfg = 0x1e,
99+
ToneCtl = 0x1f,
100+
MasterAVol = 0x20,
101+
MasterBVol = 0x21,
102+
HeadphoneAVol = 0x22,
103+
HeadphoneBVol = 0x23,
104+
SpeakerAVol = 0x24,
105+
SpeakerBVol = 0x25,
106+
ChannelMixer = 0x26,
107+
LimitCtl1 = 0x27,
108+
LimitClt2 = 0x28,
109+
LimitAttack = 0x29,
110+
Status = 0x2e,
111+
BatteryComp = 0x2f,
112+
VpBatteryLevel = 0x30,
113+
SpeakerStatus = 0x31,
114+
/// This is used in the specified startup sequence, but its actual content is not documented.
115+
Magic32 = 0x32,
116+
ChargePumpFreq = 0x34,
117+
/// This is used in the specified startup sequence, but its actual content is not documented.
118+
Magic47 = 0x47,
119+
}

0 commit comments

Comments
 (0)