Interfacing Teensy 4.1 with a CN0540 ADC board (24 bit SPI??)

Status
Not open for further replies.

Girija

Member
I am setting up the CN0540 to communicate over SPI with the Teensy 4.1

https://wiki.analog.com/resources/eval/user-guides/circuits-from-the-lab/cn0540?doc=cn0540.pdf

The documentation says that CN0540 is Arduino compatible - what does that mean as far as the SPI interface goes - the word length.

Does the Teensy 4.1 accept 24 bit data? The CN0540 uses an AD7768 - which is a 24 bit ADC.
I'm continuing to search in the forum and datasheets. But am getting confused......
 
Hi,

I'll try my best to help if I can.

I think in this situation, SPI is SPI - the amount of data the device puts out is unrelated. Although, you'll have to make multiple transfers to get a single 24 bit value.

Your CN0540 board uses an AD7768 ADC.

SPI Interface Details

The AD7768/AD7768-4 have a 4-wire SPI interface that is compatible with QSPI™, MICROWIRE®, and DSPs. The interface operates in SPI Mode 0.

Each SPI access frame is 16 bits long. The MSB (Bit 15) of the SDI command is the R/W bit; 1 = read and 0 = write. Bits[14:8] of the SDI command are the address bits.

If it's Arduino compatible, it should work just fine with T4.1.
 
Hi,

I'll try my best to help if I can.

I think in this situation, SPI is SPI - the amount of data the device puts out is unrelated. Although, you'll have to make multiple transfers to get a single 24 bit value.

Your CN0540 board uses an AD7768 ADC.



If it's Arduino compatible, it should work just fine with T4.1.



Thanks. I'm confused whether I will have to do 2 reads with one shift or 3 reads with 2 shifts? Does the SPI.transfer command read 8 bits at a time? - in which case, something along the lines of the following code should do? I'm assuming the MSB is being put out first by the ADC.

first=SPI.transfer();
second=SPI.transfer();
third=SPI.transfer();
Longvalue = (first << 16) | (second << 8) | third
 
Thanks. I'm confused whether I will have to do 2 reads with one shift or 3 reads with 2 shifts? Does the SPI.transfer command read 8 bits at a time? - in which case, something along the lines of the following code should do? I'm assuming the MSB is being put out first by the ADC.

first=SPI.transfer();
second=SPI.transfer();
third=SPI.transfer();
Longvalue = (first << 16) | (second << 8) | third

I'd have to look at the datasheet and have a think .. I'm more familiar with i2c, usually try to use that instead of SPI if I can, and even then often I've used libraries.

I'd guess if "Bits[14:8] of the SDI command are the address bits. " that only really leaves 8 bits for data in each request, so you'll probably have to do 3 reads at 8 bytes a piece.. LB, MB, HB..

I wonder if there's a better way to interface than this though, ADCs often have multiple options and I don't think this one is an exception. I'm not at all familiar with it though.

Perhaps there's an SPI/QSPI library you can use for this. Not sure.

Someone else here will likely know much more. My best recommendation is beat your head against the datasheet for a few days...usually works for me :)
 
The SPI control interface uses an off frame protocol. This means that the master (FPGA/DSP) communicates with the AD7768/ AD7768-4 in two frames. The first frame sends a 16-bit instruction (R/W, address, and data) and the second frame is the response where the AD7768/AD7768-4 send 16 bits back to the master.

So, you are going to send an SPI transfer with the address of the register you want to read from or write to. I think if writing you can send with that transfer. Then the AD7768 should send a transfer back with 16 bits.

You'll have to look at the register table to see how the data is stored/how to access it. On page 78, you can see 'channel 0 offset' and such, and as I guessed, low, mid, high bytes for each register..

Some of these registers are meant to be written to for configuration as well, so you'll want to check that the right bits are set for your application.

If this is beyond your comfort zone, I'd definitely try to find a library for this device...otherwise, you're going to have to dig in..!
 
You mention it's Arduino compatible, and it seems like your eval board is a shield... I'd be willing to bet there's already a library out there for you to more easily use this device in that case, it may be written for an Arduino specifically but a lot of stuff 'just works' due to teensy lib compatibility magic, otherwise you might not have to adjust much to make it work. I'd definitely go that route unless one doesn't exist.
 
So, you are going to send an SPI transfer with the address of the register you want to read from or write to. I think if writing you can send with that transfer. Then the AD7768 should send a transfer back with 16 bits.

You'll have to look at the register table to see how the data is stored/how to access it. On page 78, you can see 'channel 0 offset' and such, and as I guessed, low, mid, high bytes for each register..

Some of these registers are meant to be written to for configuration as well, so you'll want to check that the right bits are set for your application.

If this is beyond your comfort zone, I'd definitely try to find a library for this device...otherwise, you're going to have to dig in..!

I was talking about importing the ADC data from the CN0540 to the Teensy.

Incidentally, I have also been trying to see how to write to the CN0540 registers using SPI. I haven't had much luck getting answers in other forums on that one. The user guide doesn't give an explanation either.

So I have 2 questions - how do I use SPI commands to read 24 bit ADC data?
How do I use SPI to read and write registers. The user guide gives register numbers rather than register addresses.
 
I was talking about importing the ADC data from the CN0540 to the Teensy.

Yes, I understand that. You'll still need to likely send data to the device, writing to registers to set options and such, to configure the device appropriately before you can start reading it from the teensy.

Quick Google didn't show any libraries for this specifically but I wouldn't be surprised if the manufacturer had something, I did see a Linux driver I think which you could look at.

Incidentally, I have also been trying to see how to write to the CN0540 registers using SPI. I haven't had much luck getting answers in other forums on that one. The user guide doesn't give an explanation either.

Again, I haven't used SPI much and when I have there's been library available for the device. I'm sure there's a generic library for SPI communications that works much like your pseudo code in post #2.


So I have 2 questions - how do I use SPI commands to read 24 bit ADC data?
How do I use SPI to read and write registers. The user guide gives register numbers rather than register addresses.

I believe your register numbers are going to be your addresses. While SPI can address more bits, the ADC doesn't have that large of memory address space / many addresses/registers. You can simply pad 0's if needed, for the address.

I'm basing everything here off the data sheet and educated guesses, again, I'm not super familiar with SPI but a data bus is a data bus.

The datasheet says that each SPI access frame is 16 bits.

MSB or Bit 15 of the SPI command is the READ/WRITE# bit, so if it's 1 you're doing a read, if zero, you're doing a write.

Next up, from bits 14-8, we have the address bits. These are going to be the address of the register you want to write to. This leave bits 7-0 for data.

Again, datasheet says:

The SPI control interface uses an off frame protocol. This means that the master (FPGA/DSP) communicates with the AD7768/ AD7768-4 in two frames. The first frame sends a 16-bit instruction (R/W, address, and data) and the second frame is the response where the AD7768/AD7768-4 send 16 bits back to the master.

So, you'll send your frame, then get a 16-bit frame back. Study page 51+ and the timing diagrams. I believe only bits 0-7 will be set in commands you receive from the ADC - and indeed there is a low, mid and high register for each channel, so you'll be doing 3 8-bit reads and shifting.

The ADC will detect illegal commands and respond with 0x0E00 when there's an error.

When the ADC resets, it will set some default settings (page 51) - this is likely how it boots up as well, like I said, you'll likely have to set some registers to get it working properly.

One example is one of the first settings, table 24, 0x01 should set channel mode a, register 0x02 channel mode b, the setting, with bit 3 lets you choose between a wideband filter or a sinc5 filter and set the decimation rate, with bits 2-0, from x32 to x1024. I believe reset in the table says what the value is after a reset.

To do this you'd want to send a transfer kind of like this: (registers usually count down, depends on endianess and platform etc)

0x810D or 0b1000'0001'0000'1101 // write transfer, setting channel mode A filter to Sinc5, decimation rate to x1024.

that's if the register counts down and it gets transferred like that, if not, you might have to reverse the bits, ie:
0xB081 or 0b1011'0000'1000'0001

Good idea to read 51-54 a few times. This will explain the different options, clocking and configurations, which again should ideally be set up for your application.

It actually seems that SPI might only be used for controlling the device. I'm not sure it's used for data output at all - see page 66 for data output options.

There are three options for data output format it seems, table 33.

Each ADC result comprises 32 bits. The first eight bits are the header status bits, which contain status information and the channel number.

The last 24 bits are your 24-bit conversion. DRDY# will go low, then 32 bits will get clocked in on a DOUTx line (assuming you have it set for a channel on each line, again, there are 3 data output configurations)

Also read page 68 from Data Interface: standard conversion operation..

..the datasheet is your friend! study it!
 
Wouldn't I2C also have the same issue of having to read 24 bit data?

I2C has two addressing modes, 7 and 10 bit but I think after that, due reserved bytes etc, transfers can be arbitrary.

Technically, a serial bus is a 1-bit bus anyway - just depends how you frame that data..UART example has a start and stop bit and 8 data bits.. another protocol could have a start and stop bit and arbitrary data bits.

But your device actually only uses SPI for configuration, not data output..in fact if you don't want to change the settings, you don't have to touch the SPI. The data output is serially clocked and framed by DRDY#.

The way it's outputted to the data pins depends on a pin configuration and is not changeable in software. Your dev board may have jumpers to change it or it may be hardwired to a specific mode.

Best of luck, again, I recommend studying the data sheets, all the information you need is there, it may take a while to start making sense sometimes but that's how it is.. I didn't know anything about the teensy 4.1 last week..and reading the manual the first 10 times barely helped...now I know a fair bit about it though and what registers do what.
 
Wow. Thanks for the detailed explanation. I was wondering how I could be sure if I was doing the right thing with the registers and was going to try multiple reads and writes like you suggested. The error codes would definitely be useful - I wasn't aware of that. Will play around with that. Thanks
 
Based on a quick look, you should have interrupt service routines for the rising edges of DRDY# and SCLK. When SCLK rises, input a single bit value from DOUT and left shift the accumulator. When DRDY# rises, store the 32 bit accumulated value.

You probably don't need SPI at all.
 
Based on a quick look, you should have interrupt service routines for the rising edges of DRDY# and SCLK. When SCLK rises, input a single bit value from DOUT and left shift the accumulator. When DRDY# rises, store the 32 bit accumulated value.

You probably don't need SPI at all.

Thanks. I am stuck at a more basic point. Spent a day trying to figure this out and gave up and figured I should ask.

Where is THE SPI pin? According to the data sheet: "To use SPI control mode, set the PIN/SPI pin high."
 
Yes, I understand that. You'll still need to likely send data to the device, writing to registers to set options and such, to configure the device appropriately before you can start reading it from the teensy.

Quick Google didn't show any libraries for this specifically but I wouldn't be surprised if the manufacturer had something, I did see a Linux driver I think which you could look at.



Again, I haven't used SPI much and when I have there's been library available for the device. I'm sure there's a generic library for SPI communications that works much like your pseudo code in post #2.




I believe your register numbers are going to be your addresses. While SPI can address more bits, the ADC doesn't have that large of memory address space / many addresses/registers. You can simply pad 0's if needed, for the address.

I'm basing everything here off the data sheet and educated guesses, again, I'm not super familiar with SPI but a data bus is a data bus.

The datasheet says that each SPI access frame is 16 bits.

MSB or Bit 15 of the SPI command is the READ/WRITE# bit, so if it's 1 you're doing a read, if zero, you're doing a write.

Next up, from bits 14-8, we have the address bits. These are going to be the address of the register you want to write to. This leave bits 7-0 for data.

Again, datasheet says:



So, you'll send your frame, then get a 16-bit frame back. Study page 51+ and the timing diagrams. I believe only bits 0-7 will be set in commands you receive from the ADC - and indeed there is a low, mid and high register for each channel, so you'll be doing 3 8-bit reads and shifting.

The ADC will detect illegal commands and respond with 0x0E00 when there's an error.

When the ADC resets, it will set some default settings (page 51) - this is likely how it boots up as well, like I said, you'll likely have to set some registers to get it working properly.

One example is one of the first settings, table 24, 0x01 should set channel mode a, register 0x02 channel mode b, the setting, with bit 3 lets you choose between a wideband filter or a sinc5 filter and set the decimation rate, with bits 2-0, from x32 to x1024. I believe reset in the table says what the value is after a reset.

To do this you'd want to send a transfer kind of like this: (registers usually count down, depends on endianess and platform etc)

0x810D or 0b1000'0001'0000'1101 // write transfer, setting channel mode A filter to Sinc5, decimation rate to x1024.

that's if the register counts down and it gets transferred like that, if not, you might have to reverse the bits, ie:
0xB081 or 0b1011'0000'1000'0001

Good idea to read 51-54 a few times. This will explain the different options, clocking and configurations, which again should ideally be set up for your application.

It actually seems that SPI might only be used for controlling the device. I'm not sure it's used for data output at all - see page 66 for data output options.

There are three options for data output format it seems, table 33.



The last 24 bits are your 24-bit conversion. DRDY# will go low, then 32 bits will get clocked in on a DOUTx line (assuming you have it set for a channel on each line, again, there are 3 data output configurations)

Also read page 68 from Data Interface: standard conversion operation..

..the datasheet is your friend! study it!

I am not seeing similar information in tables like you are talking about. Is this the datasheet you are referring to?
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7768-1.pdf

I couldn't find a datasheet for the CN0540
 
Thanks. I am stuck at a more basic point. Spent a day trying to figure this out and gave up and figured I should ask.

Where is THE SPI pin? According to the data sheet: "To use SPI control mode, set the PIN/SPI pin high."

I located the SPI pin on the board and traced it with a continuity tester to a pin on the Arduino header. It is the same pin as the one marked as DRDY in the CN0540's schematic diagram!
Anyway...setting it to high didn't help. I'm getting either a 255 or 0 reads on registers irrespective of the status of Chip Select and various delays timers between issuing the transfer for the register number and the response.
 
> located the SPI pin on the board

As I see it, the board has a pull-up resistor on this pin - so you don't need to do anything to use SPI.
 
> located the SPI pin on the board

As I see it, the board has a pull-up resistor on this pin - so you don't need to do anything to use SPI.

Thanks. You're right. I'm assuming the CN0540 basically doesn't allow PIN mode operation of the AD7768 in that case. I'm having no luck getting any response other than 0s from the registers. I have reached out to AD to see if the CN0540 sets everything to zero by default. I doubt that's the case. My code is attached. I will have to purchase a logic analyzer. If that doesn't help, I will not waste any more time on this board.


Code:
#include <SPI.h>

//ADC's memory register addresses:
const int POWER_mode = 0x15;      //2 least significant bits of power_clock
const int MCLK_divider = 0x15;  //Bits 4,5 of power_clock
const byte FILTER_type = 0x19;     // Bits 4-6 of Digital_filter
const byte DECIMATION_ratio = 0x19;   // 3 least significant bits of Digital_filter
const int CONVERSION_mode = 0x18;  //3 least significant bits 

const int chipSelectPin = 10;

void setup() {
  Serial.begin(115200);

  // start the SPI library:
  SPI.begin();
 
  // initalize the pins:
  pinMode(13, OUTPUT); //SPI SCK
  pinMode(11, OUTPUT); //SPI MOSI
  pinMode(12, INPUT); //SPI MISO
  pinMode(chipSelectPin, OUTPUT);

  delay(2000);

  //SPI.beginTransaction(SPISettings(14000000, MSBFIRST, SPI_MODE3));
  SPI.setDataMode (SPI_MODE3);
  // take the chip select low to select the device:
  digitalWrite(chipSelectPin, LOW);

  
  // send the device the register you want to read:
  SPI.transfer(0x15);
  byte result1 = SPI.transfer(0x00);
  //delay(20);
  Serial.println("Power register");
  Serial.println(result1);
 

  SPI.transfer(0x18);
  int result2 = SPI.transfer(0x00);
  delay(20);
  Serial.println("Decimation");
  Serial.println(result2);

  
  SPI.transfer(0x19);
  unsigned int result3 = SPI.transfer(0x00);
  delay(20);
  Serial.println("Filter");
  Serial.println(result3);

  // take the chip select high to de-select:
  digitalWrite(chipSelectPin, HIGH);
  // release control of the SPI port
  SPI.endTransaction();
}

void loop() {
  
}

HTML:
The output is below:

Power register
0
Decimation
0
Filter
0
 
Status
Not open for further replies.
Back
Top