Skip to content

Commit d51569b

Browse files
ledneczkirleh
authored andcommitted
[driver] Add CS43L22 audio DAC (I2S + I²C)
1 parent 5a040e0 commit d51569b

File tree

3 files changed

+528
-0
lines changed

3 files changed

+528
-0
lines changed

src/modm/driver/dac/cs43l22.hpp

Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
/*
2+
* Copyright (c) 2021, Marton Ledneczki
3+
*
4+
* This file is part of the modm project.
5+
*
6+
* This Source Code Form is subject to the terms of the Mozilla Public
7+
* License, v. 2.0. If a copy of the MPL was not distributed with this
8+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
// ----------------------------------------------------------------------------
11+
12+
#ifndef MODM_CS43L22_HPP
13+
#define MODM_CS43L22_HPP
14+
15+
#include <modm/architecture/interface/i2c_device.hpp>
16+
17+
namespace modm
18+
{
19+
20+
/// @ingroup modm_driver_cs43l22
21+
struct cs43l22
22+
{
23+
enum class
24+
Register : uint8_t
25+
{
26+
ChipIdRevision = 0x01,
27+
PowerControl1 = 0x02,
28+
PowerControl2 = 0x04,
29+
ClockingControl = 0x05,
30+
InterfaceControl1 = 0x06,
31+
InterfaceControl2 = 0x07,
32+
PassthroughASelect = 0x08,
33+
PassthroughBSelect = 0x09,
34+
AnalogZcAndSrSettings = 0x0A,
35+
PassthroughGansControl = 0x0C,
36+
PlaybackControl1 = 0x0D,
37+
MiscellaneousControls = 0x0E,
38+
PlaybackControl2 = 0x0F,
39+
PassthroughAVolume = 0x14,
40+
PassthroughBVolume = 0x15,
41+
PcmAVolume = 0x1A,
42+
PcmBVolume = 0x1B,
43+
BeepFrequencyAndOnTime = 0x1C,
44+
BeepVolumeAndOffTime = 0x1D,
45+
BeepAndToneConfiguration = 0x1E,
46+
ToneControl = 0x1F,
47+
MasterVolumeControlA = 0x20,
48+
MasterVolumeControlB = 0x21,
49+
HeadphoneVolumeControlA = 0x22,
50+
HeadphoneVolumeControlB = 0x23,
51+
SpeakerVolumeControlA = 0x24,
52+
SpeakerVolumeControlB = 0x25,
53+
PcmChannelSwap = 0x26,
54+
LimiterControl1MinMaxThresholds = 0x27,
55+
LimiterControl2ReleaseRate = 0x28,
56+
LimiterAttackRate = 0x29,
57+
Status = 0x2E,
58+
VpBatteryLevel = 0x30,
59+
SpeakerStatus = 0x31,
60+
ChargePumpFrequency = 0x34
61+
};
62+
63+
enum class
64+
ChipIdRevision : uint8_t
65+
{
66+
REVID0 = Bit0,
67+
REVID1 = Bit1,
68+
REVID2 = Bit2,
69+
70+
CHIPID0 = Bit3,
71+
CHIPID1 = Bit4,
72+
CHIPID2 = Bit5,
73+
CHIPID3 = Bit6,
74+
CHIPID4 = Bit7
75+
};
76+
MODM_FLAGS8(ChipIdRevision);
77+
78+
enum class
79+
ChipId : uint8_t
80+
{
81+
CS43L22 = int(ChipIdRevision::CHIPID4) | int(ChipIdRevision::CHIPID3) | int(ChipIdRevision::CHIPID2)
82+
};
83+
typedef Configuration< ChipIdRevision_t, ChipId, (Bit7 | Bit6 | Bit5 | Bit4 | Bit3) > ChipId_t;
84+
85+
enum class
86+
RevisionId : uint8_t
87+
{
88+
A0 = 0,
89+
A1 = int(ChipIdRevision::REVID0),
90+
B0 = int(ChipIdRevision::REVID1),
91+
B1 = int(ChipIdRevision::REVID1) | int(ChipIdRevision::REVID0)
92+
};
93+
typedef Configuration< ChipIdRevision_t, RevisionId, (Bit2 | Bit1 | Bit0) > RevisionId_t;
94+
95+
enum class
96+
PowerControl1 : uint8_t
97+
{
98+
PDN0 = Bit0,
99+
PDN1 = Bit1,
100+
PDN2 = Bit2,
101+
PDN3 = Bit3,
102+
PDN4 = Bit4,
103+
PDN5 = Bit5,
104+
PDN6 = Bit6,
105+
PDN7 = Bit7,
106+
};
107+
MODM_FLAGS8(PowerControl1);
108+
109+
enum class
110+
Power : uint8_t
111+
{
112+
Down = int(PowerControl1::PDN0),
113+
Up = int(PowerControl1::PDN7) |
114+
int(PowerControl1::PDN4) |
115+
int(PowerControl1::PDN3) |
116+
int(PowerControl1::PDN2) |
117+
int(PowerControl1::PDN1)
118+
};
119+
typedef Configuration< PowerControl1_t, Power, 0xFF > Power_t;
120+
121+
enum class
122+
PowerControl2 : uint8_t
123+
{
124+
PDN_SPKA0 = Bit0,
125+
PDN_SPKA1 = Bit1,
126+
127+
PDN_SPKB0 = Bit2,
128+
PDN_SPKB1 = Bit3,
129+
130+
PDN_HPA0 = Bit4,
131+
PDN_HPA1 = Bit5,
132+
133+
PDN_HPB0 = Bit6,
134+
PDN_HPB1 = Bit7,
135+
};
136+
MODM_FLAGS8(PowerControl2);
137+
138+
enum class
139+
ChannelPower : uint8_t
140+
{
141+
OnWhenSelectorPinLo = 0,
142+
OnWhenSelectorPinHi = 1,
143+
OnAlways = 2,
144+
OffAlways = 3
145+
};
146+
typedef Configuration< PowerControl2_t, ChannelPower, 0b11, 0> ChannelPowerSpeakerA_t;
147+
typedef Configuration< PowerControl2_t, ChannelPower, 0b11, 2> ChannelPowerSpeakerB_t;
148+
typedef Configuration< PowerControl2_t, ChannelPower, 0b11, 4> ChannelPowerHeadphoneA_t;
149+
typedef Configuration< PowerControl2_t, ChannelPower, 0b11, 6> ChannelPowerHeadphoneB_t;
150+
151+
enum class
152+
ClockingControl : uint8_t
153+
{
154+
MCLKDIV2 = Bit0,
155+
156+
RATIO0 = Bit1,
157+
RATIO1 = Bit2,
158+
159+
VIDEOCLK = Bit3,
160+
GROUP_32K = Bit4,
161+
162+
SPEED0 = Bit5,
163+
SPEED1 = Bit6,
164+
165+
AUTO_DETECT = Bit7
166+
};
167+
MODM_FLAGS8(ClockingControl);
168+
169+
enum class
170+
InterfaceControl1 : uint8_t
171+
{
172+
AWL0 = Bit0,
173+
AWL1 = Bit1,
174+
175+
DACDIF0 = Bit2,
176+
DACDIF1 = Bit3,
177+
178+
DSP = Bit4,
179+
INV_SCLK = Bit6,
180+
MASTER = Bit7 // Master (output only), otherwise Slave (input only)
181+
};
182+
MODM_FLAGS8(InterfaceControl1);
183+
184+
enum class
185+
Role
186+
{
187+
Slave = 0,
188+
Master = int(InterfaceControl1::MASTER)
189+
};
190+
typedef Configuration< InterfaceControl1_t, Role, Bit7 > Role_t;
191+
192+
enum class
193+
DacInterfaceFormat : uint8_t
194+
{
195+
LeftJustified = 0,
196+
I2sPhillipsStandard = int(InterfaceControl1::DACDIF0),
197+
RightJustified = int(InterfaceControl1::DACDIF1)
198+
};
199+
typedef Configuration< InterfaceControl1_t, DacInterfaceFormat, (Bit2 | Bit3) > DacInterfaceFormat_t;
200+
201+
enum class
202+
MasterVolumeControl : uint8_t
203+
{
204+
MSTVOL0 = Bit0,
205+
MSTVOL1 = Bit1,
206+
MSTVOL2 = Bit2,
207+
MSTVOL3 = Bit3,
208+
MSTVOL4 = Bit4,
209+
MSTVOL5 = Bit5,
210+
MSTVOL6 = Bit6,
211+
MSTVOL7 = Bit7,
212+
};
213+
MODM_FLAGS8(MasterVolumeControl);
214+
typedef Value< MasterVolumeControl_t, 8 > MasterVol_t;
215+
216+
enum class
217+
AnalogZcAndSrSettings : uint8_t
218+
{
219+
ANLGZCA = Bit0,
220+
ANLGSFTA = Bit1,
221+
ANLGZCB = Bit2,
222+
ANLGSFTB = Bit2
223+
};
224+
MODM_FLAGS8(AnalogZcAndSrSettings);
225+
226+
enum class
227+
SoftRamp : uint8_t
228+
{
229+
Disabled = 0,
230+
Enabled = 1
231+
};
232+
typedef Configuration< AnalogZcAndSrSettings_t, SoftRamp, 0b1, 1> AnalogSoftRampA_t;
233+
typedef Configuration< AnalogZcAndSrSettings_t, SoftRamp, 0b1, 3> AnalogSoftRampB_t;
234+
235+
enum class
236+
ZeroCrossing : uint8_t
237+
{
238+
Disabled = 0,
239+
Enabled = 1
240+
};
241+
typedef Configuration< AnalogZcAndSrSettings_t, ZeroCrossing, 0b1, 0> AnalogZeroCrossingA_t;
242+
typedef Configuration< AnalogZcAndSrSettings_t, ZeroCrossing, 0b1, 2> AnalogZeroCrossingB_t;
243+
244+
enum class
245+
MiscellaneousControls
246+
{
247+
DIGZC = Bit0,
248+
DIGSFT = Bit1,
249+
DEEMPH = Bit2,
250+
FREEZE = Bit3,
251+
PASSAMUTE = Bit4,
252+
PASSBMUTE = Bit5,
253+
PASSTHRUA = Bit6,
254+
PASSTHRUB = Bit7
255+
};
256+
MODM_FLAGS8(MiscellaneousControls);
257+
258+
enum class
259+
LimiterControl1MinMaxThresholds
260+
{
261+
LIMZCDIS = Bit0,
262+
LIMSRDIS = Bit1,
263+
264+
CUSH0 = Bit2,
265+
CUSH1 = Bit3,
266+
CUSH2 = Bit4,
267+
268+
LMAX0 = Bit5,
269+
LMAX1 = Bit6,
270+
LMAX2 = Bit7
271+
};
272+
MODM_FLAGS8(LimiterControl1MinMaxThresholds);
273+
274+
using RegisterValue_t = FlagsGroup< ChipIdRevision_t, PowerControl1_t, PowerControl2_t,
275+
ClockingControl_t, InterfaceControl1_t,
276+
MasterVolumeControl_t, AnalogZcAndSrSettings_t,
277+
MiscellaneousControls_t, LimiterControl1MinMaxThresholds_t >;
278+
279+
typedef int16_t centiBel_t;
280+
static constexpr centiBel_t MaxVolume = 120;
281+
static constexpr centiBel_t MinVolume = -1020;
282+
}; // struct cs43l22
283+
284+
285+
/**
286+
* Cirrus Logic CS43L22, Portable Audio DAC with Integrated Class D Speaker Driver
287+
*
288+
* @tparam I2cMaster I²C interface
289+
*
290+
* @author Marton Ledneczki
291+
* @ingroup modm_driver_cs43l22
292+
*/
293+
template<typename I2cMaster>
294+
class Cs43l22 : public cs43l22, public modm::I2cDevice<I2cMaster, 5>
295+
{
296+
public:
297+
Cs43l22(uint8_t i2cAddress);
298+
299+
/// Initialize device, call before using any other function
300+
ResumableResult<bool>
301+
initialize();
302+
303+
/**
304+
* @note max volume is specified by cs43l22::MaxVolume
305+
* and min volume by cs43l22::MinVolume
306+
*/
307+
ResumableResult<bool>
308+
setMasterVolume(centiBel_t vol);
309+
310+
ResumableResult<bool>
311+
setMasterVolumeRelative(centiBel_t rel_vol);
312+
313+
private:
314+
uint8_t tx_buffer[2];
315+
uint8_t rx_buffer;
316+
centiBel_t volume;
317+
bool success;
318+
319+
ResumableResult<bool>
320+
writeRegister(Register reg, RegisterValue_t value);
321+
322+
ResumableResult<bool>
323+
readRegister(Register reg);
324+
325+
ResumableResult<bool>
326+
getMasterVolume();
327+
328+
void
329+
regToCentibel(uint8_t reg);
330+
331+
};
332+
333+
} // namespace modm
334+
335+
#include "cs43l22_impl.hpp"
336+
337+
#endif // MODM_CS43L22_HPP
338+

src/modm/driver/dac/cs43l22.lb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright (c) 2021, Marton Ledneczki
5+
#
6+
# This file is part of the modm project.
7+
#
8+
# This Source Code Form is subject to the terms of the Mozilla Public
9+
# License, v. 2.0. If a copy of the MPL was not distributed with this
10+
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
11+
# -----------------------------------------------------------------------------
12+
13+
14+
def init(module):
15+
module.name = ":driver:cs43l22"
16+
module.description = """\
17+
# CS43L22, Portable Audio DAC with Integrated Class D Speaker Driver
18+
19+
The CS43L22 is low power stereo digital/analog converter (DAC) with
20+
integrated headphone and Class D speaker amplifiers that operates from a
21+
low voltage analog and digital core. The DAC output path includes a
22+
digital signal processing engine with various fixed-function controls,
23+
such as tone and volume control, and includes de-emphasis, limiting
24+
functions and beep generator that delivers tones selectable across
25+
a range of two octaves. The stereo headphone amplifier is powered
26+
from a separate positive supply and the integrated charge pump provides
27+
a negative supply allowing for a ground-centered analog output to eliminate
28+
the need for DC blocking capacitors. The Class D stereo speaker does not
29+
require an external filter and can be powered directly from a battery.
30+
"""
31+
32+
def prepare(module, options):
33+
module.depends(
34+
":architecture:register",
35+
":architecture:i2c.device",
36+
":processing:resumable")
37+
return True
38+
39+
def build(env):
40+
env.outbasepath = "modm/src/modm/driver/dac"
41+
env.copy("cs43l22.hpp")
42+
env.copy("cs43l22_impl.hpp")

0 commit comments

Comments
 (0)