模数转换【1】AD7699
1.开发背景
一款可以模数转换芯片,兼容通用 SPI 接口,支持最多支持 8 个通道和 500KSPS 的采样率。支持单个通道采集和扫描采集模式。
同系列的芯片还有 AD7682 和 AD7689 等。
2.开发需求
配置内部参考电压4.096V,实现单个通道采集和扫描采集模式。
3.开发环境
Window10 + MDK + STM32F407VET6
4.详细设计
4.1 关键寄存器
配置信息可以随时更改,每次 SPI 传输 2 个字节,需要注意的是:配置寄存器需要左移2位, SPI 模式 CPHA = CPOL = 0。
4.2 控制流程
4.2.1 单个读取
例如读取通道 0 = 单极性 + IN0 + 全带宽 + 内部基准电压源 + 禁用序列器
4.2.2 扫描读取
扫描读取0-3 = 单极性 + IN3 + 全带宽 + 内部基准电压源 + 扫描IN0至IN[7:0]
4.3 软件实现
4.3.1 AD7699 头文件
#ifndef BSP_AD7699_H
#define BSP_AD7699_H#include <stdio.h>#define AD7699_LOG printf/* 采集极性选择 Default eAd7699_Incc_Single */
typedef enum
{eAd7699_Incc_DoubleDiff = 0, // 0 0 xeAd7699_Incc_Double = 0x02, // 0 1 0eAd7699_Incc_Temperaure = 0x03, // 0 1 1eAd7699_Incc_SingleDiff = 0x04, // 1 0 xeAd7699_Incc_SingleCom = 0x06, // 1 1 0eAd7699_Incc_Single = 0x07, // 1 1 1 }EnumAd7699_Incc_t;/* 通道选择 选择从 Ch0 扫描到 CHx */
typedef enum
{eAd7699_Channnel_0 = 0, // 通道eAd7699_Channnel_1,eAd7699_Channnel_2,eAd7699_Channnel_3,eAd7699_Channnel_4,eAd7699_Channnel_5,eAd7699_Channnel_6,eAd7699_Channnel_7,}EnumAd7699_Channel_t;/* 带宽选择 Default eAd7699_BandWidth_All */
typedef enum
{eAd7699_BandWidth_Quarter = 0, // 1/4 带宽eAd7699_BandWidth_All, // 全带宽 }EnumAd7699_BandWidth_t;/* 参考电压 Default eAd7699_Ref_Internal */
typedef enum
{eAd7699_Ref_Disable = 0x0,eAd7699_Ref_Internal = 0x01, // 内部参考电压 4.096V + 温度传感器eAd7699_Ref_ExterAndTemp = 0x02, // 外部电压 + 温度传感器eAd7699_Ref_ExterAndTempAndBuf = 0x03, // 外部电压 + 温度传感器 + 缓存eAd7699_Ref_Exter = 0x06, // 外部电压eAd7699_Ref_ExterAndBuf = 0x07, // 外部电压 + 缓存}EnumAd7699_Ref_t;/* 通道序列器 */
typedef enum
{eAd7699_Seq_Disable = 0, // 禁用 DefaulteAd7699_Seq_Update, // 序列扫描期间更新序列eAd7699_Seq_ChannelAndTemp, // 扫描通道和温度eAd7699_Seq_Channel, // 扫描通道}EnumAd7699_Seq_t;/* __weak 弱定义 必须先配置好 否者无效!!! */
void Ad7699_FuncDelayUs(int us);
void Ad7699_FuncChipSelect(void);
void Ad7699_FuncChipUnselect(void);
int Ad7699_FuncSpiTxRx(char *pTxData, char *pRxData, int size);/* 可调用接口 温度传感器读取的不准 */
int bAd7699_Init(void);
int bAd7699_SingleSample(EnumAd7699_Channel_t eChannel, unsigned short *pData, int num);
int bAd7699_ScanSample(EnumAd7699_Channel_t eLastCh, unsigned short *pData, int num);
int bAd7699_ScanSampleWithTemp(EnumAd7699_Channel_t eLastCh, unsigned short *pData, int num);
unsigned short bAd7699_GetChannelValue(EnumAd7699_Channel_t eChannel);/* 扩展接口 */
void bAd7699_SetIncc(EnumAd7699_Incc_t eIncc);
void bAd7699_SetBaudWidth(EnumAd7699_BandWidth_t eBaudWidth);
void bAd7699_SetRefVoltage(EnumAd7699_Ref_t eRef);
float bAd7699_GetTempFromValue(unsigned short data); // 温度测量不是很准 不建议使用/* 测试接口 */
int bAd7699_Test(void);#endif
需要注意的是 需要给定 __weak 的接口,否者读不到数据。
4.3.2 AD7699 源文件
#include "bspAd7699.h"#include <stdio.h>#define AD7699_FULL_VALUE (65536)
#define AD7699_INTERNAL_REF_V (4.096f)
#define AD7699_TEMP_25_MAP_MV (283.0f)/* 控制结构体 */
typedef struct
{/* 关键配置 */EnumAd7699_Incc_t eIncc;EnumAd7699_BandWidth_t eBandWidth;EnumAd7699_Ref_t eRef;}Ctrl_t;/* 初始化 */
static Ctrl_t s_ctrl = {.eIncc = eAd7699_Incc_Single,.eBandWidth = eAd7699_BandWidth_All,.eRef = eAd7699_Ref_Internal,
};
static Ctrl_t *p = &s_ctrl;__weak void Ad7699_FuncDelayUs(int us) {}
__weak void Ad7699_FuncChipSelect(void) {}
__weak void Ad7699_FuncChipUnselect(void) {}
__weak int Ad7699_FuncSpiTxRx(char *pTxData, char *pRxData, int size) { return 0; }/* 开始转换 */
static int Ad7699_StartConversion(void)
{Ad7699_FuncChipSelect();Ad7699_FuncDelayUs(120);Ad7699_FuncChipUnselect();Ad7699_FuncDelayUs(1);return 0;
}/* 读写数据 */
static unsigned short Ad7699_ReadWriteData(unsigned short data)
{char txData[2] = {0};char rxData[2] = {0};/* 填充数据 */txData[0] = (data >> 8) & 0xFF;txData[1] = (data >> 0) & 0xFF;/* 片选 读写数据 */Ad7699_FuncChipSelect();Ad7699_FuncSpiTxRx(txData, rxData, 2);Ad7699_FuncChipUnselect();/* 返回读写值 */return ((rxData[0] << 8) | rxData[1]);
}/* 写配置寄存器 返回回读数据 */
static unsigned short Ad7699_WriteConfig(unsigned short config)
{/* 地址左移 2 位 */config = config << 2;return Ad7699_ReadWriteData(config);
}/* 初始化 */
int bAd7699_Init(void)
{for (int i = 0; i < 1; i++){Ad7699_StartConversion();
// bAd7699_GetChannelValue(eAd7699_Channnel_0);}return 0;
}/* 采集通道数据 */
int bAd7699_SingleSample(EnumAd7699_Channel_t eChannel, unsigned short *pData, int num)
{if (pData == NULL){return -1;}/* 因为获取的数据延迟 2 个节奏,第三次才是有效值, 前两次无效 */for (int i = 0; i < num + 2; i++){unsigned short config = 0x3001;config |= (p->eIncc << 10);config |= (eChannel << 7);config |= (p->eBandWidth << 6);config |= (p->eRef << 3);config |= (eAd7699_Seq_Disable << 1);/* 前 2 次数据丢弃 */if (i < 2){Ad7699_WriteConfig(config);}else{pData[i - 2] = Ad7699_WriteConfig(config);}}return 0;
}/* 扫描采样 采集的顺序 从 CH0 到 eLastCh 顺序排列 num 是采集的数据个数 */
int bAd7699_ScanSample(EnumAd7699_Channel_t eLastCh, unsigned short *pData, int num)
{if (pData == NULL){return -1;}unsigned short config = 0x3001;config |= (p->eIncc << 10);config |= (eLastCh << 7);config |= (p->eBandWidth << 6);config |= (p->eRef << 3);config |= (eAd7699_Seq_Channel << 1);Ad7699_WriteConfig(config);/* 数据交互 */unsigned short txData = 0;for (int i = 0; i < num; i++){pData[i] = Ad7699_ReadWriteData(txData);}return 0;
}/* 扫描采样 + 温度 */
int bAd7699_ScanSampleWithTemp(EnumAd7699_Channel_t eLastCh, unsigned short *pData, int num)
{if (pData == NULL){return -1;}unsigned short config = 0x3001;config |= (p->eIncc << 10);config |= (eLastCh << 7);config |= (p->eBandWidth << 6);config |= (p->eRef << 3);config |= (eAd7699_Seq_ChannelAndTemp << 1);Ad7699_WriteConfig(config);/* 数据交互 */unsigned short txData = 0;for (int i = 0; i < num; i++){pData[i] = Ad7699_ReadWriteData(txData);}return 0;
}/* 获取通道数据 */
unsigned short bAd7699_GetChannelValue(EnumAd7699_Channel_t eChannel)
{unsigned short readData;bAd7699_SingleSample(eChannel, &readData, 1);return readData;
}/* 设置采集极性 */
void bAd7699_SetIncc(EnumAd7699_Incc_t eIncc)
{p->eIncc = eIncc;
}/* 设置带宽 */
void bAd7699_SetBaudWidth(EnumAd7699_BandWidth_t eBaudWidth)
{p->eBandWidth = eBaudWidth;
}/* 设置参考电压 */
void bAd7699_SetRefVoltage(EnumAd7699_Ref_t eRef)
{p->eRef = eRef;
}/* 数值转换温度接口 */
float bAd7699_GetTempFromValue(unsigned short data)
{/* 内部参考电压 */if (p->eRef == eAd7699_Ref_Internal){float voltage = (float)data / AD7699_FULL_VALUE * AD7699_INTERNAL_REF_V;float temp = (voltage * 1000 - AD7699_TEMP_25_MAP_MV) / 1.0f + 25.0f;
// AD7699_LOG("data = %d, voltage = %f, temp = %f\r\n", data, voltage, temp);return temp;}else{}return 0;
}/* 测试 */
int bAd7699_Test(void)
{AD7699_LOG("%s\r\n", __func__);/* 循环遍历 */for (int i = 0; i < 8; i++){unsigned short data = bAd7699_GetChannelValue((EnumAd7699_Channel_t)i);AD7699_LOG("%s [%d] data = %d\r\n", __func__, i, data);if (p->eRef == eAd7699_Ref_Internal){float voltage = (float)data / AD7699_FULL_VALUE * AD7699_INTERNAL_REF_V;AD7699_LOG("%s [%d] voltage = %.3f\r\n", __func__, i, voltage);}#define SAMPLE_DATA_NUM (100)unsigned short dataList[SAMPLE_DATA_NUM] = {0};bAd7699_SingleSample((EnumAd7699_Channel_t)i, dataList, SAMPLE_DATA_NUM);AD7699_LOG("%s DataList[%d] = ", __func__, i);for (int j = 0; j < SAMPLE_DATA_NUM; j++){AD7699_LOG("%d ", dataList[j]);}AD7699_LOG("\r\n");}/* 扫描采集 */#define SCAN_SAMPLE_DATA_NUM (100)unsigned short sampleDataList[SCAN_SAMPLE_DATA_NUM] = {0};bAd7699_ScanSample(eAd7699_Channnel_3, sampleDataList, SCAN_SAMPLE_DATA_NUM);AD7699_LOG("%s bAd7699_ScanSample = ", __func__);for (int j = 0; j < SCAN_SAMPLE_DATA_NUM; j++){AD7699_LOG("%d ", sampleDataList[j]);if ((j % 8) == 0){AD7699_LOG("\r\n");}}AD7699_LOG("\r\n"); bAd7699_ScanSampleWithTemp(eAd7699_Channnel_7, sampleDataList, SCAN_SAMPLE_DATA_NUM);AD7699_LOG("%s bAd7699_ScanSampleWithTemp = ", __func__);for (int j = 0; j < SCAN_SAMPLE_DATA_NUM; j++){AD7699_LOG("%d ", sampleDataList[j]);if ((j % 9) == 0){float temp = bAd7699_GetTempFromValue(sampleDataList[j]);AD7699_LOG("%.2f ", temp);AD7699_LOG("\r\n");}}AD7699_LOG("\r\n"); return 0;
}
4.4 测试结果
调用 bAd7699_Test 验证各个接口功能。除了内部温度传感器不准,其他的还符合预期