polarimeter_software/User/driver/ad779x/ad7793.c
2025-09-30 10:37:23 +08:00

885 lines
31 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* @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;
}