File max11300.h¶
File List > dev > max11300.h
Go to the documentation of this file
Source Code¶
#pragma once
#ifndef DSY_MAX11300_H
#define DSY_MAX11300_H
#include "daisy_core.h"
#include "per/spiMultislave.h"
#include "sys/system.h"
#include <cstring>
namespace daisy
{
//MAX11300 register definitions
#define MAX11300_DEVICE_ID 0x00
#define MAX11300_DEVCTL 0x10
#define MAX11300_FUNC_BASE 0x20
#define MAX11300_GPIDAT 0x0b
#define MAX11300_GPODAT 0x0d
#define MAX11300_ADCDAT_BASE 0x40
#define MAX11300_DACDAT_BASE 0x60
#define MAX11300_TRANSPORT_BUFFER_LENGTH 41
namespace MAX11300Types
{
enum Pin
{
PIN_0,
PIN_1,
PIN_2,
PIN_3,
PIN_4,
PIN_5,
PIN_6,
PIN_7,
PIN_8,
PIN_9,
PIN_10,
PIN_11,
PIN_12,
PIN_13,
PIN_14,
PIN_15,
PIN_16,
PIN_17,
PIN_18,
PIN_19
};
enum class AdcVoltageRange
{
ZERO_TO_10 = 0x0100,
NEGATIVE_5_TO_5 = 0x0200,
NEGATIVE_10_TO_0 = 0x0300,
ZERO_TO_2P5 = 0x0400
};
enum class DacVoltageRange
{
ZERO_TO_10 = 0x0100,
NEGATIVE_5_TO_5 = 0x0200,
NEGATIVE_10_TO_0 = 0x0300,
};
enum class Result
{
OK,
ERR
};
typedef void (*TransportCallbackFunctionPtr)(void* context,
SpiHandle::Result result);
struct DmaBuffer
{
uint8_t rx_buffer[MAX11300_TRANSPORT_BUFFER_LENGTH];
uint8_t tx_buffer[MAX11300_TRANSPORT_BUFFER_LENGTH];
};
typedef void (*UpdateCompleteCallbackFunctionPtr)(void* context);
} // namespace MAX11300Types
class MAX11300MultiSlaveSpiTransport
{
public:
template <size_t numDevices>
struct Config
{
struct PinConfig
{
Pin nss[numDevices] = {Pin(PORTG, 10)}; // Pin 7
Pin mosi = Pin(PORTB, 5); // Pin 10
Pin miso = Pin(PORTB, 4); // Pin 9
Pin sclk = Pin(PORTG, 11); // Pin 8
} pin_config;
SpiHandle::Config::Peripheral periph
= SpiHandle::Config::Peripheral::SPI_1;
SpiHandle::Config::BaudPrescaler baud_prescaler
= SpiHandle::Config::BaudPrescaler::PS_8;
};
enum class Result
{
OK,
ERR
};
template <size_t num_devices>
Result Init(Config<num_devices> config)
{
MultiSlaveSpiHandle::Config spi_config;
spi_config.pin_config.mosi = config.pin_config.mosi;
spi_config.pin_config.miso = config.pin_config.miso;
spi_config.pin_config.sclk = config.pin_config.sclk;
const auto clamped_num_devices
= std::min(num_devices, MultiSlaveSpiHandle::max_num_devices_);
for(size_t i = 0; i < clamped_num_devices; i++)
spi_config.pin_config.nss[i] = config.pin_config.nss[i];
spi_config.periph = config.periph;
spi_config.direction = SpiHandle::Config::Direction::TWO_LINES;
spi_config.datasize = 8;
spi_config.clock_polarity = SpiHandle::Config::ClockPolarity::LOW;
spi_config.clock_phase = SpiHandle::Config::ClockPhase::ONE_EDGE;
spi_config.baud_prescaler = config.baud_prescaler;
// not using clamped value here on purpose to escalate errors from SPI init
spi_config.num_devices = num_devices;
num_devices_ = num_devices;
const auto result = spi_.Init(spi_config);
ready_ = result == SpiHandle::Result::OK;
return ready_ ? Result::OK : Result::ERR;
}
bool Ready() { return ready_; }
Result TransmitBlocking(size_t device_index, uint8_t* buff, size_t size)
{
if(spi_.BlockingTransmit(device_index, buff, size)
== SpiHandle::Result::ERR)
{
return Result::ERR;
}
return Result::OK;
}
Result
TransmitDma(size_t device_index,
uint8_t* buff,
size_t size,
MAX11300Types::TransportCallbackFunctionPtr complete_callback,
void* callback_context)
{
if(spi_.DmaTransmit(device_index,
buff,
size,
nullptr, // start callback
complete_callback,
callback_context)
== SpiHandle::Result::ERR)
{
return Result::ERR;
}
return Result::OK;
}
Result TransmitAndReceiveBlocking(size_t device_index,
uint8_t* tx_buff,
uint8_t* rx_buff,
size_t size)
{
if(spi_.BlockingTransmitAndReceive(device_index, tx_buff, rx_buff, size)
== SpiHandle::Result::ERR)
{
return Result::ERR;
}
return Result::OK;
}
Result TransmitAndReceiveDma(
size_t device_index,
uint8_t* tx_buff,
uint8_t* rx_buff,
size_t size,
MAX11300Types::TransportCallbackFunctionPtr complete_callback,
void* callback_context)
{
if(spi_.DmaTransmitAndReceive(device_index,
tx_buff,
rx_buff,
size,
nullptr, // start callback
complete_callback,
callback_context)
== SpiHandle::Result::ERR)
{
return Result::ERR;
}
return Result::OK;
}
size_t GetNumDevices() const { return num_devices_; }
private:
MultiSlaveSpiHandle spi_;
size_t num_devices_ = 0;
bool ready_ = false;
};
template <typename Transport, size_t num_devices>
class MAX11300Driver
{
public:
struct Config
{
typename Transport::template Config<num_devices> transport_config;
void Defaults() { transport_config = decltype(transport_config){}; }
};
MAX11300Driver(){};
~MAX11300Driver(){};
MAX11300Types::Result Init(Config config,
MAX11300Types::DmaBuffer* dma_buffer)
{
dma_buffer_ = dma_buffer;
update_complete_callback_ = nullptr;
update_complete_callback_context_ = nullptr;
run_ = false;
if(transport_.Init(config.transport_config) != Transport::Result::OK)
return MAX11300Types::Result::ERR;
sequencer_.Invalidate();
for(size_t device_index = 0; device_index < transport_.GetNumDevices();
device_index++)
{
// First, let's verify the SPI comms, and chip presence. The DEVID register
// is a fixed, read-only value we can compare against to ensure we're connected.
if(ReadRegister(device_index, MAX11300_DEVICE_ID) != 0x0424)
{
return MAX11300Types::Result::ERR;
}
// Init routine (roughly) as per the datasheet pp. 49
// These settings were chosen as best applicable for use in a Eurorack context.
// Should the need for more configurability arise, this would be the spot to do it.
// Setup the device...
uint16_t devctl = 0x0000;
// 1:0 ADCCTL[1:0] - ADC conversion mode selection = 11: Continuous sweep
devctl = devctl | 0x0003;
// 3:2 DACCTL[1:0] - DAC mode selection = 00: Sequential Update mode for DAC-configured ports.
devctl = devctl | 0x0000;
// 5:4 ADCCONV[1:0] - ADC conversion rate selection = 11: ADC conversion rate of 400ksps
devctl = devctl | 0x0030;
// 6 DACREF - DAC voltage reference selection = 1: Internal reference voltage
devctl = devctl | 0x0040;
// 7 THSHDN - Thermal shutdown enable = 1: Thermal shutdown function enabled.
devctl = devctl | 0x0080;
// 10:8 TMPCTL[2:0] - Temperature monitor selection = 001: Internal temperature monitor enabled
devctl = devctl | 0x0100;
// 11 TMPPER - Temperature conversion time control = 0 (Default)
// 12 RS_CANCEL - Temperature sensor series resistor cancellation mode = 0 (Default)
// 13 LPEN - Power mode selection = 0 (Default)
// 14 BRST - Serial interface burst-mode selection = 1: Contextual address incrementing mode
devctl = devctl | 0x4000;
// 15 RESET - Soft reset control = 0 (Default)
// Write the device configuration
if(WriteRegister(device_index, MAX11300_DEVCTL, devctl)
== MAX11300Types::Result::ERR)
{
return MAX11300Types::Result::ERR;
}
// Verify our configuration was written...
if(ReadRegister(device_index, MAX11300_DEVCTL) != devctl)
{
return MAX11300Types::Result::ERR;
}
// Add a delay as recommended in the datasheet.
DelayUs(200);
// Set all pins to the default high impedance state...
for(uint8_t i = 0; i <= MAX11300Types::Pin::PIN_19; i++)
{
PinConfig pin_cfg;
pin_cfg.Defaults();
devices_[device_index].pin_configurations_[i] = pin_cfg;
SetPinConfig(device_index, static_cast<MAX11300Types::Pin>(i));
}
}
return MAX11300Types::Result::OK;
}
MAX11300Types::Result ConfigurePinAsDigitalRead(size_t device_index,
MAX11300Types::Pin pin,
float threshold_voltage)
{
auto& device = devices_[device_index];
if(threshold_voltage > 5.0f)
threshold_voltage = 5.0f;
if(threshold_voltage < 0.0f)
threshold_voltage = 0.0f;
device.pin_configurations_[pin].Defaults();
device.pin_configurations_[pin].mode = PinMode::GPI;
device.pin_configurations_[pin].threshold = threshold_voltage;
return SetPinConfig(device_index, pin);
}
MAX11300Types::Result ConfigurePinAsDigitalWrite(size_t device_index,
MAX11300Types::Pin pin,
float output_voltage)
{
auto& device = devices_[device_index];
if(output_voltage > 5.0f)
output_voltage = 5.0f;
if(output_voltage < 0.0f)
output_voltage = 0.0f;
device.pin_configurations_[pin].Defaults();
device.pin_configurations_[pin].mode = PinMode::GPO;
device.pin_configurations_[pin].threshold = output_voltage;
return SetPinConfig(device_index, pin);
}
MAX11300Types::Result
ConfigurePinAsAnalogRead(size_t device_index,
MAX11300Types::Pin pin,
MAX11300Types::AdcVoltageRange range)
{
auto& device = devices_[device_index];
device.pin_configurations_[pin].Defaults();
device.pin_configurations_[pin].mode = PinMode::ANALOG_IN;
device.pin_configurations_[pin].range.adc = range;
return SetPinConfig(device_index, pin);
}
MAX11300Types::Result
ConfigurePinAsAnalogWrite(size_t device_index,
MAX11300Types::Pin pin,
MAX11300Types::DacVoltageRange range)
{
auto& device = devices_[device_index];
device.pin_configurations_[pin].Defaults();
device.pin_configurations_[pin].mode = PinMode::ANALOG_OUT;
device.pin_configurations_[pin].range.dac = range;
return SetPinConfig(device_index, pin);
}
MAX11300Types::Result DisablePin(size_t device_index, Pin pin)
{
auto& device = devices_[device_index];
device.pin_configurations_[pin].Defaults();
return SetPinConfig(device_index, pin);
}
uint16_t ReadAnalogPinRaw(size_t device_index, MAX11300Types::Pin pin) const
{
auto& device = devices_[device_index];
if(device.pin_configurations_[pin].value == nullptr)
{
return 0;
}
return __builtin_bswap16(*device.pin_configurations_[pin].value);
}
float ReadAnalogPinVolts(size_t device_index, MAX11300Types::Pin pin) const
{
auto& device = devices_[device_index];
return MAX11300Driver::TwelveBitUintToVolts(
ReadAnalogPinRaw(device_index, pin),
device.pin_configurations_[pin].range.adc);
}
void WriteAnalogPinRaw(size_t device_index,
MAX11300Types::Pin pin,
uint16_t raw_value)
{
auto& device = devices_[device_index];
if(device.pin_configurations_[pin].value != nullptr)
{
*device.pin_configurations_[pin].value
= __builtin_bswap16(raw_value);
}
}
void WriteAnalogPinVolts(size_t device_index,
MAX11300Types::Pin pin,
float voltage)
{
auto& device = devices_[device_index];
auto pin_config = device.pin_configurations_[pin];
return WriteAnalogPinRaw(
device_index,
pin,
MAX11300Driver::VoltsTo12BitUint(voltage, pin_config.range.dac));
}
bool ReadDigitalPin(size_t device_index, MAX11300Types::Pin pin) const
{
auto& device = devices_[device_index];
if(pin > MAX11300Types::Pin::PIN_15)
{
return static_cast<bool>((device.gpi_buffer_[4] >> (pin - 16)) & 1);
}
else if(pin > MAX11300Types::Pin::PIN_7)
{
return static_cast<bool>((device.gpi_buffer_[1] >> (pin - 8)) & 1);
}
else
{
return static_cast<bool>((device.gpi_buffer_[2] >> pin) & 1);
}
}
void
WriteDigitalPin(size_t device_index, MAX11300Types::Pin pin, bool value)
{
auto& device = devices_[device_index];
// (void) pin;
// (void) value;
if(value)
{
if(pin > MAX11300Types::Pin::PIN_15)
{
device.gpo_buffer_[4] |= (1 << (pin - 16));
}
else if(pin > MAX11300Types::Pin::PIN_7)
{
device.gpo_buffer_[1] |= (1 << (pin - 8));
}
else
{
device.gpo_buffer_[2] |= (1 << pin);
}
}
else
{
if(pin > MAX11300Types::Pin::PIN_15)
{
device.gpo_buffer_[4] &= ~(1 << (pin - 16));
}
else if(pin > MAX11300Types::Pin::PIN_7)
{
device.gpo_buffer_[1] &= ~(1 << (pin - 8));
}
else
{
device.gpo_buffer_[2] &= ~(1 << pin);
}
}
}
MAX11300Types::Result
Start(MAX11300Types::UpdateCompleteCallbackFunctionPtr complete_callback
= nullptr,
void* complete_callback_context = nullptr)
{
if(sequencer_.IsBusy() && run_)
{
// When the sequencer is currently busy, we can just return right away, and only
// update the callbacks
update_complete_callback_ = complete_callback;
update_complete_callback_context_ = complete_callback_context;
return MAX11300Types::Result::OK;
}
run_ = true;
update_complete_callback_ = complete_callback;
update_complete_callback_context_ = complete_callback_context;
sequencer_.current_device_ = 0;
sequencer_.current_step_ = UpdateSequencer::first_step_;
ContinueUpdate();
return MAX11300Types::Result::OK;
}
void Stop() { run_ = false; }
static uint16_t VoltsTo12BitUint(float volts,
MAX11300Types::DacVoltageRange range)
{
float vmax = 0;
float vmin = 0;
float vscaler = 0;
switch(range)
{
case MAX11300Types::DacVoltageRange::NEGATIVE_10_TO_0:
vmin = -10;
vmax = 0;
vscaler = 4095.0f / (vmax - vmin);
break;
case MAX11300Types::DacVoltageRange::NEGATIVE_5_TO_5:
vmin = -5;
vmax = 5;
vscaler = 4095.0f / (vmax - vmin);
break;
case MAX11300Types::DacVoltageRange::ZERO_TO_10:
vmin = 0;
vmax = 10;
vscaler = 4095.0f / (vmax - vmin);
break;
}
// Clamp...
if(volts > vmax)
volts = vmax;
if(volts < vmin)
volts = vmin;
return static_cast<uint16_t>((volts - vmin) * vscaler);
}
static float TwelveBitUintToVolts(uint16_t value,
MAX11300Types::AdcVoltageRange range)
{
float vmax = 0;
float vmin = 0;
float vscaler = 0;
switch(range)
{
case MAX11300Types::AdcVoltageRange::NEGATIVE_10_TO_0:
vmin = -10;
vmax = 0;
vscaler = (vmax - vmin) / 4095;
break;
case MAX11300Types::AdcVoltageRange::NEGATIVE_5_TO_5:
vmin = -5;
vmax = 5;
vscaler = (vmax - vmin) / 4095;
break;
case MAX11300Types::AdcVoltageRange::ZERO_TO_10:
vmin = 0;
vmax = 10;
vscaler = (vmax - vmin) / 4095;
break;
case MAX11300Types::AdcVoltageRange::ZERO_TO_2P5:
vmin = 0;
vmax = 2.5;
vscaler = (vmax - vmin) / 4095;
break;
}
// Clamp...
if(value > 4095)
value = 4095;
return (value * vscaler) + vmin;
}
private:
enum class PinMode
{
NONE = 0x0000, // Mode 0 (High impedance)
GPI = 0x1000, // Mode 1
GPO = 0x3000, // Mode 3
ANALOG_OUT = 0x5000, // Mode 5
ANALOG_IN = 0x7000, // Mode 7
};
struct PinConfig
{
PinMode mode;
union
{
MAX11300Types::AdcVoltageRange adc;
MAX11300Types::DacVoltageRange dac;
} range;
float threshold;
uint16_t* value;
void Defaults()
{
mode = PinMode::NONE;
range.adc = MAX11300Types::AdcVoltageRange::ZERO_TO_10;
threshold = 0.0f;
value = nullptr;
}
};
MAX11300Types::Result SetPinConfig(size_t device_index,
MAX11300Types::Pin pin)
{
uint16_t pin_func_cfg = 0x0000;
auto& device = devices_[device_index];
if(device.pin_configurations_[pin].mode != PinMode::NONE)
{
// Set the pin to high impedance mode before changing (as per the datasheet).
WriteRegister(device_index, MAX11300_FUNC_BASE + pin, 0x0000);
// According to the datasheet, the amount of time necessary for the pin to
// switch to high impedance mode depends on the prior configuration.
// The worst case recommended wait time seems to be 1ms.
DelayUs(1000);
}
// Apply the pin configuration
pin_func_cfg
= pin_func_cfg
| static_cast<uint16_t>(device.pin_configurations_[pin].mode);
if(device.pin_configurations_[pin].mode == PinMode::ANALOG_OUT)
{
pin_func_cfg |= static_cast<uint16_t>(
device.pin_configurations_[pin].range.dac);
}
else if(device.pin_configurations_[pin].mode == PinMode::ANALOG_IN)
{
// In ADC mode we'll average 128 samples per Update
pin_func_cfg = pin_func_cfg | 0x00e0
| static_cast<uint16_t>(
device.pin_configurations_[pin].range.adc);
}
else if(device.pin_configurations_[pin].mode == PinMode::GPI)
{
// The DAC data register for that port needs to be set to the value corresponding to the
// intended input threshold voltage. Any input voltage above that programmed threshold is
// reported as a logic one. The input voltage must be between 0V and 5V.
// It may take up to 1ms for the threshold voltage to be effective
WriteRegister(device_index,
(MAX11300_DACDAT_BASE + pin),
MAX11300Driver::VoltsTo12BitUint(
device.pin_configurations_[pin].threshold,
device.pin_configurations_[pin].range.dac));
}
else if(device.pin_configurations_[pin].mode == PinMode::GPO)
{
// The port’s DAC data register needs to be set first. It may require up to 1ms for the
// port to be ready to produce the desired logic one level.
WriteRegister(device_index,
(MAX11300_DACDAT_BASE + pin),
MAX11300Driver::VoltsTo12BitUint(
device.pin_configurations_[pin].threshold,
device.pin_configurations_[pin].range.dac));
}
// Write the configuration now...
if(WriteRegister(device_index, MAX11300_FUNC_BASE + pin, pin_func_cfg)
!= MAX11300Types::Result::OK)
{
return MAX11300Types::Result::ERR;
}
// Wait for 1ms as per the datasheet
DelayUs(1000);
// Verify our configuration was written
if(ReadRegister(device_index, MAX11300_FUNC_BASE + pin) != pin_func_cfg)
{
return MAX11300Types::Result::ERR;
}
// Update and re-index the pin configuration now...
UpdatePinConfig(device_index);
return MAX11300Types::Result::OK;
}
MAX11300Types::Result UpdatePinConfig(size_t device_index)
{
// TODO
auto& device = devices_[device_index];
// Zero everything out...
std::memset(device.dac_buffer_, 0, sizeof(device.dac_buffer_));
std::memset(device.adc_buffer_, 0, sizeof(device.adc_buffer_));
std::memset(device.gpi_buffer_, 0, sizeof(device.gpi_buffer_));
std::memset(device.gpo_buffer_, 0, sizeof(device.gpo_buffer_));
device.dac_pin_count_ = 0;
device.adc_pin_count_ = 0;
device.gpi_pin_count_ = 0;
device.gpo_pin_count_ = 0;
for(uint8_t i = 0; i <= MAX11300Types::Pin::PIN_19; i++)
{
MAX11300Types::Pin pin = static_cast<MAX11300Types::Pin>(i);
// Always reset the value pointer first...
device.pin_configurations_[i].value = nullptr;
if(device.pin_configurations_[i].mode == PinMode::ANALOG_OUT)
{
device.dac_pin_count_++;
if(device.dac_pin_count_ == 1)
{
// If this is the first pin of this type, we need to set
// the initial address of the dac_buffer_ to point at this pin.
// The ordering of subsequent pins is known by the MAX11300.
device.dac_buffer_[0] = (MAX11300_DACDAT_BASE + pin) << 1;
}
// set the pin_config.value to a pointer at the appropriate
// index of the dac_buffer...
device.pin_configurations_[i].value
= reinterpret_cast<uint16_t*>(
&device.dac_buffer_[(2 * device.dac_pin_count_) - 1]);
}
else if(device.pin_configurations_[i].mode == PinMode::ANALOG_IN)
{
device.adc_pin_count_++;
if(device.adc_pin_count_ == 1)
{
// If this is the first pin of this type, we need to set
// the initial address of the adc_buffer_ to point at this pin.
// The ordering of subsequent pins is known by the MAX11300.
device.adc_first_adress
= ((MAX11300_ADCDAT_BASE + pin) << 1) | 1;
}
// set the pin_config.value to a pointer at the appropriate
// index of the adc_buffer...
device.pin_configurations_[i].value
= reinterpret_cast<uint16_t*>(
&device.adc_buffer_[(2 * device.adc_pin_count_) - 1]);
}
else if(device.pin_configurations_[i].mode == PinMode::GPI)
{
device.gpi_pin_count_++;
}
else if(device.pin_configurations_[i].mode == PinMode::GPO)
{
device.gpo_pin_count_++;
}
}
return MAX11300Types::Result::OK;
}
// This is just a wrapper for System::DelayUs to allow it to be
// excluded from the unit tests without dirtying up the code so much
void DelayUs(uint32_t delay)
{
(void)delay;
#ifndef UNIT_TEST
System::DelayUs(delay);
#endif
}
uint16_t ReadRegister(size_t device_index, uint8_t address)
{
uint16_t val = 0;
ReadRegister(device_index, address, &val, 1);
return val;
}
MAX11300Types::Result ReadRegister(size_t device_index,
uint8_t address,
uint16_t* values,
size_t size)
{
size_t rx_length = (size * 2) + 1;
uint8_t rx_buff[MAX11300_TRANSPORT_BUFFER_LENGTH] = {};
uint8_t tx_buff[MAX11300_TRANSPORT_BUFFER_LENGTH] = {};
tx_buff[0] = (address << 1) | 1;
if(transport_.TransmitAndReceiveBlocking(
device_index, tx_buff, rx_buff, rx_length)
!= Transport::Result::OK)
{
return MAX11300Types::Result::ERR;
}
size_t rx_idx = 1;
for(size_t i = 0; i < size; i++)
{
values[i] = static_cast<uint16_t>((rx_buff[rx_idx] << 8)
+ rx_buff[rx_idx + 1]);
rx_idx = rx_idx + 2;
}
return MAX11300Types::Result::OK;
}
MAX11300Types::Result
WriteRegister(size_t device_index, uint8_t address, uint16_t value)
{
return WriteRegister(device_index, address, &value, 1);
}
MAX11300Types::Result WriteRegister(size_t device_index,
uint8_t address,
uint16_t* values,
size_t size)
{
size_t tx_size = (size * 2) + 1;
uint8_t tx_buff[MAX11300_TRANSPORT_BUFFER_LENGTH] = {};
tx_buff[0] = (address << 1);
size_t tx_idx = 1;
for(size_t i = 0; i < size; i++)
{
tx_buff[tx_idx++] = static_cast<uint8_t>(values[i] >> 8);
tx_buff[tx_idx++] = static_cast<uint8_t>(values[i]);
}
if(transport_.TransmitBlocking(device_index, tx_buff, tx_size)
== Transport::Result::OK)
{
return MAX11300Types::Result::OK;
}
return MAX11300Types::Result::ERR;
}
MAX11300Types::Result ReadModifyWriteRegister(size_t device_index,
uint8_t address,
uint16_t mask,
uint16_t value)
{
uint16_t reg = ReadRegister(device_index, address);
reg = (reg & ~mask) | (uint16_t)(value);
return WriteRegister(device_index, address, reg);
}
void ContinueUpdate()
{
// read results from the transmission that was just completed
switch(sequencer_.current_step_)
{
case UpdateSequencer::Step::start:
sequencer_.current_device_ = 0;
sequencer_.current_step_ = UpdateSequencer::Step::updateDac;
break;
case UpdateSequencer::Step::updateDac:
// nothing to read back; we only sent data
sequencer_.current_step_ = UpdateSequencer::Step::updateAdc;
break;
case UpdateSequencer::Step::updateAdc:
{
// read back rx data
const size_t size
= (devices_[sequencer_.current_device_].adc_pin_count_ * 2)
+ 1;
memcpy(devices_[sequencer_.current_device_].adc_buffer_,
dma_buffer_->rx_buffer,
size);
sequencer_.current_step_ = UpdateSequencer::Step::updateGpo;
}
break;
case UpdateSequencer::Step::updateGpo:
// nothing to read back; we only sent data
sequencer_.current_step_ = UpdateSequencer::Step::updateGpi;
break;
case UpdateSequencer::Step::updateGpi:
{
// read back rx data
const size_t size
= sizeof(devices_[sequencer_.current_device_].gpi_buffer_);
memcpy(devices_[sequencer_.current_device_].gpi_buffer_,
dma_buffer_->rx_buffer,
size);
sequencer_.current_device_++;
sequencer_.current_step_ = UpdateSequencer::Step::updateDac;
}
break;
}
bool done = false;
while(!done)
{
if(sequencer_.current_device_ >= num_devices)
{
sequencer_.Invalidate();
if(update_complete_callback_)
update_complete_callback_(
update_complete_callback_context_);
// retrigger if not stopped
if(run_)
{
sequencer_.current_device_ = 0;
sequencer_.current_step_ = UpdateSequencer::Step::updateDac;
}
else
return; // all devices complete, no retriggering
}
auto& device = devices_[sequencer_.current_device_];
switch(sequencer_.current_step_)
{
case UpdateSequencer::Step::updateDac:
if(device.dac_pin_count_ > 0)
{
// This is a burst transaction utilizing the contextual addressing
// scheme of the MAX11300. See the datasheet @ pp. 30
//
// We've prefixed the dac_buffer_ to point at the first dac pin to be written to.
// Subsequent DAC pins are written in their absolute order (0-19) if configured as such.
// For example:
// [1st_dac_pin_addr],[1st_dac_pin_msb],[1st_dac_pin_lsb],[2nd_dac_pin_msb],[2nd_dac_pin_lsb]...
//
// The size of the transaction is determined by the number of configured dac pins,
// plus one byte for the initial pin address.
size_t tx_size = (device.dac_pin_count_ * 2) + 1;
memcpy(dma_buffer_->tx_buffer,
device.dac_buffer_,
tx_size);
transport_.TransmitDma(sequencer_.current_device_,
dma_buffer_->tx_buffer,
tx_size,
&DmaCompleteCallback,
this);
done = true;
}
else
{
sequencer_.current_step_
= UpdateSequencer::Step::updateAdc;
done = false; // cycle again
}
break;
case UpdateSequencer::Step::updateAdc:
if(device.adc_pin_count_ > 0)
{
// Reading ADC pins is a burst transaction approximately the same as the DAC transaction
// as described above...
size_t size = (device.adc_pin_count_ * 2) + 1;
dma_buffer_->tx_buffer[0] = device.adc_first_adress;
// clear the rest of the buffer (it may contain stuff from a previous transaction)
for(size_t i = 1; i < size; i++)
dma_buffer_->tx_buffer[i] = 0x00;
transport_.TransmitAndReceiveDma(
sequencer_.current_device_,
dma_buffer_->tx_buffer,
dma_buffer_->rx_buffer,
size,
&DmaCompleteCallback,
this);
done = true;
}
else
{
sequencer_.current_step_
= UpdateSequencer::Step::updateGpo;
done = false; // cycle again
}
break;
case UpdateSequencer::Step::updateGpo:
if(device.gpo_pin_count_ > 0)
{
// Writing GPO pins is a single 5 byte transaction, with the first byte being the
// the GPO data register, and the subsequent 4 bytes containing the state of the
// GPO ports to be written.
memcpy(dma_buffer_->tx_buffer,
device.gpo_buffer_,
sizeof(device.gpo_buffer_));
dma_buffer_->tx_buffer[0] = (MAX11300_GPODAT << 1);
transport_.TransmitDma(sequencer_.current_device_,
dma_buffer_->tx_buffer,
sizeof(device.gpo_buffer_),
&DmaCompleteCallback,
this);
done = true;
}
else
{
sequencer_.current_step_
= UpdateSequencer::Step::updateGpi;
done = false; // cycle again
}
break;
case UpdateSequencer::Step::updateGpi:
if(device.gpi_pin_count_ > 0)
{
// Reading GPI pins is a single, 5 byte, full-duplex transaction with the first
// and only TX byte being the GPI register.
dma_buffer_->tx_buffer[0] = (MAX11300_GPIDAT << 1) | 1;
// clear the rest of the buffer (it may contain stuff from a previous transaction)
for(size_t i = 1; i < sizeof(device.gpi_buffer_); i++)
dma_buffer_->tx_buffer[i] = 0x00;
transport_.TransmitAndReceiveDma(
sequencer_.current_device_,
dma_buffer_->tx_buffer,
dma_buffer_->rx_buffer,
sizeof(device.gpi_buffer_),
&DmaCompleteCallback,
this);
done = true;
}
else
{
sequencer_.current_step_
= UpdateSequencer::Step::updateDac;
sequencer_
.current_device_++; // go to next chip in sequence
done = false; // cycle again
}
break;
default: break;
}
}
}
static void DmaCompleteCallback(void* context, SpiHandle::Result result)
{
auto& driver = *reinterpret_cast<MAX11300Driver*>(context);
if(result == SpiHandle::Result::OK)
{
driver.ContinueUpdate();
}
else
{
driver.sequencer_.Invalidate();
}
}
MAX11300Types::DmaBuffer* dma_buffer_;
struct Device
{
PinConfig pin_configurations_[20];
uint8_t dac_pin_count_;
uint8_t adc_pin_count_;
uint8_t gpi_pin_count_;
uint8_t gpo_pin_count_;
uint8_t dac_buffer_[41];
uint8_t adc_first_adress;
uint8_t adc_buffer_[41];
uint8_t gpi_buffer_[5];
uint8_t gpo_buffer_[5];
};
Device devices_[num_devices];
struct UpdateSequencer
{
size_t current_device_ = 0;
enum class Step
{
start = 0,
updateDac,
updateAdc,
updateGpo,
updateGpi,
};
static constexpr auto first_step_ = Step::start;
static constexpr auto last_step_ = Step::updateGpi;
Step current_step_ = first_step_;
bool IsBusy() const { return current_device_ < num_devices; }
void Invalidate()
{
current_device_ = num_devices;
current_step_ = first_step_;
}
} sequencer_;
Transport transport_;
MAX11300Types::UpdateCompleteCallbackFunctionPtr update_complete_callback_;
void* update_complete_callback_context_;
bool run_;
};
template <size_t num_devices = 1>
using MAX11300
= daisy::MAX11300Driver<MAX11300MultiSlaveSpiTransport, num_devices>;
}; // namespace daisy
#endif