polarimeter_software/User/driver/ad779x/ad7793.c

885 lines
31 KiB
C
Raw Normal View History

2025-09-30 02:37:23 +00:00
/*
* @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 bitbit7is 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;
}