Arduino library for the Texas Instruments PCMD3180 eight-channel PDM-to-PCM converter with I2C control interface.
Not yet published, still in beta
The PCMD3180 is a 4-port Pulse Density Modulation (PDM) input, 8-channel Pulse Code Modulation (PCM) output audio codec. It accepts up to four PDM microphone pairs (8 physical microphones) and outputs PCM audio over Inter-Integrated Circuit Sound (I2S) or Time-Division Multiplexing (TDM). This library provides an Arduino interface built on the Wire library.
- Utility methods for common setups:
configureAsSlave(),configureAsMaster(),configurePDMInput() - I2S, Left-Justified, and TDM (Time-Division Multiplexed) serial formats
- Configurable word lengths: 16, 20, 24, 32-bit
- Master and slave clock modes, with auto clock detection in slave mode
- Per-channel digital volume control (channels 1–8)
- PDM clock output configuration
- GPIO, GPO, and GPI port configuration
- Granular power control:
powerMICBIAS(),powerPDM(),powerPLL(),wakeUp(),sleep() - Hardware shutdown via SHDNZ pin
- Low-level register access for advanced use
| PCMD3180 | Arduino |
|---|---|
| SDA | SDA |
| SCL | SCL |
| GND | GND |
| AVDD, DVDD | 3.3 V (check datasheet for all supply pins) |
Add 4.7 kΩ pull-up resistors on SDA and SCL if not already present on your board.
Default address is 0x4C. This can be changed via hardware address pins. Check your schematic.
Each PDMIN port carries two microphone channels on one data line, differentiated by clock edge:
| Port | Rising edge | Falling edge |
|---|---|---|
| PDMIN1 | CH1 | CH2 |
| PDMIN2 | CH3 | CH4 |
| PDMIN3 | CH5 | CH6 |
| PDMIN4 | CH7 | CH8 |
The default edge assignment (EDGE_MODE_EVEN_NEGATIVE) samples the even-numbered channel on the falling edge. Most PDM microphones specify which edge their data is valid on. Check your mic's datasheet and use setPortEdgeLatchMode() if you need to change the default.
Connect BCLK, FSYNC, and SDOUT to your MCU or DSP's I2S/TDM input. In slave mode the PCMD3180 follows the clocks your host provides; in master mode it drives them.
- Create a folder named
PCMD3180inside your Arduinolibrariesfolder - Copy
PCMD3180.handPCMD3180.cppinto it - Restart the Arduino IDE
Most setups follow the same three-step pattern after begin():
configureAsSlave()orconfigureAsMaster()— sets the ASI format and channel countconfigurePDMInput()— sets the PDM clock and enables the right portspowerPDM(true)— powers up the PDM block
#include <Wire.h>
#include "PCMD3180.h"
PCMD3180 mic;
void setup() {
Wire.begin();
if (!mic.begin()) {
while (1); // halt on failure
}
mic.configureAsSlave(FORMAT_I2S, WORD_32_BIT, 2);
mic.configurePDMInput(PDMCLK_2822_KHZ, 2);
mic.powerPDM(true);
mic.powerPLL(true);
}The most common setup: two PDM mics on port 1, output over I2S, clocked by the host.
#include <Wire.h>
#include "PCMD3180.h"
PCMD3180 mic;
void setup() {
Wire.begin();
if (!mic.begin()) {
while (1);
}
// I2S, 32-bit words, slave mode, 2 channels
mic.configureAsSlave(FORMAT_I2S, WORD_32_BIT, 2);
// Enable port 1 (CH1 + CH2) at 2.8224 MHz PDM clock
mic.configurePDMInput(PDMCLK_2822_KHZ, 2);
mic.powerPDM(true);
mic.powerPLL(true);
}
void loop() {}Four PDM mics across two ports, output over TDM. Useful when connecting to a DSP that consumes multiple channels on one serial line.
#include <Wire.h>
#include "PCMD3180.h"
PCMD3180 mic;
void setup() {
Wire.begin();
if (!mic.begin()) {
while (1);
}
// TDM, 32-bit, slave mode, 4 channels (ports 1 and 2 enabled automatically)
mic.configureAsSlave(FORMAT_TDM, WORD_32_BIT, 4);
mic.configurePDMInput(PDMCLK_2822_KHZ, 4);
mic.powerPDM(true);
mic.powerPLL(true);
}
void loop() {}Use this when the PCMD3180 is the clock source on the bus, for example when connecting directly to an MCU I2S peripheral in slave mode.
Warning: Master mode has not been tested
#include <Wire.h>
#include "PCMD3180.h"
PCMD3180 mic;
void setup() {
Wire.begin();
if (!mic.begin()) {
while (1);
}
// I2S master at 48 kHz, BCLK = 64 × FSYNC = 3.072 MHz
mic.configureAsMaster(FORMAT_I2S, WORD_32_BIT, FSRATE_48, BCLKRATIO_64, 2);
mic.configurePDMInput(PDMCLK_2822_KHZ, 2);
mic.powerPDM(true);
mic.powerPLL(true);
}
void loop() {}setDigitalVolume() accepts values 0x00–0xFF. 0x00 is approximately mute;
0x7F is unity gain (0 dB); 0xFF is +24 dB.
// Set all channels to unity gain
mic.setAllChannelVolumes(0x7F);
// Or set individual channels
mic.setDigitalVolume(1, 0x7F); // CH1 unity gain
mic.setDigitalVolume(2, 0x60); // CH2 slightly lowerIf your board connects a GPIO to the SHDNZ pin, pass it to the constructor.
The library will assert it low at construction and release it in begin().
#define SHDNZ_PIN 5
PCMD3180 mic(SHDNZ_PIN);
void setup() {
Wire.begin();
mic.begin(); // releases SHDNZ internally
// ...
}The MICBIAS pin can supply power directly to PDM microphones, eliminating the need for an external supply. TI recommends configuring it to track AVDD (rather than VREF) so the PDM signal levels match AVDD directly and no level shifters are needed. MICBIAS is off by default.
void setup() {
Wire.begin();
mic.begin();
mic.configureAsSlave(FORMAT_I2S, WORD_32_BIT, 2);
// Configure MICBIAS to follow AVDD voltage
// VREF_SEL defaults to 2.75 V — set appropriately for your AVDD
mic.configureBIAS(MICBIAS_AVDD, VREF_2V75);
// Power on MICBIAS before the PDM block
mic.powerMICBIAS(true);
mic.configurePDMInput(PDMCLK_2822_KHZ, 2);
mic.powerPDM(true);
mic.powerPLL(true);
}If your AVDD supply is 1.8 V rather than 3.3 V, pass AVDD_INPUT_18V to the constructor.
The library will automatically set VREF to 1.375 V during begin() to keep it below AVDD.
PCMD3180 mic(PCMD3180_SHDNZ_PIN_DEFAULT, PCMD3180_I2C_ADDR_DEFAULT,
false, AVDD_INPUT_18V);Each device needs a unique I2C address (set via hardware address pins). Call begin() on each device individually first, this resets and wakes each one. Then enable broadcast on all of them so subsequent shared configuration writes reach every device simultaneously.
PCMD3180 mic_a(0xFF, 0x4C);
PCMD3180 mic_b(0xFF, 0x4D);
void setup() {
Wire.begin();
// Each device must be initialized individually
mic_a.begin();
mic_b.begin();
// Enable broadcast on all devices before shared configuration
mic_a.setI2CBroadcast(true);
mic_b.setI2CBroadcast(true);
// These calls now go to both devices simultaneously via the broadcast address
mic_a.configureAsSlave(FORMAT_TDM, WORD_32_BIT, 8);
mic_a.configurePDMInput(PDMCLK_2822_KHZ, 8);
mic_a.powerPDM(true);
mic_a.powerPLL(true);
}PCMD3180(uint8_t shdnz_pin = PCMD3180_SHDNZ_PIN_DEFAULT,
uint8_t i2c_addr = PCMD3180_I2C_ADDR_DEFAULT,
bool areg_internal = false,
AVDDInputVoltage avdd = AVDD_INPUT_33V);Pass PCMD3180_SHDNZ_PIN_DEFAULT (0xFF) for shdnz_pin if you are not controlling the shutdown pin from firmware. Pass true for areg_internal if you want to generate the AREG voltage internally in the PCMD3180.
These cover the majority of use cases and are the recommended starting point.
| Method | Description |
|---|---|
begin(wirePort) |
Initialize device, reset, wake. Returns false if not found on I2C. |
configureAsSlave(format, word_len, num_channels) |
Set ASI format and enable channels; device follows host clocks. |
configureAsMaster(format, word_len, fsync_rate, bclk_ratio, num_channels) |
Set ASI format, program clock rates, enable channels; device drives clocks. |
configurePDMInput(clk, num_channels) |
Set PDM clock frequency and enable the required ports and channels. |
enableAllChannels(num_channels) |
Enable the first N input and output channels by count instead of bitmask. |
setAllChannelVolumes(volume) |
Set the same digital volume on all 8 channels. |
| Method | Description |
|---|---|
setDigitalVolume(channel, volume) |
Volume for one channel. 0x00 ≈ mute, 0x7F = 0 dB, 0xFF = +24 dB. |
setGainCalibration(channel, value) |
Fine gain trim per channel (0–15). |
setPhaseCalibration(channel, value) |
Phase offset per channel (0–255). |
enablePort(port, enable) |
Enable or disable a PDM input port (1–4). |
setPortEdgeLatchMode(port, mode) |
Choose which clock edge latches the even-numbered channel. |
setASISlotAssignment(channel, slot) |
Assign a channel to a TDM slot (0–63). |
setASIOutputLine(channel, pin) |
Route a channel to the primary or secondary SDOUT. |
| Method | Description |
|---|---|
powerPDM(enable) |
Power the PDM input block on or off. |
powerMICBIAS(enable) |
Power MICBIAS on or off. |
powerPLL(enable) |
Power the PLL on or off. |
wakeUp() |
Exit sleep mode. |
sleep() |
Enter sleep mode (registers retained). |
hardwarePowerUp() |
Assert SHDNZ pin high (requires pin configured in constructor). |
hardwarePowerDown() |
Assert SHDNZ pin low. |
| Method | Description |
|---|---|
isConnected() |
Returns true if the device acknowledges on I2C. |
getDeviceStatus(status0, status1) |
Read DEV_STS0 and DEV_STS1 registers. |
getLatchedInterruptStatus(asi_error, pll_error) |
Read latched interrupt flags. |
getAutodetectedClocks(fsync, ratio) |
Read auto-detected FSYNC and BCLK/FSYNC ratio (slave mode). |
Use these if you need registers not covered by the higher-level API.
mic.writeRegister(reg, value);
mic.readRegister(reg, value);
mic.updateRegisterBits(reg, mask, value); // read-modify-writeThe PCMD3180 uses register paging (pages 0–4). This library implements page 0 only. To access other pages:
mic.writeRegister(0x00, page_number); // select page
mic.writeRegister(reg, value); // access register on that page
mic.writeRegister(0x00, 0); // return to page 0The volume register uses a linear code where each step is approximately 0.5 dB. Notable values:
| Register value | Decimal | Level |
|---|---|---|
0x00 |
0 | Mute |
0x01 |
1 | –100 dB |
0xC9 |
201 | 0 dB (unity gain, default) |
0xFF |
255 | +27 dB |
Consult the datasheet "Digital Volume Control" section for the full dB-per-step table.
The library handles the timing requirements from the datasheet automatically:
- 10 ms delay after software reset
- 1 ms delay after entering active mode
- 50 ms delay in
hardwarePowerDown()
The PDM clock must be chosen to match your microphone and sample rate. Common pairings:
| Sample rate | PDM clock |
|---|---|
| 48 kHz | PDMCLK_2822_KHZ or PDMCLK_5644_KHZ |
| 44.1 kHz | PDMCLK_2822_KHZ |
| 16 kHz | PDMCLK_1411_KHZ |
Check your microphone's datasheet for its supported PDM clock range.
The following device capabilities have no corresponding library method. All can be accessed using the low-level writeRegister() / readRegister() / updateRegisterBits() calls.
ASI_CFG2 register (0x09) — TX offset
The TX offset field (bits [4:0]) controls how many BCLKs after FSYNC the device waits before transmitting. There is no setTXOffset() method; the default value of 0 (transmit on the first BCLK after FSYNC) is suitable for standard I2S and TDM. If you need a non-zero offset, write directly:
mic.updateRegisterBits(0x09, 0x1F, offset_value);Biquad filter coefficients (pages 1–4)
setBiquadCfg() sets the number of active biquad stages (0–3), but the actual filter coefficients live in registers on pages 1–4 and are not accessible through any library method. To load custom coefficients you need to switch pages manually, write the coefficient registers, and return to page 0:
mic.writeRegister(0x00, 1); // select page 1
mic.writeRegister(coeff_reg, value); // write coefficient
mic.writeRegister(0x00, 0); // return to page 0Refer to the datasheet section "Programmable Biquad Filter" for the register map and coefficient format.
I2C checksum (REG_I2C_CKSUM, 0x7E)
The hardware checksum feature for verifying I2C writes is not exposed. setI2CBroadcast() uses this register internally to set the broadcast bit, but the checksum enable bit itself has no dedicated method.
Device not found (begin() returns false)
- Check SDA/SCL wiring and pull-up resistors
- Verify the I2C address matches your hardware (default 0x4C)
- Try
Wire.setClock(100000)to lower the bus speed
No audio output
- Confirm
powerPDM(true)was called - Verify PDM microphone wiring and that the correct PDM clock frequency is set
- Check that both input channels (
enableChannels) and output channels (enableOutputASIChannels) are enabled —configurePDMInput()andenableAllChannels()handle both together - Verify BCLK and FSYNC are present on the ASI pins (use a scope or logic analyser)
Distorted or clipped audio
- Lower the digital volume with
setDigitalVolume()orsetAllChannelVolumes() - Verify the PDM clock frequency is appropriate for your sample rate
Some channels are not synchronized to the others
- Ensure that the number of enabled biquads per channel is less or equal to what the PCDM3180 supports. See the section
Programmable Digital Biquad Filtersin the datasheet.
This library is provided under an MIT license. You are permitted to copy or modify this code, and you are permitted to use it in commercial products.
Magne Lauritzen / Summa Cogni