885 lines
31 KiB
C
885 lines
31 KiB
C
|
/*
|
|||
|
* @Author: mypx
|
|||
|
* @Email: mypx_coder@163.com
|
|||
|
* @Date: 2025-06-23 10:26:12
|
|||
|
* @LastEditors: mypx mypx_coder@163.com
|
|||
|
* @Description: AD7793 driver implementation
|
|||
|
*/
|
|||
|
#include "ad7793.h"
|
|||
|
#include <stdbool.h>
|
|||
|
#include <stdio.h>
|
|||
|
|
|||
|
#ifdef DEBUG
|
|||
|
#include <rtthread.h>
|
|||
|
#define DEBUG_PRINTF(fmt, ...) \
|
|||
|
do { \
|
|||
|
rt_kprintf("[AD7793] " fmt, ##__VA_ARGS__); \
|
|||
|
} while (0)
|
|||
|
#else
|
|||
|
#define DEBUG_PRINTF(fmt, ...) \
|
|||
|
do { \
|
|||
|
} while (0)
|
|||
|
#endif
|
|||
|
/**
|
|||
|
* @brief Send SPI command and receive data.
|
|||
|
* @param dev Pointer to ad7793_dev_t instance.
|
|||
|
* @param cmd Command byte to send.
|
|||
|
* @param tx_buf Transmit buffer.
|
|||
|
* @param rx_buf Receive buffer.
|
|||
|
* @param len Data length to transfer.
|
|||
|
* @return Operation status. True if successful, false otherwise.
|
|||
|
*/
|
|||
|
static bool ad7793_spi_transfer(ad7793_dev_t *dev, uint8_t cmd, uint8_t *tx_buf, uint8_t *rx_buf, uint16_t len)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
uint8_t tx[32] = {0};
|
|||
|
uint8_t rx[32] = {0};
|
|||
|
|
|||
|
tx[0] = cmd;
|
|||
|
if (tx_buf)
|
|||
|
{
|
|||
|
for (uint16_t i = 0; i < len; i++)
|
|||
|
{
|
|||
|
tx[i + 1] = tx_buf[i];
|
|||
|
}
|
|||
|
}
|
|||
|
dev->hw_if->spi_transfer(tx, rx, len + 1);
|
|||
|
|
|||
|
if (rx_buf)
|
|||
|
{
|
|||
|
for (uint16_t i = 0; i < len; i++)
|
|||
|
{
|
|||
|
rx_buf[i] = rx[i + 1];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Write communication register.
|
|||
|
* @param dev Pointer to ad7793_dev_t instance.
|
|||
|
* @param cmd Command byte to write to the communication register.
|
|||
|
* @return Operation status. True if successful, false otherwise.
|
|||
|
*/
|
|||
|
bool ad7793_write_communication(ad7793_dev_t *dev, uint8_t cmd)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
dev->hw_if->gpio_set(dev->cs_pin, false);
|
|||
|
ad7793_spi_transfer(dev, cmd, NULL, NULL, 0);
|
|||
|
dev->hw_if->gpio_set(dev->cs_pin, true);
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Write data to a specified register.
|
|||
|
* @param dev Pointer to ad7793_dev_t instance.
|
|||
|
* @param reg Register address to write to.
|
|||
|
* @param data Data buffer to be written.
|
|||
|
* @param len Data length (2/3 bytes).
|
|||
|
* @return Operation status. True if successful, false otherwise.
|
|||
|
* @detail This function constructs a communication command to write data to the specified register.
|
|||
|
* It first clears the chip select pin, then performs an SPI transfer to send the command and data.
|
|||
|
* Finally, it sets the chip select pin back to high.
|
|||
|
*/
|
|||
|
bool ad7793_write_reg(ad7793_dev_t *dev, uint8_t reg, uint32_t data, uint16_t len)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
uint8_t cmd = 0;
|
|||
|
uint8_t tmp[4] = {0};
|
|||
|
|
|||
|
/* Build communication command: WEN=0, R/W=0(write), register address */
|
|||
|
cmd = (reg & 0x07) << 3;
|
|||
|
cmd &= ~COMM_RW;
|
|||
|
cmd &= ~COMM_WEN;
|
|||
|
|
|||
|
if (len == 1)
|
|||
|
{
|
|||
|
tmp[0] = data >> 0;
|
|||
|
}
|
|||
|
else if (len == 2)
|
|||
|
{
|
|||
|
tmp[0] = data >> 8;
|
|||
|
tmp[1] = data >> 0;
|
|||
|
}
|
|||
|
else if (len == 3)
|
|||
|
{
|
|||
|
tmp[0] = data >> 16;
|
|||
|
tmp[1] = data >> 8;
|
|||
|
tmp[2] = data >> 0;
|
|||
|
}
|
|||
|
dev->hw_if->gpio_set(dev->cs_pin, false);
|
|||
|
dev->hw_if->spi_write(cmd, tmp, len);
|
|||
|
//ad7793_spi_transfer(dev, cmd, data, NULL, len);
|
|||
|
dev->hw_if->gpio_set(dev->cs_pin, true);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Read data from a specified register.
|
|||
|
* @param dev Pointer to ad7793_dev_t instance.
|
|||
|
* @param reg Register address to read from.
|
|||
|
* @param data Buffer to store the read data.
|
|||
|
* @param len Data length (2/3 bytes).
|
|||
|
* @return Operation status. True if successful, false otherwise.
|
|||
|
* @detail This function constructs a communication command to read data from the specified register.
|
|||
|
* It first clears the chip select pin, then performs an SPI transfer to receive the data.
|
|||
|
* Finally, it sets the chip select pin back to high.
|
|||
|
*/
|
|||
|
bool ad7793_read_reg(ad7793_dev_t *dev, uint8_t reg, uint32_t *data, uint16_t len)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
uint8_t cmd = 0;
|
|||
|
uint8_t tmp[4] = {0};
|
|||
|
|
|||
|
/* Build communication command: WEN=0, R/W=1(read), register address */
|
|||
|
cmd = (reg & 0x07) << 3;
|
|||
|
cmd |= COMM_RW; // 0: Write operation; 1: Read operation
|
|||
|
cmd &= ~COMM_WEN; // write enable bit should be 0 for read operation
|
|||
|
|
|||
|
dev->hw_if->gpio_set(dev->cs_pin, false);
|
|||
|
dev->hw_if->spi_read(cmd, tmp, len);
|
|||
|
//ad7793_spi_transfer(dev, cmd, NULL, data, len);
|
|||
|
//dev->hw_if->delay_ms(1);
|
|||
|
dev->hw_if->gpio_set(dev->cs_pin, true);
|
|||
|
if (len == 1)
|
|||
|
{
|
|||
|
*data = tmp[0] << 0;
|
|||
|
}
|
|||
|
else if (len == 2)
|
|||
|
{
|
|||
|
*data = tmp[0] << 8 | tmp[1] << 0;
|
|||
|
}
|
|||
|
else if (len == 3)
|
|||
|
{
|
|||
|
*data = tmp[0] << 16 | tmp[1] << 8 | tmp[2] << 0;
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Reset the device.
|
|||
|
* @param dev Pointer to ad7793_dev_t instance.
|
|||
|
* @return Operation status. True if successful, false otherwise.
|
|||
|
* @detail This function resets the device by writing 32 ones to the device.
|
|||
|
* It clears the chip select pin, sends the reset sequence via SPI, and then sets the chip select pin back to high.
|
|||
|
* After that, it waits for 1 millisecond to ensure the reset is completed.
|
|||
|
*/
|
|||
|
bool ad7793_reset(ad7793_dev_t *dev)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
dev->hw_if->gpio_set(dev->cs_pin, false);
|
|||
|
|
|||
|
uint8_t reset_seq[4] = {0xFF, 0xFF, 0xFF, 0xFF};
|
|||
|
dev->hw_if->spi_transfer(reset_seq, NULL, 4);
|
|||
|
|
|||
|
dev->hw_if->gpio_set(dev->cs_pin, true);
|
|||
|
dev->hw_if->delay_ms(1); /* Wait for reset to complete */
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Set the working mode of the device.
|
|||
|
* @param dev Pointer to ad7793_dev_t instance.
|
|||
|
* @param mode Mode enum value representing the desired working mode.
|
|||
|
* @return Operation status. True if successful, false otherwise.
|
|||
|
* @detail This function reads the current mode register, clears the mode bits, and then sets the new mode according to the input.
|
|||
|
* It updates the current mode in the device structure and writes the new mode to the mode register.
|
|||
|
*/
|
|||
|
bool ad7793_set_mode(ad7793_dev_t *dev, ad7793_mode_t mode)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
uint32_t mode_reg = 0;
|
|||
|
ad7793_read_reg(dev, REG_MODE, &mode_reg, 2);
|
|||
|
DEBUG_PRINTF("read current mode: 0x%04X\n", mode_reg);
|
|||
|
/* Clear mode bits */
|
|||
|
mode_reg &= ~(MODE_MD2 | MODE_MD1 | MODE_MD0);
|
|||
|
|
|||
|
/* Set new mode */
|
|||
|
switch (mode)
|
|||
|
{
|
|||
|
case AD7793_MODE_CONTINUOUS:
|
|||
|
break; /* Default mode, MD2-MD0=000 */
|
|||
|
case AD7793_MODE_SINGLE:
|
|||
|
mode_reg |= MODE_MD0;
|
|||
|
break;
|
|||
|
case AD7793_MODE_IDLE:
|
|||
|
mode_reg |= MODE_MD1;
|
|||
|
break;
|
|||
|
case AD7793_MODE_POWER_DOWN:
|
|||
|
mode_reg |= MODE_MD1 | MODE_MD0;
|
|||
|
break;
|
|||
|
case AD7793_MODE_INTERNAL_ZERO:
|
|||
|
mode_reg |= MODE_MD2;
|
|||
|
break;
|
|||
|
case AD7793_MODE_INTERNAL_FULL:
|
|||
|
mode_reg |= MODE_MD2 | MODE_MD0;
|
|||
|
break;
|
|||
|
case AD7793_MODE_SYSTEM_ZERO:
|
|||
|
mode_reg |= MODE_MD2 | MODE_MD1;
|
|||
|
break;
|
|||
|
case AD7793_MODE_SYSTEM_FULL:
|
|||
|
mode_reg |= MODE_MD2 | MODE_MD1 | MODE_MD0;
|
|||
|
break;
|
|||
|
}
|
|||
|
DEBUG_PRINTF("[mode-w]set mode: 0x%04X\n", mode_reg);
|
|||
|
dev->cur_mode = mode;
|
|||
|
|
|||
|
return ad7793_write_reg(dev, REG_MODE, mode_reg, 2);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Set the gain of the device.
|
|||
|
* @param dev Pointer to ad7793_dev_t instance.
|
|||
|
* @param gain Gain enum value representing the desired gain setting.
|
|||
|
* @return Operation status. True if successful, false otherwise.
|
|||
|
* @detail This function reads the current configuration register, clears the gain bits, and then sets the new gain according to the input.
|
|||
|
* It updates the current gain in the device structure and writes the new configuration to the configuration register.
|
|||
|
*/
|
|||
|
bool ad7793_set_gain(ad7793_dev_t *dev, ad7793_gain_t gain)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
uint32_t config_reg = 0;
|
|||
|
ad7793_read_reg(dev, REG_CONFIG, &config_reg, 2);
|
|||
|
|
|||
|
/* Clear gain bits (G2/G1/G0) */
|
|||
|
config_reg &= ~(CONFIG_G2 | CONFIG_G1 | CONFIG_G0);
|
|||
|
|
|||
|
/* Set new gain (PDF->table 17) */
|
|||
|
switch (gain)
|
|||
|
{
|
|||
|
case AD7793_GAIN_1: // 000
|
|||
|
break;
|
|||
|
case AD7793_GAIN_2: // 001
|
|||
|
config_reg |= CONFIG_G0;
|
|||
|
break;
|
|||
|
case AD7793_GAIN_4: // 010
|
|||
|
config_reg |= CONFIG_G1;
|
|||
|
break;
|
|||
|
case AD7793_GAIN_8: // 011
|
|||
|
config_reg |= CONFIG_G1 | CONFIG_G0;
|
|||
|
break;
|
|||
|
case AD7793_GAIN_16: // 100
|
|||
|
config_reg |= CONFIG_G2;
|
|||
|
break;
|
|||
|
case AD7793_GAIN_32: // 101
|
|||
|
config_reg |= CONFIG_G2 | CONFIG_G0;
|
|||
|
break;
|
|||
|
case AD7793_GAIN_64: // 110
|
|||
|
config_reg |= CONFIG_G2 | CONFIG_G1;
|
|||
|
break;
|
|||
|
case AD7793_GAIN_128: // 111
|
|||
|
config_reg |= CONFIG_G2 | CONFIG_G1 | CONFIG_G0;
|
|||
|
break;
|
|||
|
default:
|
|||
|
DEBUG_PRINTF("Invalid gain value: %d\n", gain);
|
|||
|
return false; // unavailable gain
|
|||
|
}
|
|||
|
DEBUG_PRINTF("[config-w]set gain: 0x%04X\n", config_reg);
|
|||
|
dev->cur_gain = gain;
|
|||
|
return ad7793_write_reg(dev, REG_CONFIG, config_reg, 2);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Set the channel of the device.
|
|||
|
* @param dev Pointer to ad7793_dev_t instance.
|
|||
|
* @param channel Channel enum value representing the desired channel.
|
|||
|
* @return Operation status. True if successful, false otherwise.
|
|||
|
* @detail This function reads the current configuration register, clears the channel bits,
|
|||
|
* and then sets the new channel according to the input.It updates the current
|
|||
|
* channel in the device structure and writes the new configuration to the configuration register.
|
|||
|
*/
|
|||
|
bool ad7793_set_channel(ad7793_dev_t *dev, ad7793_channel_t channel)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
uint32_t config_reg = 0;
|
|||
|
ad7793_read_reg(dev, REG_CONFIG, &config_reg, 2);
|
|||
|
|
|||
|
/* Clear channel bits */
|
|||
|
config_reg &= ~(CONFIG_CH2 | CONFIG_CH1 | CONFIG_CH0);
|
|||
|
|
|||
|
/* Set new channel */
|
|||
|
switch (channel)
|
|||
|
{
|
|||
|
case AD7793_CHANNEL_1:
|
|||
|
break; /* CH2-CH0=000 */
|
|||
|
case AD7793_CHANNEL_2:
|
|||
|
config_reg |= CONFIG_CH0;
|
|||
|
break;
|
|||
|
case AD7793_CHANNEL_3:
|
|||
|
config_reg |= CONFIG_CH1;
|
|||
|
break;
|
|||
|
case AD7793_CHANNEL_TEMP:
|
|||
|
config_reg |= CONFIG_CH1 | CONFIG_CH0;
|
|||
|
break;
|
|||
|
case AD7793_CHANNEL_AVDD:
|
|||
|
config_reg |= CONFIG_CH2 | CONFIG_CH1 | CONFIG_CH0;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
dev->cur_channel = channel;
|
|||
|
|
|||
|
DEBUG_PRINTF("[config-w]set channel: 0x%04X\n", config_reg);
|
|||
|
return ad7793_write_reg(dev, REG_CONFIG, config_reg, 2);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Set the update rate of the device.
|
|||
|
* @param dev Pointer to ad7793_dev_t instance.
|
|||
|
* @param rate Update rate enum value representing the desired update rate.
|
|||
|
* @return Operation status. True if successful, false otherwise.
|
|||
|
* @detail This function reads the current mode register, clears the rate bits, and then sets the new rate according to the input.
|
|||
|
* It updates the current update rate in the device structure and writes the new mode to the mode register.
|
|||
|
*/
|
|||
|
bool ad7793_set_rate(ad7793_dev_t *dev, ad7793_rate_t rate)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
uint32_t mode_reg = 0;
|
|||
|
ad7793_read_reg(dev, REG_MODE, &mode_reg, 2);
|
|||
|
/* Clear rate bits */
|
|||
|
mode_reg &= ~(MODE_FS3 | MODE_FS2 | MODE_FS1 | MODE_FS0);
|
|||
|
|
|||
|
/* Set new rate */
|
|||
|
mode_reg |= (uint16_t)rate;
|
|||
|
|
|||
|
dev->cur_rate = rate;
|
|||
|
DEBUG_PRINTF("[mode]set rate: 0x%04X\n", mode_reg);
|
|||
|
return ad7793_write_reg(dev, REG_MODE, mode_reg, 2);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Set the reference source of the device.
|
|||
|
* @param dev Pointer to ad7793_dev_t instance.
|
|||
|
* @param use_internal true=internal reference, false=external reference.
|
|||
|
* @param external_ref External reference voltage (if use_internal is false).
|
|||
|
* @return Operation status. True if successful, false otherwise.
|
|||
|
* @detail This function reads the current configuration register and sets the reference select bit according to the input.
|
|||
|
* It updates the reference source information in the device structure and writes the new configuration to the
|
|||
|
* configuration register.
|
|||
|
*/
|
|||
|
bool ad7793_set_reference(ad7793_dev_t *dev, bool use_internal, float external_ref)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
uint32_t config_reg = 0;
|
|||
|
ad7793_read_reg(dev, REG_CONFIG, &config_reg, 2);
|
|||
|
|
|||
|
/* Set reference select bit */
|
|||
|
if (use_internal)
|
|||
|
{
|
|||
|
dev->use_internal_ref = true;
|
|||
|
config_reg |= CONFIG_REFSEL;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
dev->external_ref = external_ref;
|
|||
|
config_reg &= ~CONFIG_REFSEL;
|
|||
|
}
|
|||
|
DEBUG_PRINTF("[config-w]set reference: 0x%04X\n", config_reg);
|
|||
|
return ad7793_write_reg(dev, REG_CONFIG, config_reg, 2);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Wait for the conversion to be ready.
|
|||
|
* @param dev Pointer to ad7793_dev_t instance.
|
|||
|
* @param timeout_ms Timeout in milliseconds.
|
|||
|
* @return true=ready, false=timeout.
|
|||
|
* @detail This function continuously checks the GPIO pin status until the device is ready or the timeout occurs.
|
|||
|
* It waits for 1 millisecond between each check.
|
|||
|
*/
|
|||
|
bool ad7793_wait_ready(ad7793_dev_t *dev, uint16_t timeout_ms)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
uint16_t timeout = 0;
|
|||
|
uint32_t status = 0;
|
|||
|
|
|||
|
while (timeout < timeout_ms)
|
|||
|
{
|
|||
|
ad7793_read_reg(dev, REG_STATUS, &status, 1);
|
|||
|
|
|||
|
if (status & 0x40) // check if the device is in error state
|
|||
|
{
|
|||
|
DEBUG_PRINTF("Device in error state, status: 0x%02X\n", status);
|
|||
|
return false; // device error
|
|||
|
}
|
|||
|
// check RDY bit(bit7)is zero or not
|
|||
|
if ((status & 0x80) == 0)
|
|||
|
{
|
|||
|
return true; // data is rdy
|
|||
|
}
|
|||
|
|
|||
|
dev->hw_if->delay_ms(1);
|
|||
|
timeout++;
|
|||
|
}
|
|||
|
|
|||
|
DEBUG_PRINTF("Timeout waiting for data ready\n");
|
|||
|
return false; // timeout
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Read the conversion data from the device.
|
|||
|
* @param dev Pointer to ad7793_dev_t instance.
|
|||
|
* @param value Data buffer (3 bytes for AD7793) to store the read data.
|
|||
|
* @return Operation status. True if successful, false otherwise.
|
|||
|
* @detail This function reads the conversion data from the data register.
|
|||
|
* If the device is AD7793, it reads 3 bytes; if it is AD7792, it reads 2 bytes.
|
|||
|
*/
|
|||
|
bool ad7793_read_data(ad7793_dev_t *dev, uint32_t *value)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
uint32_t data = 0;
|
|||
|
bool status = false;
|
|||
|
if (dev->is_ad7793)
|
|||
|
{
|
|||
|
/* AD7793: 24-bit data, read 3 bytes */
|
|||
|
status = ad7793_read_reg(dev, REG_DATA, &data, 3);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* AD7792: 16-bit data, read 2 bytes */
|
|||
|
status = ad7793_read_reg(dev, REG_DATA, &data, 2);
|
|||
|
}
|
|||
|
if (status)
|
|||
|
{
|
|||
|
*value = data;
|
|||
|
}
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Convert the raw digital data read from AD7793 to the corresponding voltage value.
|
|||
|
* @param dev Pointer to the ad7793_dev_t instance.
|
|||
|
* @param raw_data The raw digital data (24-bit) read from AD7793.
|
|||
|
* @return The converted voltage value (unit: volts).
|
|||
|
* @detail This function converts the raw digital data to the voltage value according to the formula provided in the AD7793 manual.
|
|||
|
* The formula is: V = (D * Vref) / (2^N * G)
|
|||
|
* Where:
|
|||
|
* V is the converted voltage value.
|
|||
|
* D is the raw digital data.
|
|||
|
* Vref is the reference voltage.
|
|||
|
* N is the number of ADC bits (24 bits for AD7793).
|
|||
|
* G is the gain setting.
|
|||
|
*/
|
|||
|
float ad7793_convert_to_voltage(ad7793_dev_t *dev, uint32_t adc_code)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
#if (USING_AUTO_READ_CONFIG_BITS == 1)
|
|||
|
// Determine the reference voltage: 1.17V for internal, or user-defined external VREF
|
|||
|
// Read configuration and mode registers from the AD7793 device
|
|||
|
uint32_t config_reg = 0;
|
|||
|
ad7793_read_reg(dev, REG_CONFIG, &config_reg, 2);
|
|||
|
DEBUG_PRINTF("read config: 0x%04X\n", config_reg);
|
|||
|
|
|||
|
float vref = (config_reg & CONFIG_REFSEL) ? 1.17f : dev->external_ref;
|
|||
|
// Extract gain bits (G2, G1, G0) from config register and calculate gain = 2^gain_bits
|
|||
|
uint8_t gain_bits = 0;
|
|||
|
gain_bits |= (config_reg & CONFIG_G2) ? 0x04 : 0x00;
|
|||
|
gain_bits |= (config_reg & CONFIG_G1) ? 0x02 : 0x00;
|
|||
|
gain_bits |= (config_reg & CONFIG_G0) ? 0x01 : 0x00;
|
|||
|
uint8_t gain = 1 << gain_bits; // Gain = 2^gain_bits (e.g., 1, 2, ..., 128)
|
|||
|
|
|||
|
// Determine bipolar mode: if CONFIG_U_B bit is 0, then bipolar is enabled (Offset Binary)
|
|||
|
bool bipolar = ((config_reg & CONFIG_U_B) == 0);
|
|||
|
|
|||
|
// Determine if input buffer is enabled (bit CONFIG_BUF)
|
|||
|
bool buffer_enabled = (config_reg & CONFIG_BUF) ? true : false;
|
|||
|
#else
|
|||
|
float vref = dev->use_internal_ref ? 1.17f : dev->external_ref;
|
|||
|
uint8_t gain = dev->cur_gain;
|
|||
|
bool bipolar = !dev->unipolar_mode;
|
|||
|
bool buffer_enabled = dev->buffered;
|
|||
|
#endif
|
|||
|
// Set resolution: 24-bit for AD7793, 16-bit for AD7792
|
|||
|
uint8_t resolution_bits = dev->is_ad7793 ? 24 : 16;
|
|||
|
// Validate parameters:
|
|||
|
if (vref <= 0.0f)
|
|||
|
return 0.0f; // Invalid reference voltage
|
|||
|
if (!(gain == 1 || gain == 2 || gain == 4 || gain == 8 || gain == 16 || gain == 32 || gain == 64 || gain == 128))
|
|||
|
return 0.0f; // Invalid gain
|
|||
|
if (!(resolution_bits == 16 || resolution_bits == 24))
|
|||
|
return 0.0f; // Invalid resolution
|
|||
|
if (!buffer_enabled && gain > 2)
|
|||
|
return 0.0f; // According to datasheet, buffer must be enabled when gain > 2
|
|||
|
|
|||
|
// Check that the ADC code is within valid range
|
|||
|
uint32_t max_code = (resolution_bits == 24) ? 0xFFFFFFu : 0xFFFFu;
|
|||
|
if (adc_code > max_code)
|
|||
|
return 0.0f; // ADC code out of range
|
|||
|
|
|||
|
// Begin voltage calculation
|
|||
|
float voltage = 0.0f;
|
|||
|
|
|||
|
if (!bipolar)
|
|||
|
{
|
|||
|
// Unipolar mode (Straight Binary)
|
|||
|
// Code range: 0x000000 to 0xFFFFFF
|
|||
|
// Input range: 0V to +VREF / gain
|
|||
|
// Voltage = (ADC_Code / 2^N) * (VREF / Gain)
|
|||
|
uint32_t full_range = (uint32_t)1 << resolution_bits;
|
|||
|
voltage = (float)((double)adc_code * vref / (gain * (double)full_range));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// Bipolar mode (Offset Binary)
|
|||
|
// Code range: 0x000000 to 0xFFFFFF
|
|||
|
// Midpoint: 0x800000 (represents 0V)
|
|||
|
// Voltage = ((ADC_Code / 2^(N-1)) - 1) * (VREF / Gain)
|
|||
|
// This maps code range [0x000000, 0xFFFFFF] to voltage range [-VREF/Gain, +VREF/Gain]
|
|||
|
double half_range = (double)((uint32_t)1 << (resolution_bits - 1));
|
|||
|
double code_norm = (double)adc_code / half_range;
|
|||
|
voltage = (float)((code_norm - 1.0) * (vref / gain));
|
|||
|
}
|
|||
|
|
|||
|
return voltage;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Convert the input voltage to the corresponding ADC code for AD7793.
|
|||
|
* @param dev Pointer to the AD7793 device structure.
|
|||
|
* @param voltage Input voltage value (V).
|
|||
|
* @return Corresponding ADC code value.
|
|||
|
*/
|
|||
|
uint32_t ad7793_convert_voltage_to_code(ad7793_dev_t *dev, float voltage)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
#if (USING_AUTO_READ_CONFIG_BITS == 1)
|
|||
|
// Read configuration and mode registers from the AD7793 device
|
|||
|
uint32_t config_reg = 0;
|
|||
|
ad7793_read_reg(dev, REG_CONFIG, &config_reg, 2);
|
|||
|
DEBUG_PRINTF("read config: 0x%04X\n", config_reg);
|
|||
|
|
|||
|
float vref = (config_reg & CONFIG_REFSEL) ? 1.17f : dev->external_ref;
|
|||
|
// Extract gain bits (G2, G1, G0) from config register and calculate gain = 2^gain_bits
|
|||
|
uint8_t gain_bits = 0;
|
|||
|
gain_bits |= (config_reg & CONFIG_G2) ? 0x04 : 0x00;
|
|||
|
gain_bits |= (config_reg & CONFIG_G1) ? 0x02 : 0x00;
|
|||
|
gain_bits |= (config_reg & CONFIG_G0) ? 0x01 : 0x00;
|
|||
|
uint8_t gain = 1 << gain_bits; // Gain = 2^gain_bits (e.g., 1, 2, ..., 128)
|
|||
|
|
|||
|
// Determine bipolar mode: if CONFIG_U_B bit is 0, then bipolar is enabled (Offset Binary)
|
|||
|
bool bipolar = ((config_reg & CONFIG_U_B) == 0);
|
|||
|
|
|||
|
// Determine if input buffer is enabled (CONFIG_BUF bit)
|
|||
|
bool buffer_enabled = (config_reg & CONFIG_BUF) ? true : false;
|
|||
|
#else
|
|||
|
float vref = dev->use_internal_ref ? 1.17f : dev->external_ref;
|
|||
|
uint8_t gain = dev->cur_gain;
|
|||
|
bool bipolar = !dev->unipolar_mode;
|
|||
|
bool buffer_enabled = dev->buffered;
|
|||
|
#endif
|
|||
|
|
|||
|
// Set resolution: 24-bit for AD7793, 16-bit for AD7792
|
|||
|
uint8_t resolution_bits = dev->is_ad7793 ? 24 : 16;
|
|||
|
// Parameter validation:
|
|||
|
if (vref <= 0.0f)
|
|||
|
return 0; // Invalid reference voltage
|
|||
|
if (!(gain == 1 || gain == 2 || gain == 4 || gain == 8 || gain == 16 || gain == 32 || gain == 64 || gain == 128))
|
|||
|
return 0; // Invalid gain
|
|||
|
if (!(resolution_bits == 16 || resolution_bits == 24))
|
|||
|
return 0; // Invalid resolution
|
|||
|
if (!buffer_enabled && gain > 2)
|
|||
|
return 0; // According to datasheet, buffer must be enabled when gain > 2
|
|||
|
|
|||
|
// Calculate maximum ADC code value
|
|||
|
uint32_t max_code = (resolution_bits == 24) ? 0xFFFFFFu : 0xFFFFu;
|
|||
|
uint32_t adc_code = 0;
|
|||
|
|
|||
|
// Voltage to ADC code conversion
|
|||
|
if (!bipolar)
|
|||
|
{
|
|||
|
// Unipolar mode (straight binary)
|
|||
|
// Voltage range: 0V to +VREF / gain
|
|||
|
// ADC code = (voltage * 2^N) / (VREF / gain)
|
|||
|
double full_scale = (double)vref / gain;
|
|||
|
double code_double = (double)voltage * ((double)max_code + 1) / full_scale;
|
|||
|
|
|||
|
// Clamp to valid range
|
|||
|
if (code_double < 0.0)
|
|||
|
adc_code = 0;
|
|||
|
else if (code_double >= (double)max_code)
|
|||
|
adc_code = max_code;
|
|||
|
else
|
|||
|
adc_code = (uint32_t)(code_double + 0.5); // Round to nearest
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// Bipolar mode (offset binary)
|
|||
|
// Voltage range: -VREF/gain to +VREF/gain
|
|||
|
// ADC code = ((voltage / (VREF / gain)) + 1) * 2^(N-1)
|
|||
|
double half_scale = (double)vref / gain;
|
|||
|
double normalized_voltage = (double)voltage / half_scale;
|
|||
|
double code_double = (normalized_voltage + 1.0) * ((double)max_code + 1) / 2.0;
|
|||
|
|
|||
|
// Clamp to valid range
|
|||
|
if (code_double < 0.0)
|
|||
|
adc_code = 0;
|
|||
|
else if (code_double >= (double)max_code)
|
|||
|
adc_code = max_code;
|
|||
|
else
|
|||
|
adc_code = (uint32_t)(code_double + 0.5); // Round to nearest
|
|||
|
}
|
|||
|
|
|||
|
return adc_code;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Perform internal zero-scale calibration.
|
|||
|
* @param dev Pointer to ad7793_dev_t instance.
|
|||
|
* @return Operation status. True if successful, false otherwise.
|
|||
|
* @detail This function sets the device to internal zero-scale calibration mode, waits for 10 milliseconds for the calibration to complete,
|
|||
|
* and then sets the device back to continuous conversion mode.
|
|||
|
*/
|
|||
|
bool ad7793_calibrate_internal_zero(ad7793_dev_t *dev)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
ad7793_set_mode(dev, AD7793_MODE_INTERNAL_ZERO);
|
|||
|
if (!ad7793_wait_ready(dev, 500)) /* Wait for calibration to complete */
|
|||
|
return false;
|
|||
|
return ad7793_set_mode(dev, AD7793_MODE_CONTINUOUS);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Perform internal full-scale calibration.
|
|||
|
* @param dev Pointer to ad7793_dev_t instance.
|
|||
|
* @return Operation status. True if successful, false otherwise.
|
|||
|
* @detail This function sets the device to internal full-scale calibration mode, waits for 10 milliseconds for the calibration to complete,
|
|||
|
* and then sets the device back to continuous conversion mode.
|
|||
|
*/
|
|||
|
bool ad7793_calibrate_internal_full(ad7793_dev_t *dev)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
ad7793_set_mode(dev, AD7793_MODE_INTERNAL_FULL);
|
|||
|
dev->hw_if->delay_ms(10); /* Wait for calibration to complete */
|
|||
|
return ad7793_set_mode(dev, AD7793_MODE_CONTINUOUS);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Perform system zero-scale calibration.
|
|||
|
* @param dev Pointer to ad7793_dev_t instance.
|
|||
|
* @return Operation status. True if successful, false otherwise.
|
|||
|
* @detail This function sets the device to system zero-scale calibration mode, waits for 10 milliseconds for the calibration to complete,
|
|||
|
* and then sets the device back to continuous conversion mode.
|
|||
|
*/
|
|||
|
bool ad7793_calibrate_system_zero(ad7793_dev_t *dev)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
ad7793_set_mode(dev, AD7793_MODE_SYSTEM_ZERO);
|
|||
|
dev->hw_if->delay_ms(10); /* Wait for calibration to complete */
|
|||
|
return ad7793_set_mode(dev, AD7793_MODE_CONTINUOUS);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Perform system full-scale calibration.
|
|||
|
* @param dev Pointer to ad7793_dev_t instance.
|
|||
|
* @return Operation status. True if successful, false otherwise.
|
|||
|
* @detail This function sets the device to system full-scale calibration mode, waits for 10 milliseconds for the calibration to complete,
|
|||
|
* and then sets the device back to continuous conversion mode.
|
|||
|
*/
|
|||
|
bool ad7793_calibrate_system_full(ad7793_dev_t *dev)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
ad7793_set_mode(dev, AD7793_MODE_SYSTEM_FULL);
|
|||
|
dev->hw_if->delay_ms(10); /* Wait for calibration to complete */
|
|||
|
return ad7793_set_mode(dev, AD7793_MODE_CONTINUOUS);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Configure the excitation current source.
|
|||
|
* @param dev Pointer to ad7793_dev_t instance.
|
|||
|
* @param current Current value (10/210/1000uA).
|
|||
|
* @param dir Current direction (0=IOUT1/IOUT2, 1=swap).
|
|||
|
* @return Operation status. True if successful, false otherwise.
|
|||
|
* @detail This function configures the excitation current source by setting the current value and direction in the IO register.
|
|||
|
* It then enables the current source and writes the configuration to the IO register.
|
|||
|
*/
|
|||
|
bool ad7793_config_iexc(ad7793_dev_t *dev, uint16_t current, uint8_t dir)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
uint32_t io_reg = 0;
|
|||
|
|
|||
|
/* Set current value */
|
|||
|
if (current == 10)
|
|||
|
{
|
|||
|
io_reg |= 0x01;
|
|||
|
}
|
|||
|
else if (current == 210)
|
|||
|
{
|
|||
|
io_reg |= 0x02;
|
|||
|
}
|
|||
|
else if (current == 1000)
|
|||
|
{
|
|||
|
io_reg |= 0x03;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/* Set current direction */
|
|||
|
if (dir)
|
|||
|
{
|
|||
|
io_reg |= (0x03 << 2);
|
|||
|
}
|
|||
|
|
|||
|
/* Enable current source */
|
|||
|
io_reg |= (0x03 << 0);
|
|||
|
|
|||
|
return ad7793_write_reg(dev, REG_IO, io_reg, 1);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Configure the AD7793 for unipolar or bipolar operation mode
|
|||
|
* @param dev Pointer to the AD7793 device structure
|
|||
|
* @param unipolar true = enable unipolar mode, false = enable bipolar mode
|
|||
|
* @return true if operation succeeded, false otherwise
|
|||
|
* @details
|
|||
|
* This function sets the U/B (Unipolar/Bipolar) bit in the configuration register
|
|||
|
* to determine the conversion mode:
|
|||
|
* - Unipolar mode (U/B=1): Converts 0V to VREF into 0x000000 to 0xFFFFFF
|
|||
|
* - Bipolar mode (U/B=0): Converts -VREF/2 to +VREF/2 into 0x000000 to 0xFFFFFF
|
|||
|
*
|
|||
|
* Note: Changing this bit affects the interpretation of the ADC output code.
|
|||
|
*/
|
|||
|
bool ad7793_set_unipolar(ad7793_dev_t *dev, bool unipolar)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
uint32_t config_reg = 0;
|
|||
|
|
|||
|
// Read current configuration register
|
|||
|
ad7793_read_reg(dev, REG_CONFIG, &config_reg, 2);
|
|||
|
|
|||
|
// Update the U/B bit (bit 8 in the config register)
|
|||
|
if (unipolar)
|
|||
|
{
|
|||
|
config_reg |= CONFIG_U_B; // Set U/B bit for unipolar mode
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
config_reg &= ~CONFIG_U_B; // Clear U/B bit for bipolar mode
|
|||
|
}
|
|||
|
|
|||
|
// Save the current mode setting
|
|||
|
dev->unipolar_mode = unipolar;
|
|||
|
DEBUG_PRINTF("[config-w]Unipolar mode set to 0x%04x\n", config_reg);
|
|||
|
// Write updated configuration back to the device
|
|||
|
return ad7793_write_reg(dev, REG_CONFIG, config_reg, 2);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Configure the input buffer mode of AD7793
|
|||
|
* @param dev Pointer to the ad7793_dev_t instance
|
|||
|
* @param enable true=Enable input buffer, false=Disable input buffer
|
|||
|
* @return Operation status. true=Success, false=Failure
|
|||
|
* @detail This function configures the input buffer mode of AD7793.
|
|||
|
* Enabling the buffer reduces input impedance requirements but increases power consumption.
|
|||
|
* Disabling the buffer is suitable for high-impedance sources but requires attention to input signal range limitations.
|
|||
|
*/
|
|||
|
bool ad7793_set_buffered(ad7793_dev_t *dev, bool enable)
|
|||
|
{
|
|||
|
AD7793_CHECK_INITIALIZED(dev);
|
|||
|
uint32_t config_reg = 0;
|
|||
|
|
|||
|
// Read current configuration register value
|
|||
|
ad7793_read_reg(dev, REG_CONFIG, &config_reg, 2);
|
|||
|
|
|||
|
// Update BUF bit (bit 7 of the configuration register)
|
|||
|
if (enable)
|
|||
|
{
|
|||
|
config_reg |= CONFIG_BUF; // Enable input buffer
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
config_reg &= ~CONFIG_BUF; // Disable input buffer
|
|||
|
}
|
|||
|
|
|||
|
// Save current buffer mode state
|
|||
|
dev->buffered = enable;
|
|||
|
DEBUG_PRINTF("[config-w]set buffered %04x\n", config_reg);
|
|||
|
|
|||
|
// Write updated configuration register
|
|||
|
return ad7793_write_reg(dev, REG_CONFIG, config_reg, 2);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Initialize the device.
|
|||
|
* @param dev Pointer to ad7793_dev_t instance.
|
|||
|
* @param hw_if Hardware interface structure containing function pointers.
|
|||
|
* @param cs_pin Chip select pin.
|
|||
|
* @param rdy_pin Ready pin
|
|||
|
* @return Operation status. True if successful, false otherwise.
|
|||
|
* @detail This function initializes the AD7793 device by saving the hardware interface and chip select information.
|
|||
|
* It resets the device, waits for the reset to complete, and then reads the ID register to confirm the device type.
|
|||
|
* Finally, it initializes the device with default configuration settings.
|
|||
|
*/
|
|||
|
bool ad7793_init(ad7793_dev_t *dev, ad7793_hw_if_t *hw_if, uint8_t cs_pin)
|
|||
|
{
|
|||
|
uint32_t mode_reg = 0;
|
|||
|
uint32_t config_reg = 0;
|
|||
|
if (hw_if == NULL)
|
|||
|
{
|
|||
|
DEBUG_PRINTF("hw_if is null\n");
|
|||
|
return false;
|
|||
|
}
|
|||
|
/* Save hardware interface and chip select info */
|
|||
|
dev->hw_if = hw_if;
|
|||
|
dev->cs_pin = cs_pin;
|
|||
|
dev->is_ad7793 = true; /* Default is AD7793, can be confirmed by ID register */
|
|||
|
dev->is_initialized = true;
|
|||
|
|
|||
|
/* Reset device */
|
|||
|
ad7793_reset(dev);
|
|||
|
|
|||
|
/* Wait for reset to complete */
|
|||
|
dev->hw_if->delay_ms(1);
|
|||
|
|
|||
|
/* Read ID register to confirm device type */
|
|||
|
uint32_t id = 0;
|
|||
|
ad7793_read_reg(dev, REG_ID, &id, 1);
|
|||
|
DEBUG_PRINTF("ID: 0x%02X\n", id);
|
|||
|
dev->is_ad7793 = ((id & 0x0F) == 0x0B); /* AD7793 ID read out is 0x4B */
|
|||
|
|
|||
|
/* Initialize default configuration */
|
|||
|
ad7793_set_mode(dev, AD7793_MODE_CONTINUOUS);
|
|||
|
ad7793_wait_ready(dev, 1000);
|
|||
|
ad7793_read_reg(dev, REG_MODE, &mode_reg, 2);
|
|||
|
DEBUG_PRINTF("[mode-r]: 0x%04X\n", mode_reg);
|
|||
|
|
|||
|
|
|||
|
ad7793_set_gain(dev, AD7793_GAIN_1);
|
|||
|
ad7793_wait_ready(dev, 1000);
|
|||
|
ad7793_read_reg(dev, REG_CONFIG, &config_reg, 2);
|
|||
|
DEBUG_PRINTF("[config-r]after gain config: 0x%04X\n", config_reg);
|
|||
|
|
|||
|
ad7793_set_channel(dev, AD7793_CHANNEL_1);
|
|||
|
ad7793_wait_ready(dev, 1000);
|
|||
|
ad7793_read_reg(dev, REG_CONFIG, &config_reg, 2);
|
|||
|
DEBUG_PRINTF("[config-r]after channel config: 0x%04X\n", config_reg);
|
|||
|
|
|||
|
ad7793_set_rate(dev, AD7793_RATE_8_33HZ);
|
|||
|
ad7793_wait_ready(dev, 1000);
|
|||
|
ad7793_read_reg(dev, REG_MODE, &mode_reg, 2);
|
|||
|
DEBUG_PRINTF("[mode-r]after rate mode: 0x%04X\n", mode_reg);
|
|||
|
|
|||
|
ad7793_set_reference(dev, true, 2.5); /* Use internal reference */
|
|||
|
ad7793_wait_ready(dev, 1000);
|
|||
|
ad7793_read_reg(dev, REG_CONFIG, &config_reg, 2);
|
|||
|
DEBUG_PRINTF("[config-r]after reference config: 0x%04X\n", config_reg);
|
|||
|
|
|||
|
ad7793_set_unipolar(dev, true); /* Default to bipolar mode */
|
|||
|
ad7793_wait_ready(dev, 1000);
|
|||
|
ad7793_read_reg(dev, REG_CONFIG, &config_reg, 2);
|
|||
|
DEBUG_PRINTF("[config-r]after polar config: 0x%04X\n", config_reg);
|
|||
|
|
|||
|
ad7793_set_buffered(dev, false); /* Enable input buffer */
|
|||
|
ad7793_wait_ready(dev, 1000);
|
|||
|
ad7793_read_reg(dev, REG_CONFIG, &config_reg, 2);
|
|||
|
DEBUG_PRINTF("final read config: 0x%04X\n", config_reg);
|
|||
|
ad7793_calibrate_system_zero(dev);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
|