redplanet

SPI Issue reading External ADC - AD7175-8 ID Register, using SAM4SA16B

Discussion created by redplanet on Oct 2, 2017
Latest reply on Oct 23, 2017 by DragosB

I am generating code to communicate to the Analog Devices AD7175-8 ADC using an Atmel SAM4SA16B, Cortex M4 core.    

 

SPI settings:

   Single Master/Multiple Slave Implementation - variable peripheral select mode.

   SPI Mode 3 for the ADC, CS3

   SPI Mode 0 for the Maxim MCP23S17 I/O expanders, CS1 and CS2

   8-bits per transfer

   Have not, yet, implemented the Peripheral DMA Controller.

 

SPI initialization

/****************************************************************************
* file AD7175_regs.h
* AD7175 Registers Definitions.
* author acozma (andrei.cozma@analog.com)
*
*******************************************************************************
* Copyright 2011(c) Analog Devices, Inc.
*
* All rights reserved.
*
*******************************************************************************
* SVN Revision: 0
* 1 - corrected register mistakes and omissions mra 9/20/2017
* 2 - entered probable register initial settings mra 9/26/2017
*
******************************************************************************/

#ifndef __AD7175_REGS_H__
#define __AD7175_REGS_H__

#include "stdint.h"
/* AD7175 register info */
typedef struct _st_reg
{
int32_t addr;
int32_t value;
int32_t size;
}st_reg;

/* AD7175 registers list */
enum AD7175_registers
{
Status_Register = 0x00,
ADC_Mode_Register,
Interface_Mode_Register,
Data_Register,
IOCon_Register,
ID_st_reg,
CH_Map_0,
CH_Map_1,
CH_Map_2,
CH_Map_3,
CH_Map_4,
CH_Map_5,
CH_Map_6,
CH_Map_7,
CH_Map_8,
CH_Map_9,
CH_Map_10,
CH_Map_11,
CH_Map_12,
CH_Map_13,
CH_Map_14,
CH_Map_15,
Setup_Config_0,
Setup_Config_1,
Setup_Config_2,
Setup_Config_3,
Setup_Config_4,
Setup_Config_5,
Setup_Config_6,
Setup_Config_7,
Filter_Config_0,
Filter_Config_1,
Filter_Config_2,
Filter_Config_3,
Filter_Config_4,
Filter_Config_5,
Filter_Config_6,
Filter_Config_7,
Offset_0,
Offset_1,
Offset_2,
Offset_3,
Offset_4,
Offset_5,
Offset_6,
Offset_7,
Gain_0,
Gain_1,
Gain_2,
Gain_3,
Gain_4,
Gain_5,
Gain_6,
Gain_7,
Communications_Register,
AD7175_REG_NO
};

#ifdef AD7175_INIT
/* Array holding the info for the AD7175 registers -
address, initial value, size */
st_reg AD7175_regs[] =
{
{0x00, 0x00, 1}, //STATUS contains ADC & serial i/f status info
{0x01, 0x2000, 2}, //ADCMODE controls op mode of ADC & master clk selection
{0x02, 0x0040, 2}, //IFMODE configures various serial i/f options
{0x04, 0x0000, 3}, //DATA contains the ADC conversion
{0x06, 0x0000, 2}, //GPIOCON controls the gpio pins of the ADC
{0x07, 0x0000, 2}, //ID_st_reg - ID returns 16-bit device ID of 0x3CDE
{0x10, 0x800C, 2}, //CH_Map_0
{0x11, 0x802C, 2}, //CH_Map_1
{0x12, 0x804C, 2}, //CH_Map_2
{0x13, 0x806C, 2}, //CH_Map_3
{0x14, 0x808C, 2}, //CH_Map_4
{0x15, 0x80AC, 2}, //CH_Map_5
{0x16, 0x80CC, 2}, //CH_Map_6
{0x17, 0x80EC, 2}, //CH_Map_7
{0x18, 0x810C, 2}, //CH_Map_8
{0x19, 0x812C, 2}, //CH_Map_9
{0x1A, 0x814C, 2}, //CH_Map_10
{0x1B, 0x816C, 2}, //CH_Map_11
{0x1C, 0x0001, 2}, //CH_Map_12
{0x1D, 0x0001, 2}, //CH_Map_13
{0x1E, 0x0001, 2}, //CH_Map_14
{0x1F, 0x0001, 2}, //CH_Map_15
{0x20, 0x0300, 2}, //Setup_Config_0
{0x21, 0x0000, 2}, //Setup_Config_1
{0x22, 0x0000, 2}, //Setup_Config_2
{0x23, 0x0000, 2}, //Setup_Config_3
{0x24, 0x0000, 2}, //Setup_Config_4
{0x25, 0x0000, 2}, //Setup_Config_5
{0x26, 0x0000, 2}, //Setup_Config_6
{0x27, 0x0000, 2}, //Setup_Config_7
{0x28, 0x020F, 2}, //Filter_Config_0
{0x29, 0x0200, 2}, //Filter_Config_1
{0x2a, 0x0200, 2}, //Filter_Config_2
{0x2b, 0x0200, 2}, //Filter_Config_3
{0x2C, 0x0200, 2}, //Filter_Config_4
{0x2D, 0x0200, 2}, //Filter_Config_5
{0x2E, 0x0200, 2}, //Filter_Config_6
{0x2F, 0x0200, 2}, //Filter_Config_7
{0x30, 0, 3}, //Offset_0
{0x31, 0, 3}, //Offset_1
{0x32, 0, 3}, //Offset_2
{0x33, 0, 3}, //Offset_3
{0x34, 0, 3}, //Offset_4
{0x35, 0, 3}, //Offset_5
{0x36, 0, 3}, //Offset_6
{0x37, 0, 3}, //Offset_7
{0x38, 0, 3}, //Gain_0
{0x39, 0, 3}, //Gain_1
{0x3a, 0, 3}, //Gain_2
{0x3b, 0, 3}, //Gain_3
{0x3C, 0, 3}, //Gain_4
{0x3D, 0, 3}, //Gain_5
{0x3E, 0, 3}, //Gain_6
{0x3F, 0, 3}, //Gain_7
{0xFF, 0, 1} //Communications_Register
};
#else
extern st_reg AD7175_regs[AD7175_REG_NO];
#endif

#define AD7175_SLAVE_ID ADC_cs

/* Communication Register bits */
#define COMM_REG_WEN (0 << 7)
#define COMM_REG_WR (0 << 6)
#define COMM_REG_RD (1 << 6)

/* Status Register bits */
#define STATUS_REG_RDY (1 << 7)
#define STATUS_REG_ADC_ERR (1 << 6)
#define STATUS_REG_CRC_ERR (1 << 5)
#define STATUS_REG_REG_ERR (1 << 4)
#define STATUS_REG_CH(x) ((x) & 0x03)

/* ADC Mode Register */
#define ADC_MODE_REG_REF_EN (1 << 15)
#define ADC_MODE_REG_DELAY(x) (((x) & 0x7) << 8)
#define ADC_MODE_REG_MODE(x) (((x) & 0x7) << 4)
#define ADC_MODE_REG_CLKSEL(x)) (((x) & 0x3) << 2)

/* Interface Mode Register bits */
#define INTF_MODE_REG_DOUT_RESET (1 << 8)
#define INTF_MODE_REG_CONT_READ (1 << 7)
#define INTF_MODE_REG_DATA_STAT (1 << 6)
#define INTF_MODE_REG_CRC_EN (0x02 << 2)
#define INTF_MODE_REG_CRC_STAT(x) (((x) & INTF_MODE_REG_CRC_EN) == INTF_MODE_REG_CRC_EN)

/* GPIO Configuration Register */
#define GPIO_CONF_REG_MUX_IO (1 << 12)
#define GPIO_CONF_REG_SYNC_EN (1 << 11)
#define GPIO_CONF_REG_ERR_EN(x) (((x) & 0x3) << 9)
#define GPIO_CONF_REG_ERR_DAT (1 << 8)
#define GPIO_CONF_REG_IP_EN1 (1 << 5)
#define GPIO_CONF_REG_IP_EN0 (1 << 4)
#define GPIO_CONF_REG_OP_EN1 (1 << 3)
#define GPIO_CONF_REG_OP_EN0 (1 << 2)
#define GPIO_CONF_REG_DATA1 (1 << 1)
#define GPIO_CONF_REG_DATA0 (1 << 0)

/* ID Register */
#define ID_REG_PRODUCT_ID(x) (((x) & 0xFF) << 8)

/* Channel Map Register 0-7 */
#define CH_MAP_REG_CHEN (1 << 15)
#define CH_MAP_REG_SETUP(x) (((x) & 0x7) << 12)
#define CH_MAP_REG_AINPOS(x) (((x) & 0x1F) << 5)
#define CH_MAP_REG_AINNEG(x) (((x) & 0x1F) << 0)

/* Setup Configuration Register 0-7 */
#define SETUP_CONF_REG_CHOP_MD(x) (((x) & 0x3) << 14)
#define SETUP_CONF_REG_BI_UNIPOLAR (1 << 12)
#define SETUP_CONF_REG_REF_BUF_P (1 << 11)
#define SETUP_CONF_REG_REF_BUF_N (1 << 10)
#define SETUP_CONF_REG_AIN_BUF_P (1 << 9)
#define SETUP_CONF_REG_AIN_BUF_N (1 << 8)
#define SETUP_CONF_REG_BRNOUT_EN (1 << 7)
#define SETUP_CONF_REG_REF_SEL(x) (((x) & 0x3) << 4)

/* Filter Configuration Register 0-7 */
#define FILT_CONF_REG_EXTCLKFREQ(x) (((x) & 0x3) << 13)
#define FILT_CONF_REG_ENHFILTEN (1 << 11)
#define FILT_CONF_REG_ENHFILTSEL(x) (((x) & 0x7) << 8)
#define FILT_CONF_REG_ORDER(x) (((x) & 0x7) << 5)
#define FILT_CONF_REG_ODR(x) (((x) & 0x1F) << 0)


#endif //__AD7175_REGS_H__

 

I have the AD7175-8 eval board and have extensively experimented with register setting and etc and viewed SPI comms on a logic analyzer to understand how the device is configured.

 

I have developed my own PCB with the uC, two SPI I/O expanders and the AD7175-8 in the digital section.  I have successfully communicated with the two SPI Maxim I/O expanders using this processor's SPI on my PC board.

The ADC uses a different SPI Mode 3, than the I/O expanders. 

 

The basis of my ADC communications code is the Analog Device provided Driver C code called "ad7175_generic".  I have modified Communications.h and Communications.c, renamed to adcComms.h and adcComms.c respectively, to use the Atmel Software Foundation library SPI interface.  

The AD7175-8 has been configured by writing to the registers, slowing acquisition to 60 samples/sec and disabling all channels.  This was performed to reduce the occurrence of the DOUT/RDY* pulses (on MISO), which seemed to be read as random data if they occurred during a register read.  I have also sped up the SPI comms to the ADC to 1 MHz.  

 

The SPI configuration writes seemed to complete successfully, however, I cannot be certain because SPI reads are suspect.  The ADC does behave as though its data acquisition process is stopped or slowed.

 

To verify SPI comms with the ADC, I have tried to do as suggested on page 20 of the Analog Devices datasheet and perform a read on the AD7175-8 ID Register.  Note here that the datasheet is a bit unclear regarding the important ID register contents, specified as "0x3CDx". If this register contents should be used to verify comms, then why such a cryptic value?  Is 0x03CD or 0x3CDE?  

 

Read routine for the ADC

/****************************************************************************
* Reads AD7175-8 ADC data using SPI comms.
*
* parameters:
* slaveDeviceId - The ID of the selected slave device.
* data - pointer to the the read buffer.
* bytesNumber - Number of bytes to read.
*
* return - Number of bytes read.
******************************************************************************/
unsigned char adc_SPI_Read(uint8_t slaveDeviceId,
uint16_t* data,
uint8_t bytesNumber)
{
   uint8_t spi_pcs_cs;
   static uint16_t i;
   uint16_t temp;

   i = 0; // first byte in data buffer is the ADC command byte

 

   spi_pcs_cs = spi_get_pcs(slaveDeviceId);

 

   /*** send command byte to the ADC ***/
   spi_write(SPI_MASTER_BASE, data[i], spi_pcs_cs, 0);

   i++;

   /* Wait to receive first byte . */
   while(!(spi_read_status(SPI_MASTER_BASE) & SPI_SR_RDRF)){;}

 

   /* Read ADC data */
   spi_read(SPI_MASTER_BASE, &data[i], &spi_pcs_cs);

 

   for (i = 2; i <= bytesNumber; i++){

      /* Dummy write to clock out the read data */
      spi_write(SPI_MASTER_BASE, (uint16_t)(0xFFFF), spi_pcs_cs, 0);

      while(!(spi_read_status(SPI_MASTER_BASE) & SPI_SR_RDRF)){;}

      /* Read ADC data */
      spi_read(SPI_MASTER_BASE, &data[i], &spi_pcs_cs);
   }
return (unsigned char)(i);
}

 

I proceed by performing a read on the ID register and set a breakpoint after the contents are received to validate the value.  

 

First issue -  The software does not always read both bytes of the ID register. The logic analyzer shows both bytes being transported...

 

Screen shot of the received data, failed to read both bytes of ID register.

 

 

Logic analyzer display of the ID register read sequence. 

 

 

Second issue - It seems I need to read 3 bytes to get all of the register contents.  The register contains only two bytes, but there seems to be a byte
              of junk that needs to be read.  By the way, the read seems correct on the Logic analyzer capture.

 

Third issue - The logic analyzer shows the read bytes being transported in an order that is as expected.  However, the bytes received in the 
              data buffer, over SPI are in an unexpected order.

 

Screen shot of both ID register byte being read, however, not correct order and a junk byte.

 

 

I have battled this for several days and am now stuck. Why does it not always read all of the register contents?

Does the AD7175-8 transmit out a byte as the command opcode is transmitted in, i.e., act as a shift register when receiving a command opcode byte?

 

Why the third junk byte when I read the ID register? 

Why are the bytes from the ID register not in sequence as seen on the logic analyzer?

 

Thanks, in advance, for answers, hints, suggestions, etc..

Mark

Outcomes