MAX11300 20-Port Programmable Mixed-Signal I/O Module
Device driver for MAX11300 PIXI - 20-port ADC/DAC/GPIO device. Highly opinionated implementation optimized for Eurorack modular synthesis.
This is a full wrapper around libDaisy's MAX11300 C++ driver, supporting:
- 20 configurable pins (ADC, DAC, GPIO input/output)
- DMA-based continuous updates via Start() method
- Multiple voltage ranges (-10V to +10V)
- Up to 4 devices on shared SPI bus (multi-slave)
⚠️ IMPORTANT: This implementation wraps libDaisy's C++ driver but has NOT been tested on hardware. It follows libDaisy's proven code and compiles successfully, but should be considered experimental until hardware validation is complete.
Example:
var pixi: MAX11300[1] # Single device var config: MAX11300Config[1] config.transport_config.pin_config.defaults() if pixi.init(config) != MAX_OK: # Handle error # Configure pins discard pixi.configurePinAsAnalogRead(0, PIN_0, ADC_NEG5_TO_5) discard pixi.configurePinAsAnalogWrite(0, PIN_1, DAC_NEG5_TO_5) # Start DMA updates (optional) discard pixi.start(nil, nil) # Read/write let cvIn = pixi.readAnalogPinVolts(0, PIN_0) pixi.writeAnalogPinVolts(0, PIN_1, cvIn)
Types
AdcVoltageRange {.importcpp: "daisy::MAX11300Types::AdcVoltageRange", size: 4.} = enum ADC_0_TO_10 = "daisy::MAX11300Types::AdcVoltageRange::ZERO_TO_10", ADC_NEG5_TO_5 = "daisy::MAX11300Types::AdcVoltageRange::NEGATIVE_5_TO_5", ADC_NEG10_TO_0 = "daisy::MAX11300Types::AdcVoltageRange::NEGATIVE_10_TO_0", ADC_0_TO_2P5 = "daisy::MAX11300Types::AdcVoltageRange::ZERO_TO_2P5"
- ADC voltage ranges (assumes proper power supply) WARNING: DigitalRead pins are 0-5V only and corrupted by negative voltages Source Edit
DacVoltageRange {.importcpp: "daisy::MAX11300Types::DacVoltageRange", size: 4.} = enum DAC_0_TO_10 = "daisy::MAX11300Types::DacVoltageRange::ZERO_TO_10", DAC_NEG5_TO_5 = "daisy::MAX11300Types::DacVoltageRange::NEGATIVE_5_TO_5", DAC_NEG10_TO_0 = "daisy::MAX11300Types::DacVoltageRange::NEGATIVE_10_TO_0"
- DAC voltage ranges (assumes proper power supply) DigitalWrite pins are 0-5V only Source Edit
MAX11300[N] {.bycopy, nodecl.} = object
-
MAX11300 device wrapper (currently supports N=1 only)
Note: The libDaisy C++ template is complex to wrap with multiple devices. This wrapper is hard-coded for single device (N=1). Multi-device support would require additional Nim/C++ template instantiation work.
Source Edit MAX11300Config {.importcpp: "daisy::MAX11300<1>::Config", bycopy.} = object transport_config* {.importc: "transport_config".}: MAX11300TransportConfig
- MAX11300 device configuration (currently supports N=1 only) Source Edit
MAX11300Cpp {.importcpp: "daisy::MAX11300<1>".} = object
- C++ MAX11300 driver object (opaque wrapper) Source Edit
MAX11300DmaBuffer {.importcpp: "daisy::MAX11300Types::DmaBuffer", bycopy.} = object rx_buffer* {.importc: "rx_buffer".}: array[41, uint8] tx_buffer* {.importc: "tx_buffer".}: array[41, uint8]
- DMA buffer for SPI transfers - must be in non-cached memory Source Edit
MAX11300Pin {.importcpp: "daisy::MAX11300Types::Pin", size: 4.} = enum PIN_0 = "daisy::MAX11300Types::Pin::PIN_0", PIN_1 = "daisy::MAX11300Types::Pin::PIN_1", PIN_2 = "daisy::MAX11300Types::Pin::PIN_2", PIN_3 = "daisy::MAX11300Types::Pin::PIN_3", PIN_4 = "daisy::MAX11300Types::Pin::PIN_4", PIN_5 = "daisy::MAX11300Types::Pin::PIN_5", PIN_6 = "daisy::MAX11300Types::Pin::PIN_6", PIN_7 = "daisy::MAX11300Types::Pin::PIN_7", PIN_8 = "daisy::MAX11300Types::Pin::PIN_8", PIN_9 = "daisy::MAX11300Types::Pin::PIN_9", PIN_10 = "daisy::MAX11300Types::Pin::PIN_10", PIN_11 = "daisy::MAX11300Types::Pin::PIN_11", PIN_12 = "daisy::MAX11300Types::Pin::PIN_12", PIN_13 = "daisy::MAX11300Types::Pin::PIN_13", PIN_14 = "daisy::MAX11300Types::Pin::PIN_14", PIN_15 = "daisy::MAX11300Types::Pin::PIN_15", PIN_16 = "daisy::MAX11300Types::Pin::PIN_16", PIN_17 = "daisy::MAX11300Types::Pin::PIN_17", PIN_18 = "daisy::MAX11300Types::Pin::PIN_18", PIN_19 = "daisy::MAX11300Types::Pin::PIN_19"
- Represents a pin/port on the MAX11300 (20 pins total) Source Edit
MAX11300Result {.importcpp: "daisy::MAX11300Types::Result", size: 4.} = enum MAX_OK = "daisy::MAX11300Types::Result::OK", MAX_ERR = "daisy::MAX11300Types::Result::ERR"
- Operation result codes Source Edit
MAX11300TransportConfig {.importcpp: "daisy::MAX11300MultiSlaveSpiTransport::Config<1>", bycopy.} = object pin_config* {.importc: "pin_config".}: MAX11300TransportPinConfig periph* {.importc: "periph".}: SpiPeripheral baud_prescaler* {.importc: "baud_prescaler".}: SpiBaudPrescaler
- Transport layer configuration Source Edit
MAX11300TransportPinConfig {.importcpp: "daisy::MAX11300MultiSlaveSpiTransport::Config<1>::PinConfig", bycopy.} = object nss* {.importc: "nss".}: array[1, Pin] ## Chip select pins (one per device) mosi* {.importc: "mosi".}: Pin ## SPI MOSI miso* {.importc: "miso".}: Pin ## SPI MISO sclk* {.importc: "sclk".}: Pin ## SPI clock
- SPI pin configuration for transport layer Source Edit
UpdateCompleteCallback = proc (context: pointer) {.cdecl.}
- Callback called after each successful DMA update cycle Source Edit
Procs
proc configurePinAsAnalogRead[N](max: var MAX11300[N]; device: csize_t; pin: MAX11300Pin; range: AdcVoltageRange): MAX11300Result
-
Configure pin as analog input (ADC)
Parameters:
- device - Device index (0 to N-1)
- pin - Pin number (PIN_0 to PIN_19)
- range - Voltage range (ADC_0_TO_10, ADC_NEG5_TO_5, etc.)
Returns: MAX_OK on success, MAX_ERR on failure
Note: Requires appropriate power supply for chosen range
Source Edit proc configurePinAsAnalogWrite[N](max: var MAX11300[N]; device: csize_t; pin: MAX11300Pin; range: DacVoltageRange): MAX11300Result
-
Configure pin as analog output (DAC)
Parameters:
- device - Device index (0 to N-1)
- pin - Pin number (PIN_0 to PIN_19)
- range - Voltage range (DAC_0_TO_10, DAC_NEG5_TO_5, etc.)
Returns: MAX_OK on success, MAX_ERR on failure
Note: Requires appropriate power supply for chosen range
Source Edit proc configurePinAsDigitalRead[N](max: var MAX11300[N]; device: csize_t; pin: MAX11300Pin; threshold: float32 = 2.5): MAX11300Result
-
Configure pin as digital input (GPI)
Parameters:
- device - Device index (0 to N-1)
- pin - Pin number (PIN_0 to PIN_19)
- threshold - Logic level threshold voltage (default 2.5V)
Returns: MAX_OK on success, MAX_ERR on failure
WARNING: Digital input pins are 0-5V only. Voltages below -250mV will corrupt ALL analog readings on the device!
Source Edit proc configurePinAsDigitalWrite[N](max: var MAX11300[N]; device: csize_t; pin: MAX11300Pin; voltage: float32 = 5.0): MAX11300Result
-
Configure pin as digital output (GPO)
Parameters:
- device - Device index (0 to N-1)
- pin - Pin number (PIN_0 to PIN_19)
- voltage - Output voltage for logic HIGH (default 5.0V)
Returns: MAX_OK on success, MAX_ERR on failure
Note: Digital outputs are 0-5V only (no negative voltages)
Source Edit proc defaults(config: var MAX11300TransportConfig) {....raises: [], tags: [], forbids: [].}
- Set default transport configuration Source Edit
proc defaults(config: var MAX11300TransportPinConfig) {....raises: [], tags: [], forbids: [].}
-
Set default pin configuration for Daisy Seed Default pins match libDaisy defaults:
- SPI1: PORTB.5 (MOSI), PORTB.4 (MISO), PORTG.11 (SCLK)
- CS0: PORTG.10
proc disablePin[N](max: var MAX11300[N]; device: csize_t; pin: MAX11300Pin): MAX11300Result
-
Disable pin (set to High-Z mode)
Parameters:
- device - Device index (0 to N-1)
- pin - Pin number (PIN_0 to PIN_19)
Returns: MAX_OK on success, MAX_ERR on failure
Source Edit proc init[N](max: var MAX11300[N]; config: MAX11300Config): MAX11300Result
-
Initialize MAX11300 device(s)
This performs:
- SPI initialization and connectivity verification
- Device configuration (ADC/DAC modes, conversion rates, etc.)
- All pins initialized to High-Z (disabled) mode
Parameters:
- config - Configuration including SPI pins and settings
Returns: MAX_OK on success, MAX_ERR on failure
Note: Call this once at startup before configuring pins
Source Edit proc readAnalogPinRaw[N](max: var MAX11300[N]; device: csize_t; pin: MAX11300Pin): uint16
-
Read raw 12-bit ADC value (0-4095)
Note: This reads from local buffer. Call start() to enable automatic DMA updates, or values will be stale.
Parameters:
- device - Device index (0 to N-1)
- pin - Pin number configured as ADC
Returns: Raw 12-bit value (0-4095)
Source Edit proc readAnalogPinVolts[N](max: var MAX11300[N]; device: csize_t; pin: MAX11300Pin): float32
-
Read ADC value in volts
Note: This reads from local buffer. Call start() to enable automatic DMA updates, or values will be stale.
Parameters:
- device - Device index (0 to N-1)
- pin - Pin number configured as ADC
Returns: Voltage value according to configured range
Source Edit proc readDigitalPin[N](max: var MAX11300[N]; device: csize_t; pin: MAX11300Pin): bool
-
Read digital input state
Note: This reads from local buffer. Call start() to enable automatic DMA updates, or values will be stale.
Parameters:
- device - Device index (0 to N-1)
- pin - Pin number configured as GPI
Returns: true if above threshold, false otherwise
Source Edit proc start[N](max: var MAX11300[N]; callback: UpdateCompleteCallback = nil; context: pointer = nil): MAX11300Result
-
Start continuous DMA updates
This begins automatic background updates that:
- Write all DAC values to hardware
- Read all ADC values from hardware
- Write all GPO states to hardware
- Read all GPI states from hardware
- Call callback when complete (from interrupt context)
- Repeat continuously
Parameters:
- callback - Optional callback after each update (keep it fast!)
- context - Optional context pointer passed to callback
Returns: MAX_OK on success, MAX_ERR on failure
Note: Can work without calling this (polling mode), but DMA updates provide much better performance and timing.
Source Edit proc twelveBitUintToVolts(value: uint16; range: AdcVoltageRange): float32 {. ...raises: [], tags: [], forbids: [].}
-
Convert 12-bit ADC code to voltage
Parameters:
- value - Raw 12-bit value (0-4095)
- range - ADC voltage range
Returns: Voltage value
Source Edit proc voltsTo12BitUint(volts: float32; range: DacVoltageRange): uint16 {. ...raises: [], tags: [], forbids: [].}
-
Convert voltage to 12-bit DAC code
Parameters:
- volts - Voltage value
- range - DAC voltage range
Returns: 12-bit value (0-4095), clamped to range
Source Edit proc writeAnalogPinRaw[N](max: var MAX11300[N]; device: csize_t; pin: MAX11300Pin; value: uint16)
-
Write raw 12-bit DAC value (0-4095)
Note: This writes to local buffer. Call start() to enable automatic DMA updates, or values won't reach hardware.
Parameters:
- device - Device index (0 to N-1)
- pin - Pin number configured as DAC
- value - Raw 12-bit value (0-4095)
proc writeAnalogPinVolts[N](max: var MAX11300[N]; device: csize_t; pin: MAX11300Pin; voltage: float32)
-
Write DAC value in volts
Note: This writes to local buffer. Call start() to enable automatic DMA updates, or values won't reach hardware.
Parameters:
- device - Device index (0 to N-1)
- pin - Pin number configured as DAC
- voltage - Voltage value (clamped to configured range)
proc writeDigitalPin[N](max: var MAX11300[N]; device: csize_t; pin: MAX11300Pin; value: bool)
-
Write digital output state
Note: This writes to local buffer. Call start() to enable automatic DMA updates, or values won't reach hardware.
Parameters:
- device - Device index (0 to N-1)
- pin - Pin number configured as GPO
- value - true for HIGH, false for LOW