This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

ADS8691: ADS8691 SPI communication issue

Part Number: ADS8691

Hello,

I have some problems with Your ADC: ADS8691IPW.

Main issue is lack of reliable work.


It is connected to STM32F411E-DISCO dev board with jumper wires.

ADC itself is soldered into perfboard for debugging purposes.
Primarily it was built into custom PCB with four daisy chained units but problems were similar.


Stm is powered from USB and ADC analog and digital side is supplied with 5V using Keithley SMU.
Decoupling is made using 1uF electrolytic capacitors.

I’ve iterated through multiple adc with similar effects.

Problem is that it works only with oscilloscope connected.
But after reseting stm or connecting ground to logic analyzer or connecting digital pins to logic analyzer or after some time, it stops working.

I can make it work by:

Reseting adc with jumper,

disconnecting jumpers from logic analyzer,

Disconnecting ground from logic analyzer,

Reconnecting ground to logic analyzer,

and many other combinations.



Custom pcb adc schematic:





On custom PCB crosstalk, overshoot and undershoot due to parasitic were considerably lower.
Bypassing was made according to guidelines.

Note, there is a ground lead inductance that makes it look even worse.

(in all screens scope is triggering on SPI bus)
When it works data is clearly displayed





Sometimes after reseting, it looks like this:


or it doesn’t work at all.


I’ ve tried also Pullups and pulldowns in mcu as well as in hardware close to ADC.

Different CLK speeds were also tested.

Code:

part from main:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == ADC_ALARM_Pin)
	{
	   //This block will be triggered after pin activated.
		if(BUTTON_Pin == 1)
		{
			for(int i = 0; i < 4; i++)
			{
			read_hword_data();
	    	char *msg = "ADC Alarm working\r\n";
	        HAL_UART_Transmit(&huart2, msg, sizeof(msg), 100);
			}
		}
	}
}

void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef * hspi)
{
    HAL_SPI_Receive_DMA(&hspi1, RX_Buffer, sizeof(RX_Buffer));
    if(sample < 50000)
    {
    	read_adc_data(&adc_handle, RX_Buffer, sample);
    	sample++;
    }
    else if(sample == 50000)
    {
    	char *msg = "50 000 samples gathered\r\n";
        HAL_UART_Transmit(&huart2, msg, sizeof(msg), 100);
    }

}


Custom library:


struct adc_state adc_init() {
	struct adc_state state;
/*
	for(uint16_t i=0; i<12500; i++) state.data0[i] = '\0';
	for(uint16_t i=0; i<12500; i++) state.data1[i] = '\0';
	for(uint16_t i=0; i<12500; i++) state.data2[i] = '\0';
	for(uint16_t i=0; i<12500; i++) state.data3[i] = '\0';
*/
	return state;
}

/*
 * 11000_xx_<9-bit address>_ <16-bit data>
 * Command used to clear any (or a group of) bits of a register.
 * Any bit marked 1 in the data field results in that particular bit of the specified register being reset to 0, leaving the other bits unchanged.
 * Half-word command (that is, the command functions on 16 bits at a time).
 * LSB of the 9-bit address is always ignored and considered as 0b.(2)
 */
void clear_hword(char *reg)
{
	char command[16];
	sprintf(command, "11000000%s", reg);
	HAL_SPI_Transmit(&hspi1, command, sizeof(command), 0);
}

/*
 * 11001_xx_<9-bit address>_ 00000000_00000000
 * Command used to perform a 16-bit read operation.
 * Half-word command (that is, the device outputs 16 bits of register data at a time).
 * LSB of the 9-bit address is always ignored and considered as 0b.
 * Upon receiving this command, the device sends out 16 bits of the register in the next frame.
 */
void read_hword(char *reg)
{
	char x[8] = "00000000";
	char command[16];
	sprintf(command, "11001000%s", reg);
	HAL_SPI_Transmit(&hspi1, command, sizeof(command), 0);
	HAL_SPI_Transmit(&hspi1, x, sizeof(x), 0);
}


void read_hword_data()
{
	if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == 1)
		{
			HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
			for(int i = 0; i < 4; i++)
			{
				read_hword(DATAOUT_CTL_REG);
			}
			HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
		}
}

/*
 * 01001_xx_<9-bit address>_ 00000000_00000000
 * Same as the READ_HWORD except that only eight bits of the register (byte read) are returned in the next frame.
*/
void read_word(char *reg)
{
	char command[16];
	sprintf(command, "01001000%s", reg);
	HAL_SPI_Transmit(&hspi1, command, sizeof(command), 0);
}

/*
 * 11010_00_<9-bit address>_ <16-bit data>
 * Half-word write command (two bytes of input data are written into the specified address).
 * LSB of the 9-bit address is always ignored and considered as 0b.
 *
 * 11010_01_<9-bit address>_ <16-bit data>
 * Half-word write command.
 * LSB of the 9-bit address is always ignored and considered as 0b.
 * With this command, only the MS byte of the 16-bit data word is written at the specified register address. The LS byte is ignored.
 *
 * 11010_10_<9-bit address>_ <16-bit data>
 * Half-word write command.
 * LSB of the 9-bit address is always ignored and considered as 0b.
 * With this command, only the LS byte of the 16-bit data word is written at the specified register address. The MS byte is ignored.
 */
void write_word(char *reg)
{
	char command[16];
	sprintf(command, "11010000%s", reg);
	HAL_SPI_Transmit(&hspi1, command, sizeof(command), 0);
}

/*
 * 11011_xx_<9-bit address>_ <16-bit data>
 * Command used to set any (or a group of) bits of a register.
 * Any bit marked 1 in the data field results in that particular bit of the specified register being set to 1, leaving the other bits unchanged.
 * Half-word command (that is, the command functions on 16 bits at a time).
 * LSB of the 9-bit address is always ignored and considered as 0b.
 */
void set_hword(char *reg)
{
	char command[16];
	sprintf(command, "11011000%s", reg);
	HAL_SPI_Transmit(&hspi1, command, sizeof(command), 0);
}

/*
 * Configuration described in the documentation in chapter "7.6 Register Maps" (p.48)
 * https://www.ti.com/lit/ds/symlink/ads8691.pdf?HQS=dis-mous-null-mousermode-dsf-pf-null-wwe&ts=1660663610565&ref_url=https%253A%252F%252Fwww.mouser.co.uk%252F
 */
void id_config()
{
	if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == 1)
	{
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
		for(int i = 0; i < 4; i++)
		{
			write_word(DEVICE_ID_REG);
			char command[16];
			sprintf(command,"0000000000000000");
			HAL_SPI_Transmit(&hspi1, command, sizeof(command), 10);
		}
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
	}
}

/*
 * Configuration described in the documentation in chapter "7.6 Register Maps" (p.48)
 * https://www.ti.com/lit/ds/symlink/ads8691.pdf?HQS=dis-mous-null-mousermode-dsf-pf-null-wwe&ts=1660663610565&ref_url=https%253A%252F%252Fwww.mouser.co.uk%252F
 */
void reset_config()
{
	if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == 1)
	{
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
		for(int i = 0; i < 4; i++)
		{
			write_word(RST_PWRCTL_REG);
			char command[16];
			sprintf(command,"0100010100100000");
			HAL_SPI_Transmit(&hspi1, command, sizeof(command), 10);
		}
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
	}
}

/*
 * Configuration described in the documentation in chapter "7.6 Register Maps" (p.48)
 * https://www.ti.com/lit/ds/symlink/ads8691.pdf?HQS=dis-mous-null-mousermode-dsf-pf-null-wwe&ts=1660663610565&ref_url=https%253A%252F%252Fwww.mouser.co.uk%252F
 */
void sdi_config()
{
	if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == 1)
	{
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
		for(int i = 0; i < 4; i++)
		{
			write_word(SDI_CTL_REG);
			char command[16];
			sprintf(command,"0000000000000000");
			HAL_SPI_Transmit(&hspi1, command, sizeof(command), 10);
		}
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
	}
}

/*
 * Configuration described in the documentation in chapter "7.6 Register Maps" (p.48)
 * https://www.ti.com/lit/ds/symlink/ads8691.pdf?HQS=dis-mous-null-mousermode-dsf-pf-null-wwe&ts=1660663610565&ref_url=https%253A%252F%252Fwww.mouser.co.uk%252F
 */
void sdo_config()
{
	if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == 1)
	{
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
		for(int i = 0; i < 4; i++)
		{
			write_word(SDO_CTL_REG);
			char command[16];
			sprintf(command,"0000000100000000");
			HAL_SPI_Transmit(&hspi1, command, sizeof(command), 10);
		}
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
	}
}

/*
 * Configuration described in the documentation in chapter "7.6 Register Maps" (p.48)
 * https://www.ti.com/lit/ds/symlink/ads8691.pdf?HQS=dis-mous-null-mousermode-dsf-pf-null-wwe&ts=1660663610565&ref_url=https%253A%252F%252Fwww.mouser.co.uk%252F
 */
void data_config()
{
	if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == 1)
	{
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
		for(int i = 0; i < 4; i++)
		{
			write_word(DATAOUT_CTL_REG);
			char command[16];
			sprintf(command,"0100110000000000");
			HAL_SPI_Transmit(&hspi1, command, sizeof(command), 10);
		}
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
	}
}

/*
 * Configuration described in the documentation in chapter "7.6 Register Maps" (p.48)
 * https://www.ti.com/lit/ds/symlink/ads8691.pdf?HQS=dis-mous-null-mousermode-dsf-pf-null-wwe&ts=1660663610565&ref_url=https%253A%252F%252Fwww.mouser.co.uk%252F
 */
void ref_vol_config()
{
	if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == 1)
	{
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
		for(int i = 0; i < 4; i++)
		{
			write_word(RANGE_SEL_REG);
			char command[16];
			sprintf(command,"0000000000000000");
			HAL_SPI_Transmit(&hspi1, command, sizeof(command), 10);
		}
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
	}
}

/*
 * Configuration described in the documentation in chapter "7.6 Register Maps" (p.48)
 * https://www.ti.com/lit/ds/symlink/ads8691.pdf?HQS=dis-mous-null-mousermode-dsf-pf-null-wwe&ts=1660663610565&ref_url=https%253A%252F%252Fwww.mouser.co.uk%252F
 */
void alarms_config()
{
	if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == 1)
	{
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
		for(int i = 0; i < 4; i++)
		{
			write_word(ALARM_REG);
			char command[16];
			sprintf(command,"0000110000000000");
			HAL_SPI_Transmit(&hspi1, command, sizeof(command), 10);
		}
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
	}
}

/*
 * Configuration described in the documentation in chapter "7.6 Register Maps" (p.48)
 * https://www.ti.com/lit/ds/symlink/ads8691.pdf?HQS=dis-mous-null-mousermode-dsf-pf-null-wwe&ts=1660663610565&ref_url=https%253A%252F%252Fwww.mouser.co.uk%252F
 */
void alarmh_config()
{
	if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == 1)
	{
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
		for(int i = 0; i < 4; i++)
		{
			write_word(ALARM_H_TH_REG);
			char command[16];
			sprintf(command,"0000000000000000");
			HAL_SPI_Transmit(&hspi1, command, sizeof(command), 10);
		}
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
	}
}

/*
 * Configuration described in the documentation in chapter "7.6 Register Maps" (p.48)
 * https://www.ti.com/lit/ds/symlink/ads8691.pdf?HQS=dis-mous-null-mousermode-dsf-pf-null-wwe&ts=1660663610565&ref_url=https%253A%252F%252Fwww.mouser.co.uk%252F
 */
void alarml_config()
{
	if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == 1)
	{
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
		for(int i = 0; i < 4; i++)
		{
			write_word(ALARM_L_TH_REG);
			char command[16];
			sprintf(command,"0000000000000000");
			HAL_SPI_Transmit(&hspi1, command, sizeof(command), 10);		}
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
	}
}

// Initialization of all ADC's registers
void adc_config()
{
	id_config();
	reset_config();
	sdi_config();
	sdo_config();
	data_config();
	ref_vol_config();
	alarms_config();
	alarmh_config();
	alarml_config();

}

//
void read_adc_data(struct adc_state *state, uint8_t *val, uint16_t x)
{
	x = x/4;
	receiver_check(state, val, x);
	read_hword_data();

}

//
void receiver_check(struct adc_state *state, uint8_t *val, uint16_t x)
{
	int16_t data;
	data = (val[3] << 8) | val[2];



  • Hello Radoslaw,

    Thank you for your post.

    I would begin debugging by trying ways to improve the setup robustness. A few pointers:

    1. Share a solid GND connection between ADC board, STM board, and external power supply. Also ensure solid ground connections between AIN_GND and DGND. A common ground is generally recommended.
    2. To reduce overshoot and ringing on SPI lines, add some series termination (i.e. 33 ohms) to dampen the high-frequency content. A small capacitance on SCLK (i.e. 10 pF) can also help to achieve the same thing. This may be why the scope probes appear to help.
    3. Once power supplies are on and stable, a RESET pulse may help ensure ADCs all power up in a known good state. Then proceed to configure any registers if necessary.

    Best regards,

    Ryan

  • Hello Ryan,

    thank You for your reply, unfortunately, it did not resolve my issue.

    I’ve applied all suggested changes as well as increasing capacitance to increase HF loading.
    For some reason, I am able to make it work by touching pins with finger, but only when Reset is floating and after establishing data transfer, pulling it high.

    It looks like signal integrity issue but doing everything according to datasheet doesn’t work.

    After measuring all pins and supply voltages, there are not forbidden state present as well.

    Perhaps units I’ve got are defective.



  • Hello Radosaw,

    The RSTn pin should not float. It should always be driven high or low. By default, the RSTn pin is used for power-on reset (POR). After the POR time delay (tD_RST_POR), you may pull RSTn high and begin data acquisition.

    So far this does not appear to be a device issue. As you stated, it is working sometimes. I believe improving your setup and power-on sequence will yield more consistent results.

    Regards,

    Ryan