Hi,
I was wondering if there was a good sample of bringing up the max11300 sample and/or just a good solid sample to bring up a SPI device? My next step is bringing online the Max DAC.
It is super fun to bring up new hardware with the Daisy at the core.
Thanks,
Brett
BHAudio
December 10, 2021, 7:31pm
2
I wanted to share the quick photo… I’ve been working on a custom device now for a bit and I just got the first board built that has the MAX device on it!
Thanks,
Brett
1 Like
BHAudio
December 10, 2021, 9:45pm
3
Hello,
I have to admit, I do not do a lot of templates so here is what I ended up doing for my MAX setup/unitization in my BSP:
In my BSP .h file I added this line:
MAX11300Driver<BlockingSpiTransport> dac_driver;
In my BSP .cpp file I added these lines:
static constexpr MAX11300Driver<BlockingSpiTransport>::Config seq16_dac_config
= {MAX11300Driver<BlockingSpiTransport>::Config()};
In the Init BSP function I added this:
// Setup the MAX DAC
dac_driver.Init(seq16_dac_config);
In digging into the code for the MAX DAC default setup, it turns out that this is the setup I have on my hardware.
Does it look like I am missing anything here to make it go?
Note: it all complies, I just wanted to check to see if I may be missing anything in here for the config/init functions.
Thank you for your time and help,
Brett
There is a usage description in PR for this driver that might be helpful:
electro-smith:master
← recursinging:MAX11300
opened 08:58AM - 13 Oct 21 UTC
This is a very libDaisy and Eurorack oriented driver for the [MAX11300](https://… datasheets.maximintegrated.com/en/ds/MAX11300.pdf) DAC/ADC/GPIO device.
### MAX11300
> The MAX11300 integrates a PIXI™, 12-bit, multichannel, analog-to-digital converter (ADC) and a 12-bit, multichannel, buffered digital-to-analog converter (DAC) in a single integrated circuit. This device offers 20 mixed-signal high voltage, bipolar ports, which are configurable as an ADC analog input, a DAC analog output, a general-purpose input (GPI), a general-purpose output (GPO), or an analog switch terminal.
The MAX11300 is an interesting device for Eurorack applications not only because of its configurability, but also because of the voltage ranges it can support. It is possible to power the MAX11300 from the +12V and -12V Eurorack standard power rails, which allows the DAC and ADC to operate at Eurorack voltage levels, without any external scaling circuitry. This essentially means one could hook up the 20 port pins of the MAX11300 directly (or better perhaps through a small value resistor) to the 3.5mm jacks of a Eurorack module.
This flexibility comes with some limitations. The configurable voltage ranges are as follows:
* 0V to +10V
* -5V to +5V
* -10V to 0V
...all of these ranges span 10 volts. At 12 bits of DAC and ADC resolution, the accuracy is probably not good enough for V/Oct use (Although the MAX11300 based [Polyend Poly2 module](https://polyend.com/manuals/poly2-manual/#inputs-outputs) claims better than 5mV accuracy through calibration). Another limitation of the device DAC is the settling time. The datasheet recommends allowing for 40us between DAC updates. In practice I discovered that this time is cumulative per configured DAC pin. That means the more DAC pins that are configured, the longer the wait between updates, which reduces the time resolution considerably. The update rate is fine for LFOs and envelopes, but not for audio rate (>100Hz) modulation.
### API
The device can be attached to libDaisy hardware via one of the SPI peripherals. By default the MAX11300 will use `SPI_1` and its standard pin configuration, but this can be changed by providing your own `SpiHandle::Config`.
```c++
MAX11300 max11300;
MAX11300::Config max11300_config;
max11300_config.Defaults();
max11300.Init(max11300_config);
```
The API of this driver was designed to provide intuitive access to the features most relevant to Eurorack.
This includes configuring and re-configuring pins at run-time. Pins can be configured in four modes:
* DigitalRead
* DigitalWrite
* AnalogRead
* AnalogWrite
When configuring pins to `DigitalRead` or `DigitalWrite` modes, then an additional configuration `threshold` allows you to set the voltage threshold/output of boolean true. For example, `8.0f;` would configure a `DigitalWrite` pin to switch between 0V (false) and 8V (true), whereas `2.5f;` would configure a `DigitalRead` pin to return boolean true when the voltage detected at that pin is higher than 2.5V.
```c++
max11300.ConfigurePinAsDigitalWrite(MAX11300::PIN_12, 8.0f);
max11300.ConfigurePinAsDigitalRead(MAX11300::PIN_8, 2.5f);
```
When configured as `DigitalRead` or `DigitalWrite`, pins can be written to and read from as boolean values.
```c++
max11300.WriteDigitalPin(MAX11300::PIN_12, true);
bool value = max11300.ReadDigitalPin(MAX11300::PIN_8);
```
When configuring pins as `AnalogRead`, or `AnalogWrite`, a `VoltageRange` is included to configure what voltage range the pin will read from, or write to.
```cpp
max11300.ConfigurePinAsAnalogRead(MAX11300::PIN_14, MAX11300::VoltageRange::ZERO_TO_10);
max11300.ConfigurePinAsAnalogWrite(MAX11300::PIN_5, MAX11300::VoltageRange::NEGATIVE_5_TO_5);
```
When configured as `AnalogRead`, or `AnalogWrite`, pins can be written to and read from using the raw 12 bit codes, or a by voltage, which is then clamped and scaled according to the voltage range of the configured pin.
```cpp
max11300.WriteAnalogPinVolts(MAX11300::PIN_5, -3.25f);
```
All pin `Read*` and `Write*` method operate locally on memory locations that need to be synchronized with the device. This is done by invoking the `Update()` method at regular intervals.
```cpp
max11300.Update();
```
### Implementation details
After receiving some helpful advice, I decided to make the driver implementation templated with an abstract "transport". This allowed for more thorough unit testing, as well as support for a DMA based transfers in the future.
The (quasi) full-duplex nature of communication with this device required extending the API of the `SpiHandle` class in libDaisy. I added a `BlockingTransmitAndReceive` method to support sending and receiving data in the context of a single transaction (CS/NSS low). The impact of this addition should be low, as it is simply a wrapper for the underlying HAL pimpl method of the same name.
I discovered that if you try and update DAC configured pins without respecting the datasheet recommended 40us settling time, the MAX11300 will simply lock up until it is power cycled again. In order to prevent this, I added a `wait_us` parameter to the transport `Transmit` interface. This allows the driver to communicate the max update frequency to the transport implementation - which will be necessary when implementing a DMA based transport.
### Out of scope
The driver transport is currently using blocking methods of the `SpiHandle` class. This is not optimal, and the nature of the driver API should make a DMA based transport possible. There are a couple of things to be aware of when tackling this.
* The `SpiHandle` class does not yet have the DMA equivalent to `BlockingTransmitAndReceive`
* The TX and RX buffers are not in the `DMA_BUFFER_MEM_SECTION`, nor does the API allow them to be passed in. This means the DMA TX and RX method impls will need to do cache flushing and invalidation.
* The `wait_us` parameter passed from the driver will need to be respected when scheduling DMA transactions.
### Conclusion
I hope the addition of this driver will open up a number of new options for people designing Eurorack modules on the Daisy platform. I welcome any and all critique and contributions!
I also discovered some unexpected behavior which is also documented in the PR. Nothing too critical, but good to be aware of.
BHAudio
December 11, 2021, 3:04pm
5
@recursinging ,
Thank you, the API in the PR is great to read through!
Do you know if there are docs that collect all of the APIs in one place like this? That would be awesome to have!
Most of all, thank you for the pointer to the API. That is a huge help to me.
Thanks,
Brett