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.

ADS131M08EVM: Issues reading data from ADS131M08EVM with STM32.Need C Sample Driver Code

Part Number: ADS131M08EVM
Other Parts Discussed in Thread: ADS131M08, ADS131M06

Hello everyone, hope you are well!

Me and my team are developing an energy meter using an MCU STM32F756ZG with HAL Library and an ADS131M08EVM to acquire the signals.

We were able to start the communication between the STM32 and the ADSADS131M08EVM and change the the OSR to 128 and see that change monitoring the DRDY pin. But we´re finding some issues:

-The first one is when we sent the commands to ADSADS131M08EVM, the answer is not the right  at the first time. For example when we send the command Status, we expect the answer "0x500", and we only receive this after a few  times. We are trying the Status and Clock commands. We were only able to change the clock putting the commands in a while loop, we send the command until we receive the right answer.

- The second one, and main problem, is that we are not able to read the data of the ADS131M08EVM  channels, we can see in the IDE the data for SPI but the data do not correspond to the real values.

Do you have any example C code using the MCU STM32 and an ADS131M08EVM?

If you want I can share my .C and .H codes.

Best Regards

Rafael Cordini

  • Rafael,

    We do not have example code on the Metrology library for STM32 since that is not a TI processors. However, we have had customers port this library to STM32 in the past. Please send the following header files to understand your configuration:

    • emeter-app/metrology-template.h
    • emeter-metrology/metrology-template.h

    Also please send a logic analyzer output of the SPI transfers to understand what values are sent and received on the SPI bus.

    A few things to consider:

    • Ensure that ADS131M08_CONFIGURATION is defined, so that the appropriate number of channels is used for SPI transfers. On the release code this was defined if the xTIDA010225_CONNECTIONS was set.
    • Ensure that a valid clock is sent to the ADS131M08 and the appropriate SAMPLE_RATE is selected in metrology-template.h and corresponds to the input clock rate to the ADS131M08.

    Best regards,
    Pedro

  • Hi Pedro, thank you for your quick answer.

    I was talking with my colleagues, and we did not found the TIDA010225, could you send to us this documentation?

    Here is our codes:

    Header file:

    /*
     * ads131m06.h
     *
     *  Created on: 10 de nov de 2021
     *      Author: rsk
     */
    
    #ifndef INC_ADS131M06_H_
    #define INC_ADS131M06_H_
    
    // C Standard Libraries
    #include <assert.h>
    #include <stdint.h>
    
    #include "main.h"
    #include "spi.h"
    #include "gpio.h"
    
    
    
    //****************************************************************************
    //
    //  GENERAL DEFINITIONS
    //
    //****************************************************************************
    
    #define ADS131M06_NUM_CHANNELS 		8		//	!!!!MODIFICAR PARA O ADS DE 6 CANAIS!!!!
    #define ADS131M06_WORD_SIZE 		3
    
    typedef struct _ADS131M06Data_t_
    {
    	int32_t Ch0;
    	int32_t Ch1;
    	int32_t Ch2;
    	int32_t Ch3;
    	int32_t Ch4;
    	int32_t Ch5;
    	int32_t Ch6;			//	!!!!MODIFICAR PARA O ADS DE 6 CANAIS!!!!
    	int32_t Ch7;			//	!!!!MODIFICAR PARA O ADS DE 6 CANAIS!!!!
    	uint16_t Status;
    	uint16_t Checksum;
    } ADS131M06Data_t;
    
    extern ADS131M06Data_t ADSChannelData;
    
    //****************************************************************************
    //
    //  ADS131M08 COMMANDS
    //
    //****************************************************************************
    //System Commands
    #define ADS131M06_CMD_NULL						((uint16_t) 0x0000)
    #define ADS131M06_CMD_RESET						((uint16_t) 0x0011)
    #define ADS131M06_CMD_STANDBY					((uint16_t) 0x0022)
    #define ADS131M06_CMD_WAKEUP					((uint16_t) 0x0033)
    #define ADS131M06_CMD_LOCK						((uint16_t) 0x0555)
    #define ADS131M06_CMD_UNLOCK					((uint16_t) 0x0655)
    
    //Registers Read/Write Commands
    #define ADS131M06_CMD_RREG						((uint16_t) 0xA000)
    #define ADS131M06_CMD_RREGS						((uint16_t) 0xA000)
    #define ADS131M06_CMD_WREG						((uint16_t) 0x6000)
    #define ADS131M06_CMD_WREGS						((uint16_t) 0x6000)
    
    
    
    //****************************************************************************
    //
    //  ADS131M08 REGISTERS
    //
    //****************************************************************************
    
    /* Register 0x00 (ID) definition - READ ONLY */
    #define ID_ADDRESS                              ((uint8_t)  0x00)
    #define ID_DEFAULT                              ((uint16_t) 0x2000 | (ADS131M06_NUM_CHANNELS << 8))
    /* Register 0x01 (STATUS) definition - READ ONLY */
    #define STATUS_ADDRESS                          ((uint8_t)  0x01)
    #define STATUS_DEFAULT                          ((uint16_t) 0x0500)
    /* Register 0x02 (MODE) definition */
    #define MODE_ADDRESS                            ((uint8_t)  0x02)
    #define MODE_DEFAULT                            ((uint16_t) 0x0510)
    /* Register 0x03 (CLOCK) definition */
    #define CLOCK_ADDRESS                           ((uint8_t)  0x03)
    #define CLOCK_DEFAULT                           ((uint16_t) 0xFF02)	//OSR = 128 -> 32 kHz sample frequency
    
    
    
    
    //****************************************************************************
    //
    //  FUNCTION PROTOTYPES
    //
    //****************************************************************************
    
    // Low level
    void 	 ADS131M06Reset(void);
    void 	 ADS131M06SetCS (uint8_t state);
    void 	 ADS131M06XferWord(uint8_t* txData, uint8_t* rxData);
    uint16_t ADS131M06SendCmd(uint16_t cmd);
    
    // Higher level
    uint16_t ADS131M06WriteRegister(uint8_t reg, uint8_t data);
    void	 ADS131M06Init(void);
    void	 ADS131M06GetChannels(void);
    void 	 ADS_Init(void);
    
    #endif /* INC_ADS131M06_H_ */

    And the C code.

    #include "ads131m06.h"
    #include "calcFunctions.h"
    #include "spi.h"
    #include "gpio.h"
    
    
    
    
    extern SPI_HandleTypeDef hspi4;
    uint8_t emptyTxBuffer[ADS131M06_WORD_SIZE*(ADS131M06_NUM_CHANNELS+1)] = {0};
    uint8_t ADS131M06_DataBuf[ADS131M06_WORD_SIZE*(ADS131M06_NUM_CHANNELS+1)];
    volatile uint8_t ADS131M06_Ready_flag = 0;
    uint8_t ADS131M06_Init_Done = 0;
    float verFS = FS;
    
    ADS131M06Data_t ADSChannelData;
    
    
    //****************************************************************************
    //
    //  LOW LEVEL FUNCTIONS
    //
    //****************************************************************************
    
    // Transmit/Receive data (write/read register)
    void ADS131M06XferWord(uint8_t* txData, uint8_t* rxData)
    {
    
    	//HAL_SPI_TransmitReceive_IT(&hspi4, txData, rxData, ADS131M06_WORD_SIZE);
    	HAL_SPI_TransmitReceive(&hspi4, txData, rxData, ADS131M06_WORD_SIZE,1);
    
    }
    
    // Transmit/Receive system command
    uint16_t ADS131M06SendCmd(uint16_t cmd)
    {
    
    	uint8_t txData[ADS131M06_WORD_SIZE] = {0};
    	static uint8_t rxData[ADS131M06_WORD_SIZE];
    	uint16_t res = 0;
    
    	//split 16bit cmd in 8bit array
    	txData[1] = (cmd & 0xff);
    	txData[0] = (cmd >> 8);
    
    	//Send the command
    	ADS131M06XferWord(txData, rxData);
    
    	//The response of the previous cmd is in the next response
    	//So send another empty cmd to get the response
    	//Collapse response
    	res = (((uint16_t)rxData[0] << 8) | rxData[1]);
    
    	return res;
    
    }
    
    
    
    //****************************************************************************
    //
    //  HIGH LEVEL FUNCTIONS
    //
    //****************************************************************************
    
    // Write single register function
    uint16_t ADS131M06WriteRegister(uint8_t addr, uint8_t data)
    {
    
    	uint16_t word = (ADS131M06_CMD_WREG | (addr<<7));
    
    	uint16_t value = ADS131M06SendCmd(word);
    	word = (0xff << 8) | data;
    	value = ADS131M06SendCmd(word);
    
    	return value;
    
    }
    
    // Check the status and set the sampling rate to 32 kSPS -> 32 kHz
    void ADS131M06Init(void)
    {
    
    	uint16_t status = ADS131M06SendCmd((uint16_t) 0x0000);
    
    	while(status != 0x0500){
    		status = ADS131M06SendCmd(0x0000);
    	}
    	HAL_GPIO_TogglePin(blueLed);
    	uint16_t responseWREG = ADS131M06WriteRegister(CLOCK_ADDRESS, 0xFF02);
    
    	while(responseWREG != 0x4180){
    		responseWREG = ADS131M06WriteRegister(CLOCK_ADDRESS, 0xFF02);//CLOCK_DEFAULT);
    	}
    	HAL_GPIO_TogglePin(greenLed);
    }
    
    // Concatenate to result in 24bit read
    void ADS131M06GetChannels(void)
    {
    
    	ADSChannelData.Status = ADS131M06_DataBuf[0]<<8 | ADS131M06_DataBuf[1];
    	ADSChannelData.Ch0 = (ADS131M06_DataBuf[4] << 16) | (ADS131M06_DataBuf[5] << 8) | (ADS131M06_DataBuf[6]);
    	
    
    	ADSChannelData.Ch1 = (ADS131M06_DataBuf[8] << 16) | (ADS131M06_DataBuf[9] << 8) | (ADS131M06_DataBuf[10]);
    	ADSChannelData.Ch2 = (ADS131M06_DataBuf[12] << 16) | (ADS131M06_DataBuf[13] << 8) | (ADS131M06_DataBuf[14]);
    	ADSChannelData.Ch3 = (ADS131M06_DataBuf[16] << 16) | (ADS131M06_DataBuf[17] << 8) | (ADS131M06_DataBuf[18]);
    	ADSChannelData.Ch4 = (ADS131M06_DataBuf[20] << 16) | (ADS131M06_DataBuf[21] << 8) | (ADS131M06_DataBuf[22]);
    	ADSChannelData.Ch5 = (ADS131M06_DataBuf[24] << 16) | (ADS131M06_DataBuf[25] << 8) | (ADS131M06_DataBuf[26]);
    
    
    	ADSChannelData.Ch6 = (ADS131M06_DataBuf[28] << 16) | (ADS131M06_DataBuf[29] << 8) | (ADS131M06_DataBuf[30]);
    	ADSChannelData.Ch7 = (ADS131M06_DataBuf[32] << 16) | (ADS131M06_DataBuf[33] << 8) | (ADS131M06_DataBuf[34]);
    
    }
    
    // Initializes ADS and after that, enable the DRDY_Pin interruption to get ADC data
    void ADS_Init(void)
    {
    
    	ADS131M06Init();
    	CalcFunctions_Reset();
    	CalcFunctions_Init();
    
    
    	ADS131M06_Init_Done = 1;
    
    	// EXTI interrupt init
    	HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0);
    	HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
    
    }
    
    // DRDY interruption
    // Executes every DRDY_GPIO_PIN interruption = 32 kHz
    void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
    {
    	if(GPIO_Pin == DRDY_ADS_Pin && ADS131M06_Init_Done == 1)
    	{
    		//SPI Interrupt
    		HAL_NVIC_SetPriority(SPI4_IRQn, 0, 0);
    	    HAL_NVIC_EnableIRQ(SPI4_IRQn);
    		//ADS131M06_Ready_flag = 1;
    	
    		/* Read ADS registers via SPI */
    
    		HAL_SPI_TransmitReceive_IT(&hspi4, &emptyTxBuffer[0], &ADS131M06_DataBuf, ADS131M06_WORD_SIZE*(ADS131M06_NUM_CHANNELS+1));
    		CalcFunctions_Run(ADSChannelData);
    
    	
    
    	}
    }
    
    void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef * hspi)
    {
    	HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_11);
    	ADS131M06GetChannels();
    
    
    }
    
    
    //****************************************************************************
    //
    //  EXAMPLE OF DATA FRAME
    //
    //****************************************************************************
    
    /*
     * Set OSR = 128 by writing in the COLCK register
     *
     * 011a aaaa annn nnnn 	= OPCODE_WREG 		= 0x6000 	= 0110 0000 0000 0000
     * a aaaa a 			= CLOCK_ADDRESS 	= 0x03 		= 0011
     * nnn nnnn				= NUMBER_REG - 1	= 0x00 		= 000 0000
     * 		 				= COMMAND			= 0x6180	= 0110 0001 1000 0000
     *														= 0100 0001 1000 0000
     * DATA					= CLOCK_DEFAULT		= 0xFF02	= 1111 1111 0000 0010 - OSR = 128
     *
     * dataTxX[0] = 0x61;
     * dataTxX[1] = 0x80;
     * dataTxX[2] = 0xFF;
     * dataTxX[3] = 0x02;
     * spiSendReceiveArrays(dataTxX, dataRxX, 4);
     */
    

    The logic analyzer output and the SPI data I will be able to sent you tomorrow.

    Thank you for the help.

  • Rafael,

    I misunderstood part of your original question. I had assumed you had downloaded the Energy Metrology Library. This library has code for electricity meters, power-distribution units (PDUs), and circuit breakers. It also includes a hardware abstracted driver for ADS131M0x devices. It has been compiled for MSP432P processor, but customers have ported over to STM32 with the included CMSIS SPI drivers. The CMSIS drivers will be used if the code is compiled with IAR IDE since it defines __ICCARM__.

    The ADS131M0x C code is in the folder ADC/adchal.c and ADC/ads131m0x.c.

    Also, there is an example C code with a similar driver in ADS131M0x Example C Code

    The file ads131m0x.c has a routine buildSPIarray that shows how the command bytes are created. Note the the SPI frame size is dependent on the word size programmed into the device. The word size is configurable as either 16 bits, 24 bits, or 32 bits by programming the WLENGTH[1:0] bits in the MODE register. When the ADC is disabled and no conversion data is outputs, a data frame can be shortened by the host. See the Short SPI Frames section for more information about artificially shortening communication frames. To simplify things, I tend to prefer to just use a fixed SPI frame set to the normal length SPI frame. In short, the SPI frame size is:

    numberOfBytes =  (CommandWords + optionalCRCWord) * WLENGTH_BYTES

    Thus a command could have 2, 3, or 4 words, with each word being 16-bits (2 bytes), 24-bits (3 bytes), or 32-bits (4 bytes).

    In your code, commands seem to be just 3 bytes since 

    uint8_t txData[ADS131M06_WORD_SIZE] 

    #define ADS131M06_WORD_SIZE 3

    This might be too short for some commands. Using, the above definition for numberOfBytes and assuming CRC is enabled and the device is programmed for 24-bit conversions

    - For RREG, there is 1 command word with opcode:

    opcode = OPCODE_RREG | (((uint16_t) address) << 7);

    numberOfBytes = (1 + 1)  * 3 = 6

    - For WREG, there are 2 command words with opcodes:

    opcodes[0] = OPCODE_WREG | (((uint16_t) address) << 7);
    opcodes[1] = data;

    numberOfBytes = (2 + 1)  * 3 = 9

    - For other commands, there are 2 command words with opcodes:

    opcode = command
    numberOfBytes = (1 + 1)  * 3 = 6

    Using, the above definition for numberOfBytes and not enabling CRC and the device is programmed for 24-bit conversions

    - For RREG, there is 1 command word with opcode:

    opcode = OPCODE_RREG | (((uint16_t) address) << 7);

    numberOfBytes = (1 + 0)  * 3 = 3

    - For WREG, there are 2 command words with opcodes:

    opcodes[0] = OPCODE_WREG | (((uint16_t) address) << 7);
    opcodes[1] = data;

    numberOfBytes = (2 + 0)  * 3 = 6

    - For other commands, there are 2 command words with opcodes:

    opcode = command
    numberOfBytes = (1 + 0)  * 3 = 3

    Please look at ADS131M0x Example C Code or Energy Metrology Library C code for more examples.

    Best regards,
    Pedro