Skip to content

Commit 3db0a3d

Browse files
committed
[driver] Add MAX14661 analog MUX driver
1 parent f57e1d3 commit 3db0a3d

4 files changed

Lines changed: 316 additions & 7 deletions

File tree

README.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,55 +1013,56 @@ your specific needs.
10131013
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ltc2499">LTC2499</a></td>
10141014
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ltc2984">LTC2984</a></td>
10151015
</tr><tr>
1016+
<td align="center"><a href="https://modm.io/reference/module/modm-driver-max14661">MAX14661</a></td>
10161017
<td align="center"><a href="https://modm.io/reference/module/modm-driver-max31855">MAX31855</a></td>
10171018
<td align="center"><a href="https://modm.io/reference/module/modm-driver-max31865">MAX31865</a></td>
10181019
<td align="center"><a href="https://modm.io/reference/module/modm-driver-max6966">MAX6966</a></td>
10191020
<td align="center"><a href="https://modm.io/reference/module/modm-driver-max7219">MAX7219</a></td>
10201021
<td align="center"><a href="https://modm.io/reference/module/modm-driver-mcp23x08">MCP23X08</a></td>
1021-
<td align="center"><a href="https://modm.io/reference/module/modm-driver-mcp23x17">MCP23x17</a></td>
10221022
</tr><tr>
1023+
<td align="center"><a href="https://modm.io/reference/module/modm-driver-mcp23x17">MCP23x17</a></td>
10231024
<td align="center"><a href="https://modm.io/reference/module/modm-driver-mcp2515">MCP2515</a></td>
10241025
<td align="center"><a href="https://modm.io/reference/module/modm-driver-mcp3008">MCP3008</a></td>
10251026
<td align="center"><a href="https://modm.io/reference/module/modm-driver-mcp7941x">MCP7941x</a></td>
10261027
<td align="center"><a href="https://modm.io/reference/module/modm-driver-mcp990x">MCP990X</a></td>
10271028
<td align="center"><a href="https://modm.io/reference/module/modm-driver-mmc5603">MMC5603</a></td>
1028-
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ms5611">MS5611</a></td>
10291029
</tr><tr>
1030+
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ms5611">MS5611</a></td>
10301031
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ms5837">MS5837</a></td>
10311032
<td align="center"><a href="https://modm.io/reference/module/modm-driver-nokia5110">NOKIA5110</a></td>
10321033
<td align="center"><a href="https://modm.io/reference/module/modm-driver-nrf24">NRF24</a></td>
10331034
<td align="center"><a href="https://modm.io/reference/module/modm-driver-parallel_tft_display">TFT-DISPLAY</a></td>
10341035
<td align="center"><a href="https://modm.io/reference/module/modm-driver-pat9125el">PAT9125EL</a></td>
1035-
<td align="center"><a href="https://modm.io/reference/module/modm-driver-pca8574">PCA8574</a></td>
10361036
</tr><tr>
1037+
<td align="center"><a href="https://modm.io/reference/module/modm-driver-pca8574">PCA8574</a></td>
10371038
<td align="center"><a href="https://modm.io/reference/module/modm-driver-pca9535">PCA9535</a></td>
10381039
<td align="center"><a href="https://modm.io/reference/module/modm-driver-pca9548a">PCA9548A</a></td>
10391040
<td align="center"><a href="https://modm.io/reference/module/modm-driver-pca9685">PCA9685</a></td>
10401041
<td align="center"><a href="https://modm.io/reference/module/modm-driver-pcal6524">PCAL6524</a></td>
10411042
<td align="center"><a href="https://modm.io/reference/module/modm-driver-qmc5883l">QMC5883L</a></td>
1042-
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sh1106">SH1106</a></td>
10431043
</tr><tr>
1044+
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sh1106">SH1106</a></td>
10441045
<td align="center"><a href="https://modm.io/reference/module/modm-driver-siemens_s65">SIEMENS-S65</a></td>
10451046
<td align="center"><a href="https://modm.io/reference/module/modm-driver-siemens_s75">SIEMENS-S75</a></td>
10461047
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sk6812">SK6812</a></td>
10471048
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sk9822">SK9822</a></td>
10481049
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ssd1306">SSD1306</a></td>
1049-
<td align="center"><a href="https://modm.io/reference/module/modm-driver-st7586s">ST7586S</a></td>
10501050
</tr><tr>
1051+
<td align="center"><a href="https://modm.io/reference/module/modm-driver-st7586s">ST7586S</a></td>
10511052
<td align="center"><a href="https://modm.io/reference/module/modm-driver-st7789">ST7789</a></td>
10521053
<td align="center"><a href="https://modm.io/reference/module/modm-driver-stts22h">STTS22H</a></td>
10531054
<td align="center"><a href="https://modm.io/reference/module/modm-driver-stusb4500">STUSB4500</a></td>
10541055
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sx1276">SX1276</a></td>
10551056
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sx128x">SX128X</a></td>
1056-
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tcs3414">TCS3414</a></td>
10571057
</tr><tr>
1058+
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tcs3414">TCS3414</a></td>
10581059
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tcs3472">TCS3472</a></td>
10591060
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tlc594x">TLC594x</a></td>
10601061
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tmp102">TMP102</a></td>
10611062
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tmp12x">TMP12x</a></td>
10621063
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tmp175">TMP175</a></td>
1063-
<td align="center"><a href="https://modm.io/reference/module/modm-driver-touch2046">TOUCH2046</a></td>
10641064
</tr><tr>
1065+
<td align="center"><a href="https://modm.io/reference/module/modm-driver-touch2046">TOUCH2046</a></td>
10651066
<td align="center"><a href="https://modm.io/reference/module/modm-driver-vl53l0">VL53L0</a></td>
10661067
<td align="center"><a href="https://modm.io/reference/module/modm-driver-vl6180">VL6180</a></td>
10671068
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ws2812">WS2812</a></td>

src/modm/driver/mux/max14661.hpp

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* Copyright (c) 2026, Niklas Hauser
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+
#pragma once
13+
14+
#include <modm/architecture/interface/register.hpp>
15+
#include "max14661_transport.hpp"
16+
17+
namespace modm
18+
{
19+
20+
/// @ingroup modm_driver_max14661
21+
struct max14661
22+
{
23+
static inline uint8_t address(uint8_t bits = 0)
24+
{ return 0x4C | (bits & 0b11); }
25+
26+
/// The 16 AB_ pins that can be routed to COMA and/or COMB.
27+
enum class
28+
Channel : uint16_t
29+
{
30+
Ch1 = Bit0,
31+
Ch2 = Bit1,
32+
Ch3 = Bit2,
33+
Ch4 = Bit3,
34+
Ch5 = Bit4,
35+
Ch6 = Bit5,
36+
Ch7 = Bit6,
37+
Ch8 = Bit7,
38+
Ch9 = Bit8,
39+
Ch10 = Bit9,
40+
Ch11 = Bit10,
41+
Ch12 = Bit11,
42+
Ch13 = Bit12,
43+
Ch14 = Bit13,
44+
Ch15 = Bit14,
45+
Ch16 = Bit15,
46+
};
47+
MODM_FLAGS16(Channel);
48+
};
49+
50+
/**
51+
* MAX14661 Beyond-the-Rails 16:2 analog multiplexer.
52+
*
53+
* A serially controlled, dual-channel analog multiplexer allowing any of
54+
* the 16 AB pins to be connected to either common pin (COMA, COMB)
55+
* simultaneously in any combination. Beyond-the-Rails technology allows
56+
* switching ±5.5V signals from a single +1.6V to +5.5V supply.
57+
*
58+
* Channels are addressed with the `max14661::Channel` flags, which can be
59+
* combined with `|` to connect multiple channels to the same common pin
60+
* at once:
61+
*
62+
* @code
63+
* modm::Max14661<Max14661TransportI2c<MyI2cMaster>> mux;
64+
* mux.setChannelsA(max14661::Channel::Ch1 | max14661::Channel::Ch5);
65+
* mux.setChannelsB(max14661::Channel::Ch2);
66+
* @endcode
67+
*
68+
* @tparam Transport Either `Max14661TransportI2c` or `Max14661TransportSpi`.
69+
*
70+
* @see Max14661TransportI2c
71+
* @see Max14661TransportSpi
72+
*
73+
* @ingroup modm_driver_max14661
74+
* @author Niklas Hauser
75+
*/
76+
template < class Transport >
77+
class Max14661 : public max14661, public Transport
78+
{
79+
public:
80+
/// Constructor.
81+
/// @param address I2C slave address (7bit, unshifted), default is A1=A0=0.
82+
/// Ignored when used with `Max14661TransportSpi`.
83+
Max14661(uint8_t address = max14661::address())
84+
: Transport(address) {}
85+
86+
/// Connect the given channels to COMA, disconnecting all others on
87+
/// this bank.
88+
bool inline
89+
setChannelsA(Channel_t channels)
90+
{ return Transport::writeChannelsA(channels.value); }
91+
92+
/// Connect the given channels to COMB, disconnecting all others on
93+
/// this bank.
94+
bool inline
95+
setChannelsB(Channel_t channels)
96+
{ return Transport::writeChannelsB(channels.value); }
97+
98+
/// Disconnect all channels from COMA.
99+
bool inline
100+
disableChannelsA()
101+
{ return setChannelsA(Channel_t()); }
102+
103+
/// Disconnect all channels from COMB.
104+
bool inline
105+
disableChannelsB()
106+
{ return setChannelsB(Channel_t()); }
107+
};
108+
109+
} // namespace modm

src/modm/driver/mux/max14661.lb

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright (c) 2026, Niklas Hauser
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:max14661"
16+
module.description = """\
17+
# MAX14661 Beyond-the-Rails 16:2 Multiplexer
18+
19+
The MAX14661 is a serially controlled, dual-channel analog multiplexer
20+
allowing any of the 16 AB pins to be connected to either common pin
21+
(COMA, COMB) simultaneously in any combination. The device features
22+
Beyond-the-Rails capability so that ±5.5V signals can be passed with any
23+
single supply between +1.6V and +5.5V.
24+
25+
The serial control is selectable between I2C and SPI, both of which are
26+
implemented as separate transport layers so the same driver can be used
27+
with either bus.
28+
"""
29+
30+
def prepare(module, options):
31+
module.depends(
32+
":architecture:register",
33+
":architecture:i2c.device",
34+
":architecture:spi.device",
35+
":architecture:fiber")
36+
return True
37+
38+
def build(env):
39+
env.outbasepath = "modm/src/modm/driver/mux"
40+
env.copy("max14661.hpp")
41+
env.copy("max14661_transport.hpp")
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
* Copyright (c) 2026, Niklas Hauser
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+
#pragma once
13+
14+
#include <modm/architecture/interface/spi_device.hpp>
15+
#include <modm/architecture/interface/i2c_device.hpp>
16+
#include <modm/processing/fiber.hpp>
17+
18+
namespace modm
19+
{
20+
21+
/**
22+
* MAX14661 I2C Transport Layer.
23+
*
24+
* In I2C mode the switches of each bank are updated through the shadow
25+
* registers: the desired 16-bit channel mask is written into the two
26+
* shadow registers of a bank, and then the "copy shadow registers to
27+
* switches" command is issued for that bank. There is no command that
28+
* copies both banks at once, so bank A and bank B always have to be
29+
* updated with two separate register writes.
30+
*
31+
* The I2C interface is compliant with Fast Mode (up to 400kHz). The slave
32+
* address is configured by the A0/A1 pins.
33+
*
34+
* @see Max14661
35+
*
36+
* @ingroup modm_driver_max14661
37+
* @author Niklas Hauser
38+
*/
39+
template < class I2cMaster >
40+
class Max14661TransportI2c : public modm::I2cDevice< I2cMaster >
41+
{
42+
public:
43+
Max14661TransportI2c(uint8_t address) : I2cDevice<I2cMaster>(address) {}
44+
45+
protected:
46+
/// Write the 16-bit channel mask into the bank A shadow registers
47+
/// and copy them to the bank A switches.
48+
bool
49+
writeChannelsA(uint16_t mask)
50+
{
51+
// SHDW0/SHDW1 = 0x10/0x11, CMD_A = 0x14
52+
return writeShadow(0x10, 0x14, mask);
53+
}
54+
55+
/// Write the 16-bit channel mask into the bank B shadow registers
56+
/// and copy them to the bank B switches.
57+
bool
58+
writeChannelsB(uint16_t mask)
59+
{
60+
// SHDW2/SHDW3 = 0x12/0x13, CMD_B = 0x15
61+
return writeShadow(0x12, 0x15, mask);
62+
}
63+
64+
private:
65+
bool
66+
writeShadow(uint8_t shadowRegister, uint8_t commandRegister, uint16_t mask)
67+
{
68+
// SHDWx: low byte, SHDWx+1: high byte, auto-incrementing register address
69+
buffer[0] = shadowRegister;
70+
buffer[1] = uint8_t(mask);
71+
buffer[2] = uint8_t(mask >> 8);
72+
if (not this->write(buffer, 3))
73+
return false;
74+
75+
// 0b10001 = copy shadow registers of this bank to the switches
76+
buffer[0] = commandRegister;
77+
buffer[1] = 0b10001;
78+
return this->write(buffer, 2);
79+
}
80+
81+
uint8_t buffer[3];
82+
};
83+
84+
/**
85+
* MAX14661 SPI Transport Layer.
86+
*
87+
* In SPI mode the MAX14661 has no addressable registers: it is a plain
88+
* 32-bit shift register and all 32 switches (16 of bank A, 16 of bank B)
89+
* are transitioned simultaneously on the rising edge of CS. This transport
90+
* therefore caches both channel masks locally and always shifts out the
91+
* complete 32-bit word, so that updating one bank does not disturb the
92+
* other.
93+
*
94+
* The SPI interface requires Mode3 and can be clocked with up to ~10MHz.
95+
*
96+
* @see Max14661
97+
*
98+
* @tparam Cs connected chip select pin
99+
*
100+
* @ingroup modm_driver_max14661
101+
* @author Niklas Hauser
102+
*/
103+
template < class SpiMaster, class Cs >
104+
class Max14661TransportSpi : public modm::SpiDevice< SpiMaster >
105+
{
106+
public:
107+
Max14661TransportSpi(uint8_t)
108+
{
109+
Cs::setOutput(modm::Gpio::High);
110+
}
111+
112+
protected:
113+
/// Set the bank A channel mask and shift the complete 32-bit switch
114+
/// state into the device.
115+
bool
116+
writeChannelsA(uint16_t mask)
117+
{
118+
maskA = mask;
119+
return writeShiftRegister();
120+
}
121+
122+
/// Set the bank B channel mask and shift the complete 32-bit switch
123+
/// state into the device.
124+
bool
125+
writeChannelsB(uint16_t mask)
126+
{
127+
maskB = mask;
128+
return writeShiftRegister();
129+
}
130+
131+
private:
132+
bool
133+
writeShiftRegister()
134+
{
135+
modm::this_fiber::poll([&]{ return this->acquireMaster(); });
136+
Cs::reset();
137+
138+
// Table 4: SW16B..SW09B, SW08B..SW01B, SW16A..SW09A, SW08A..SW01A
139+
const uint8_t buffer[4]
140+
{
141+
uint8_t(maskB >> 8),
142+
uint8_t(maskB),
143+
uint8_t(maskA >> 8),
144+
uint8_t(maskA),
145+
};
146+
SpiMaster::transfer(buffer, nullptr, 4);
147+
148+
if (this->releaseMaster())
149+
Cs::set();
150+
151+
return true;
152+
}
153+
154+
uint16_t maskA{0};
155+
uint16_t maskB{0};
156+
};
157+
158+
} // namespace modm

0 commit comments

Comments
 (0)