Skip to content

Commit 096e90b

Browse files
committed
[driver] Add MA12070P audio dac (I2S + I²C)
1 parent 4bd6d60 commit 096e90b

File tree

3 files changed

+570
-0
lines changed

3 files changed

+570
-0
lines changed

src/modm/driver/dac/ma12070p.hpp

Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
/*
2+
* Copyright (c) 2021 - 2022, Raphael Lehmann
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_MA12070P_HPP
13+
#define MODM_MA12070P_HPP
14+
15+
#include <modm/architecture/interface/i2c_device.hpp>
16+
#include <optional>
17+
18+
namespace modm
19+
{
20+
21+
/// @ingroup modm_driver_ma12070p
22+
struct ma12070p
23+
{
24+
enum class
25+
Register : uint8_t
26+
{
27+
PowerModeControl = 0x00,
28+
ThresholdForPowerModeChange12 = 0x01,
29+
ThresholdForPowerModeChange21 = 0x02,
30+
ThresholdForPowerModeChange23 = 0x03,
31+
ThresholdForPowerModeChange32 = 0x04,
32+
DisableAmplifier = 0x07,
33+
SoftClippingAndOcpLatching = 0x0a,
34+
SelectPowerModeProfile = 0x1d,
35+
PowerModeProfileConfiguration = 0x1e,
36+
OcpLatchClear = 0x20,
37+
AudioInputMode = 0x25,
38+
DcProtection = 0x26,
39+
AudioInputModeOverwrite = 0x27,
40+
ErrorHandlerClear = 0x2d,
41+
PcmFormat = 0x35,
42+
PcmConfiguration = 0x36,
43+
LimiterConfiguration = 0x35,
44+
MuteAndLimiterMux = 0x36,
45+
MasterVolumeDbInteger = 0x40,
46+
MasterVolumeDbFract = 0x41,
47+
Ch0LVolumeDbInteger = 0x42,
48+
Ch0RVolumeDbInteger = 0x43,
49+
Ch1LVolumeDbInteger = 0x44,
50+
Ch1RVolumeDbInteger = 0x45,
51+
ChxxVolumeDbFract = 0x46,
52+
Ch0LLimiterDbfsInteger = 0x47,
53+
Ch0RLimiterDbfsInteger = 0x48,
54+
Ch1LLimiterDbfsInteger = 0x49,
55+
Ch1RLimiterDbfsInteger = 0x4a,
56+
ChxxLimiterDbfsFract = 0x4b,
57+
VolumeClippingIndicator = 0x7e,
58+
MonitorCh0FreqPower = 0x60,
59+
MonitorCh0 = 0x61,
60+
MonitorCh0ModulationIndex = 0x62,
61+
MonitorCh1FreqPower = 0x64,
62+
MonitorCh1 = 0x65,
63+
MonitorCh1ModulationIndex = 0x66,
64+
ErrorAccumulated = 0x6d,
65+
MonitorMsel = 0x75,
66+
Error = 0x7c,
67+
VlpMonitor = 0x7e,
68+
};
69+
70+
71+
enum class
72+
PcmWordFormat : uint8_t
73+
{
74+
I2s = 0b000,
75+
LeftJustifed = 0b001, /// default
76+
RightJustifed16b = 0b100,
77+
RightJustifed18b = 0b101,
78+
RightJustifed20b = 0b110,
79+
RightJustifed24b = 0b111,
80+
};
81+
82+
enum class
83+
ClockPolarity : uint8_t
84+
{
85+
FallingEdge = 0b0, /// Capture data at the falling edge of the serial clock signal SCK
86+
RisingEdge = 0b1, /// Capture data at the rising edge of the serial clock signal SCK
87+
};
88+
89+
enum class
90+
FrameSize : uint8_t
91+
{
92+
Bits64 = (0b00 << 3),
93+
Bits48 = (0b01 << 3),
94+
Bits32 = (0b10 << 3),
95+
//Reserved = (0b11 << 3),
96+
};
97+
98+
enum class
99+
WordSelectPolarity : uint8_t
100+
{
101+
Low = (0b0 << 1), /// First word of a simultaneously sampled PCM data pair is transmitted while word select (WS) is low.
102+
High = (0b1 << 1), /// First word of a simultaneously sampled PCM data pair is transmitted while word select (WS) is high.
103+
};
104+
105+
enum class
106+
DataOrder : uint8_t
107+
{
108+
MsbFirst = (0b0 << 2),
109+
LsbFirst = (0b1 << 2),
110+
};
111+
112+
enum class
113+
RightLeftOrder : uint8_t
114+
{
115+
LeftFirst = (0b0 << 5),
116+
RightFirst = (0b1 << 5),
117+
};
118+
119+
enum class
120+
LimiterTiming : uint8_t
121+
{
122+
Slow = 0b00,
123+
Normal = 0b01,
124+
Fast = 0b10,
125+
};
126+
127+
struct I2sAndVlpConfig
128+
{
129+
const PcmWordFormat pcmWordFormat = PcmWordFormat::LeftJustifed;
130+
const ClockPolarity clockPolarity = ClockPolarity::FallingEdge;
131+
const FrameSize frameSize = FrameSize::Bits64;
132+
const WordSelectPolarity wordSelectPolarity = WordSelectPolarity::Low;
133+
const DataOrder dataOrder = DataOrder::MsbFirst;
134+
const RightLeftOrder rightLeftOrder = RightLeftOrder::LeftFirst;
135+
const bool useVlp = true;
136+
const bool useLimiter = true;
137+
const LimiterTiming limiterReleaseTime = LimiterTiming::Normal;
138+
const LimiterTiming limiterAttackTime = LimiterTiming::Normal;
139+
};
140+
141+
enum class
142+
VlpMonitor : uint8_t
143+
{
144+
LimiterCh0L = Bit0,
145+
LimiterCh0R = Bit1,
146+
LimiterCh1L = Bit2,
147+
LimiterCh1R = Bit3,
148+
ClippingCh0L = Bit4,
149+
ClippingCh0R = Bit5,
150+
ClippingCh1L = Bit6,
151+
ClippingCh1R = Bit7,
152+
};
153+
MODM_FLAGS8(VlpMonitor);
154+
155+
#if MODM_HAS_IOSTREAM
156+
friend IOStream&
157+
operator << (IOStream& os, const VlpMonitor_t& c)
158+
{
159+
os << "VlpMonitor(";
160+
if(c & VlpMonitor::LimiterCh0L)
161+
os << "LimiterCh0L ";
162+
if(c & VlpMonitor::LimiterCh0R)
163+
os << "LimiterCh0R ";
164+
if(c & VlpMonitor::LimiterCh1L)
165+
os << "LimiterCh1L ";
166+
if(c & VlpMonitor::LimiterCh1R)
167+
os << "LimiterCh1R ";
168+
if(c & VlpMonitor::ClippingCh0L)
169+
os << "ClippingCh0L ";
170+
if(c & VlpMonitor::ClippingCh0R)
171+
os << "ClippingCh0R ";
172+
if(c & VlpMonitor::ClippingCh1L)
173+
os << "ClippingCh1L ";
174+
if(c & VlpMonitor::ClippingCh1R)
175+
os << "ClippingCh1R ";
176+
os << ")";
177+
return os;
178+
}
179+
#endif
180+
181+
enum class
182+
ErrorRegister : uint8_t
183+
{
184+
FlyingCapOverVolt = Bit0,
185+
OverCurrent = Bit1,
186+
Pll = Bit2,
187+
PvddUnderVolt = Bit3,
188+
OverTempWarning = Bit4,
189+
OverTempError = Bit5,
190+
PinToPinLowImpedance = Bit6,
191+
DcProtection = Bit7,
192+
};
193+
MODM_FLAGS8(ErrorRegister);
194+
195+
#if MODM_HAS_IOSTREAM
196+
friend IOStream&
197+
operator << (IOStream& os, const ErrorRegister_t& c)
198+
{
199+
os << "ErrorRegister(";
200+
if(c & ErrorRegister::FlyingCapOverVolt)
201+
os << "FlyingCapOverVolt ";
202+
if(c & ErrorRegister::OverCurrent)
203+
os << "OverCurrent ";
204+
if(c & ErrorRegister::Pll)
205+
os << "Pll ";
206+
if(c & ErrorRegister::PvddUnderVolt)
207+
os << "PvddUnderVolt ";
208+
if(c & ErrorRegister::OverTempWarning)
209+
os << "OverTempWarning ";
210+
if(c & ErrorRegister::OverTempError)
211+
os << "OverTempError ";
212+
if(c & ErrorRegister::PinToPinLowImpedance)
213+
os << "PinToPinLowImpedance ";
214+
if(c & ErrorRegister::DcProtection)
215+
os << "DcProtection ";
216+
os << ")";
217+
return os;
218+
}
219+
#endif
220+
221+
222+
// quarter_decibel type
223+
enum class
224+
quarter_decibel_t : int16_t
225+
{};
226+
227+
template<typename T>
228+
static constexpr quarter_decibel_t
229+
quarter_decibel(T value)
230+
{
231+
return quarter_decibel_t(int16_t(value * 4ul));
232+
}
233+
234+
static constexpr float
235+
quarterDecibelToFloat(quarter_decibel_t value)
236+
{
237+
return int16_t(value) / 4.f;
238+
}
239+
240+
static constexpr quarter_decibel_t MaxVolume = static_cast<quarter_decibel_t>(24*4);
241+
static constexpr quarter_decibel_t MinVolume = static_cast<quarter_decibel_t>(-144*4);
242+
// static constexpr quarter_decibel_t MaxVolume = quarter_decibel(24);
243+
// static constexpr quarter_decibel_t MinVolume = quarter_decibel(-144);
244+
}; // struct ma12070p
245+
246+
constexpr ma12070p::quarter_decibel_t
247+
operator-(ma12070p::quarter_decibel_t v)
248+
{
249+
return static_cast<ma12070p::quarter_decibel_t>(-static_cast<int16_t>(v));
250+
}
251+
252+
namespace literals
253+
{
254+
constexpr ma12070p::quarter_decibel_t
255+
operator""_q_db(long double value)
256+
{
257+
return ma12070p::quarter_decibel(value);
258+
}
259+
260+
constexpr ma12070p::quarter_decibel_t
261+
operator""_q_db(unsigned long long int value)
262+
{
263+
return ma12070p::quarter_decibel(value);
264+
}
265+
}
266+
267+
/**
268+
* Infineon MA12070P, Portable Audio DAC with Integrated Class D Speaker Driver
269+
*
270+
* @tparam I2cMaster I2C interface
271+
*
272+
* @author Raphael Lehmann
273+
* @ingroup modm_driver_ma12070p
274+
*/
275+
template<typename I2cMaster>
276+
class Ma12070p : public ma12070p, public modm::I2cDevice<I2cMaster, 5>
277+
{
278+
public:
279+
/**
280+
* Constructor.
281+
*
282+
* \param i2cAddress The I²C address depends on the AD1 and AD0 pins:
283+
* AD1=0, AD0=0 -> 0x20
284+
* AD1=0, AD0=1 -> 0x21
285+
* AD1=1, AD0=0 -> 0x22
286+
* AD1=1, AD0=1 -> 0x23
287+
*/
288+
Ma12070p(uint8_t i2cAddress);
289+
290+
/// Initialize device, call before using any other function
291+
ResumableResult<bool>
292+
initialize();
293+
294+
/// Configure I2S input format and the volume and limiter processor (VLP)
295+
ResumableResult<bool>
296+
configureI2sAndVlp(I2sAndVlpConfig config);
297+
298+
/// Set the limiter treshold for individual channels (-144db to +24db)
299+
ResumableResult<bool>
300+
setLimiterTreshold(quarter_decibel_t ch0l, quarter_decibel_t ch0r, quarter_decibel_t ch1l = -144, quarter_decibel_t ch1r = -144);
301+
302+
/// Set the master volume (-144db to +24db)
303+
ResumableResult<bool>
304+
setMasterVolume(quarter_decibel_t volume);
305+
306+
/// Set the volume for individual channels (-144db to +24db)
307+
ResumableResult<bool>
308+
setChannelVolume(quarter_decibel_t ch0l, quarter_decibel_t ch0r, quarter_decibel_t ch1l = -144, quarter_decibel_t ch1r = -144);
309+
310+
/// Read if limiters are active or if clipping occurs on the VLP output signals
311+
ResumableResult<std::optional<VlpMonitor_t>>
312+
readVlpMonitor();
313+
314+
/// Disable amplifier channels
315+
/// true means disabled
316+
ResumableResult<bool>
317+
disableAmplifier(bool ch0, bool ch1);
318+
319+
/// Enable DC protection. Default on.
320+
ResumableResult<bool>
321+
enableDcProtection(bool enable = true);
322+
323+
/// Read error monitor register. Gives the live status of every potential error source.
324+
ResumableResult<std::optional<ErrorRegister_t>>
325+
readErrors();
326+
327+
/// Read error monitor register.
328+
/// Gives the accumulated status of every potential error source.
329+
/// This register should be cleared by using `clearErrorHandler()`.
330+
ResumableResult<std::optional<ErrorRegister_t>>
331+
readAccumulatedErrors();
332+
333+
/// Clears the error handler
334+
ResumableResult<bool>
335+
clearErrorHandler();
336+
337+
private:
338+
uint8_t tx_buffer[6];
339+
uint8_t rx_buffer;
340+
bool success;
341+
342+
ResumableResult<bool>
343+
writeRegister(Register reg, uint8_t value);
344+
345+
ResumableResult<std::optional<uint8_t>>
346+
readRegister(Register reg);
347+
};
348+
349+
} // namespace modm
350+
351+
#include "ma12070p_impl.hpp"
352+
353+
#endif // MODM_MA12070P_HPP
354+

src/modm/driver/dac/ma12070p.lb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright (c) 2021 - 2022, Raphael Lehmann
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:ma12070p"
16+
module.description = """\
17+
# MA12070P Filterless and High-Efficiency Audio Amplifier with I2S Digital Input
18+
19+
The MA12070P is a super-efficient audio power amplifier based on proprietary
20+
multi-level switching technology. It supports a 4-26V supply voltage range,
21+
allowing it to be used in many different applications.
22+
23+
Datasheet: https://www.infineon.com/dgdl/Infineon-MA12070P-DS-v01_00-EN.pdf?fileId=5546d46264a8de7e0164b761f2f261e4
24+
25+
"""
26+
27+
def prepare(module, options):
28+
module.depends(
29+
":architecture:register",
30+
":architecture:i2c.device",
31+
":processing:resumable")
32+
return True
33+
34+
def build(env):
35+
env.outbasepath = "modm/src/modm/driver/dac"
36+
env.copy("ma12070p.hpp")
37+
env.copy("ma12070p_impl.hpp")

0 commit comments

Comments
 (0)