Hey it works! It was the mode, needs to be SPI_MODE1 or 2 as Synvox suggests (MarkT said it too!). And though I tried all the modes before, I think this was before I had tied the RESET pin to HIGH, an oversight on my part.
I understand mode 1 to be: data sampled on the falling edge and shifted out on the rising edge
and mode 2 to be: data sampled on the rising edge and shifted out on the falling edge
These two ideas seem logically opposed, if a device is setup to allow one of these modes to work, how could the other also work? Indeed as Synvox says, both modes work the same.. at least in my write-only use case, perhaps if I was also reading this would make a difference? Anyone able to clear this up?
...
I found that I needed to perform one more operation to the address variable to render the actual address of each DAC channel. I had to change the first SPI.transfer() call to this:
SPI.transfer(pow(2, address) + 16);
This means the uint8_t address values that refer to each channel are:
Ch A: 1 + 16
Ch B: 2 + 16
Ch C: 4 + 16
Ch D: 8 + 16
which is:
Ch A: 17
Ch B: 18
Ch C: 20
Ch D: 24
Which, when you Serial.print(number, BIN) each of those, you get:
Ch A: 10001
Ch B: 10010
Ch C: 10100
Ch D: 11000
The last four digits in the binary serial output are the channel code Im pretty sure, but why is there an extra 1 at the beginning? Are leading zeros chopped off and the 1 at the beginning is whats left of the first 0001 (C3, C2, C1, C0) command to write to the input register? If not, is that first 0001 command sent under the hood with the SPI lib in beginTransaction()?
To get the right DAC channel address, I needed to use the formula 2^n + 16. Why is it 2^n? Why is it + 16? Im not able to make sense of this yet.
...
Regarding SPI speed, is there some convention to apply here in choosing a speed? My users will likely not appreciate the difference between 10MHz and 30MHZ, so in the interest of signal integrity, should I normally opt for a speed well under device maximums?
...
Here is the minimum code and pin connections to get this DAC working with a Teensy, hopefully others will find the reference useful:
Code:
#include <SPI.h>
// Pin configuration for AD5686R DAC and Teensy 4.1
// DAC pin-1, Vref > NC (putting out 2.5v)
// DAC pin-2, VoutB > NC
// DAC pin-3, VoutA > NC
// DAC pin-4, GND > GND
// DAC pin-5, Vdd > Teensy +5.5V
// DAC pin-6, VoutC > NC
// DAC pin-7, VoutD > NC
// DAC pin-8, SDO > NC
// DAC pin-9, LDAC -> GND
// DAC pin-10, Gain > GND
// DAC pin-11, Vlogic > Teensy +3.3V
// DAC pin-12, SCLK > Teensy SCK, pin-13
// DAC pin-13, SYNC > Teensy CS, pin-10
// DAC pin-14, SDIN > Teensy MOSI, pin-11
// DAC pin-15, RESET > tied HIGH (Vlogic)
// DAC pin-16, RSTSEL > GND
const int slaveSelect = SS; // 10 (teensy 'CS' connected to AD5686R 'SYNC')
SPISettings AD5686R(10000000, MSBFIRST, SPI_MODE1);
void setup()
{
pinMode(slaveSelect, OUTPUT);
digitalWrite(slaveSelect, HIGH);
SPI.begin();
delay(1000);
}
void loop()
{
// Setting all channels to ~middle of range for 1 sec
Serial.println("Setting to middle");
for (uint8_t i = 0; i < 4; i++)
{
setDacChannelLevel(i, 35532);
}
delay(1000);
// Setting all channels to ~bottom of range for 1 sec
Serial.println("Setting to low");
for (uint8_t i = 0; i < 4; i++)
{
setDacChannelLevel(i, 24);
}
delay(1000);
}
void setDacChannelLevel(uint8_t address, uint16_t value)
{
SPI.beginTransaction(AD5686R);
digitalWrite(slaveSelect, LOW);
SPI.transfer(pow(2, address) + 16);
SPI.transfer(highByte(value));
SPI.transfer(lowByte(value));
digitalWrite(slaveSelect, HIGH);
SPI.endTransaction();
}
Thanks to all who have contributed!