Zynq Linux 使用 SPI ADC (ADS8332)
时间:2023-09-12 12:07:02
目录
- 参考文章
- 一、ADS8332介绍
-
- 1. ADS8332通道选择模式
- 2. ADS8332转换开始(CONVST)控制
- 3. ADS8332转换结束(EOC)状态
- 4. ADS8332 TAG模式
- 5. ADS8332转换结果
- 6. ADS8332时序
- 7. ADS8332 SPI接口
- 二、Vivado工程说明
- 三、内核配置
- 四、设备树配置
- 五、测试程序
- 六、测试
参考文章
- ZYNQ Linux使用SPI驱动
- STM32之ADS8332
- 详解SPI中的极性CPOL和相位CPHA
一、ADS8332介绍
ADS8331是低功耗,16,500-k采样每秒(SPS)模数转换器(ADC),单极,4-1多路转换器(mux)输入。 该装置包括基于电容的16个基于固有取样和维护的逐次近似寄存器(SAR) ADC。
ADS8332基于相同的核心,包括单极8对1输入多路复用器。 当菊花链运行时,这两种设备提供高速、宽电压串行接口和多个转换器。
? SCLK up to 40 MHz (VA = VBD = 5 V)
? Supports SPI Modes 1 (0,1) and 2 (1,0):
? VREF (REF – REF–) = 4.096 V when VA = VBD = 5 V
? VREF (REF – REF–) = 2.5 V when VA = VBD = 2.7V
1. ADS8332通道选择模式
- 手动通道选择模式:
配置寄存器(CFR)启用,将CFR_D11位设置为0(见表5)。获取过程从选择输入通道开始。 这个选择是将所需的通道号写入命令寄存器(CMR)来完成的; 详见表4。 - 自动通道选择模式:
如果启用自动通道选择模式(默认)(CFR_D11 = 一、通道选择也可自动进行。若设备编程为自动通道选择模式,则所有通道的信号将按固定顺序获取。 在自动通道选择模式下,进入该模式后的第一次转换总是来自启用该模式前完成的最后一次转换。 然后依次扫描通道,直到包括最后一个通道(即ADS8331的通道3和ADS8332通道7),然后返回开始序列的通道。 例如,如果在启用Auto channel Select转换中使用的最后一个通道是通道2,所以ADS8332的序列将是:2、3、4、5、6、7、2,等等,如图39所示。 若手动通道选择模式中的最后一个通道碰巧是通道7,则序列为:7、7、7等。 图40显示了在Auto channel Select在模式下,何时激活序列中的下一个通道。 此时允许下一个通道在收购前解决。 在将CFR_D自动测序将在设置为0后停止循环。
2. ADS8332转换开始(CONVST)控制
采集的结束与转换的开始是相同的。 这个过程是通过程CONVST引脚至少40 ns来启动的。 满足最低要求后,CONVST可调高引脚。 CONVST独立于FS/CS,因此,对于需要同时采样/保持多个转换器的应用程序,可以使用通用程序CONVST。
如果ADS833x编程为自动触发模式(CFR_D9 = 0)转换也可以不使用CONVST启动。当转换器配置为此模式时,CFR_D8 = 0时,下一个转换在转换结束后三个转换时钟(CCLK)自动启动后。 这三个转换时钟(CCLK)收集时间。 在这种情况下,完成收购和转换周期的时间是21个cclk。 表1总结了不同的转换模式。
当需要从单个通道连续转换时,通常使用手动通道选择启用自动触发模式。 在这种模式下,循环输入mux通过将转换器设置为手动触发模式来停止转换通道。 在选择合适的输入通道时,转换器可以放回自动触发模式,继续从新通道转换。
3. ADS8332转换结束(EOC)状态
状态输出引脚可编程。 它可以用作EOC输出(CFR_D[7:6] = 11),低时间等于转换时间。 状态引脚编程为EOC,极性设置为 当有源低电时,引脚的工作方式如下:手动触发模式下,当CONVST电平低时,EOC输出立即降低。 EOC在整个转换过程中处于低位,在转换结束时返回高位。如果启用自动触发模式,则在前一个模式下EOC上升后,三个转换时钟(CCLK)的EOC保持高输出。
4. ADS8332 TAG模式
ADS833x包含一个TAG可用于指示转换结果的信道来源。 如果启用TAG模式,在从SDO读取转换数据LSB添加三个地址位,以指示哪个通道对应结果。 这些地址是0000,1001,2010,3011,4100,5101,610,711。 当启用TAG转换器至少需要19个模式sclk传输16位转换结果和3位转换结果TAG位。
5. ADS8332转换结果
如表6所示,转换结果为16位二进制格式数据。 通常需要16个SCLKs,但也有例外,如果需要超过16个SCLKs(见表7)(SDO)数据输出首先是左对齐MSB。 后面有三个位置TAG位置(如果启用)加上所有0填充。 在FS/CS在再次上升之前,SDO一直处于低位。
6. ADS8332时序
- spi 时序
- 手动触发转换时序
- 转换时序自动触发
7. ADS8332 SPI接口
串行接口设计用于适应最新的SCLK频率高达40 MHz (VA = VBD = 5 V)每个周期的高速处理器FS/CS从下降边开始。 输出寄存器可以在转换结束时使用的内部数据寄存器显示FS/CS下降沿上的SDO输出引脚。 第一个位置是最有效的位置(MSB)。 输出数据位SCLK下降沿上有效,时延为tD2(见Timing Requirements: VA = 2.7 V and Timing Characteristics: VA = 5 V),这样主机处理器就可以读取下降沿上的数据。 也有串行数据输入SCLK沿上读取下降。
完整的串行I/O周期从FS/CS从下降边开始,到16个SCLK下降沿结束(见注)。 当CPOL = 1, CPHA = 0串行接口工作。 这个设置意味着当SCLK高时,FS/CS下降边缘可能会下降。 FS/CS只要最后一个样的时间放松,只要最后一个SCLK下降沿在FS/CS出现在上升沿之前,SCLK也许高或低。
二、Vivado工程说明
-
ADS8332 连接到 PS 端的 SPI_1 上
-
RESET、EOC、CONVST连接到 PS 端的 GPIO-EMIO上
#MGT_Voltage_ADC_nRESET set_property PACKAGE_PIN T6 [get_ports {EMIO_tri_io[10]}] #MGT_Voltage_ADC_nCONVST set_property PACKAGE_PIN AA4 [get_ports {EMIO_tri_io[11]}] #MGT_Voltage_ADC_EOC set_property PACKAGE_PIN R6 [get_ports {EMIO_tri_io[12]}]
注意:
ADS8332 的 SDI 管脚为 输入(连接)设备MOSI), SDO输出(连接)设备MISO)
SPI控制器 的 SPI_1_io0_io 为 MOSI , SPI_1_io1_io 为 MISO
三、内核配置h> CONFIG_SPI_CADENCE
CONFIG_SPI_SPIDEV
Device Drivers --->
[*] SPI support --->
<*> Cadence SPI controller
<*> User mode SPI device driver support
CONFIG_SPI_CADENCE
CONFIG_SPI_SPIDEV
Device Drivers --->
[*] SPI support --->
<*> Cadence SPI controller
<*> User mode SPI device driver support
四、设备树配置
在 spi 节点中添加 spidev 节点,根据 芯片手册 配置设备树如下:
&spi1 {
is-decoded-cs = <0>;
num-cs = <1>;
status = "okay";
adc-ads8332@0 {
// 必须添加spidev的设备节点
compatible = "spidev";
spi-max-frequency = <40000000>;
reg = <0>;
};
};
说明:
- 使用现有驱动程序 "spidev"
- ADS8332 最大时钟频率 40MHz
五、测试程序
adc_ads833x.h
:
/****************************************************************************** * 文 件 名 称:adc_ads833x.h * 文件功能概述:实现ADS8332驱动接口声明 * 文 件 作 者: * 版 本:V1.0.0.0 * 修 订 记 录: ******************************************************************************/
#ifndef __ADC_ADS833x_H__
#define __ADC_ADS833x_H__
/*----------------------------------------------* * 包含头文件 * *----------------------------------------------*/
#include
/*----------------------------------------------* * 宏定义 * *----------------------------------------------*/
/******************内部寄存器 CMR(高4位) CFR(低12位)***************************************/
/*******************CMR 位定义********************************/
#define SELECT_CHANNEL_0 (0x00 << 4)
#define SELECT_CHANNEL_1 (0x01 << 4)
#define SELECT_CHANNEL_2 (0x02 << 4)
#define SELECT_CHANNEL_3 (0x03 << 4)
#define SELECT_CHANNEL_4 (0x04 << 4)
#define SELECT_CHANNEL_5 (0x05 << 4)
#define SELECT_CHANNEL_6 (0x06 << 4)
#define SELECT_CHANNEL_7 (0x07 << 4)
#define WAKE_UP (0x0B << 4)
#define READ_CFR (0x0C << 4)
#define READ_DATA (0x0D << 4)
#define WRITE_CFR (0x0E << 4)
#define DEFAULT_MODE (0x0F << 4)
/******************CFR 位定义***********************************/
#define AUTO_CHANNEL_SELECT (1 << 3)
#define SELECT_INTERNAL_OSC (1 << 2)
#define MANUAL_TRIGGER (1 << 1)
#define SAMPLE_250KPS (1 << 0)
#define EOC_ACTIVE_LOW (1 << 7)
#define EOC_FUNCTION (1 << 6)
#define PIN10_FOR_EOC (1 << 5)
#define AUTO_PWDN_DISABLE (1 << 4)
#define NAP_PWDN_DISABLE (1 << 3)
#define DEEP_PWDN_DISABLE (1 << 2)
#define TAG_BIT_OUTPUT (1 << 1)
#define NORMAL_OPERATION (1 << 0)
/**********************************************/
#define BIT(X) (1ul << X)
#define ADS8332_CMD_SEL_Ch_0 0x0000
#define ADS8332_CMD_SEL_Ch_1 0x1000
#define ADS8332_CMD_SEL_Ch_2 0x2000
#define ADS8332_CMD_SEL_Ch_3 0x3000
#define ADS8332_CMD_SEL_Ch_4 0x4000
#define ADS8332_CMD_SEL_Ch_5 0x5000
#define ADS8332_CMD_SEL_Ch_6 0x6000
#define ADS8332_CMD_SEL_Ch_7 0x7000
#define ADS8332_CMD_WAKE_UP 0xB000
#define ADS8332_CMD_READ_CFR 0xC000
#define ADS8332_CMD_READ_DATA 0xD000
#define ADS8332_CMD_WRITE_CFR 0xE000
#define ADS8332_CMD_DEFAULT_CFR 0xF000
//
//配置寄存器 (CFR)位定义
//
#define ADS8332_CFR_AUTO_CH BIT(11)
#define ADS8332_CFR_CCLK_INTERNAL BIT(10)
#define ADS8332_CFR_MAN_TRG BIT(9)
#define ADS8332_CFR_SAMPLE_RATE BIT(8)
#define ADS8332_CFR_POL_INT_EOC_LOW BIT(7)
#define ADS8332_CFR_PIN10_EOC BIT(6)
#define ADS8332_CFR_PIN10_OUTPUT BIT(5)
#define ADS8332_CFR_AUTO_NAP_DISABLE BIT(4)
#define ADS8332_CFR_NAP_PD_DISABLE BIT(3)
#define ADS8332_CFR_DEEP_PWD_DISABLE BIT(2)
#define ADS8332_CFR_TAG BIT(1)
#define ADS8332_CFR_RESET BIT(0)
/*********************************************/
#define ADS8332_RATIO (0xFFFF) //精度是65535
#define ADS8332_REF (4096) //单位是mv
/* * 数据类型-ADS8332的端口信息 */
typedef struct ADS833xCtrlStruc
{
char* ads833x_spi;
int ads833x_convst;
int ads833x_eoc;
int ads833x_reset;
}S_ADS833xCtrl;
/*----------------------------------------------* * 常量定义 * *----------------------------------------------*/
/*----------------------------------------------* * 外部变量说明 * *----------------------------------------------*/
/*----------------------------------------------* * 全局变量 * *----------------------------------------------*/
/*----------------------------------------------* * 模块级变量 * *----------------------------------------------*/
/*----------------------------------------------* * 外部函数原型说明 * *----------------------------------------------*/
/*----------------------------------------------* * 内部函数原型说明 * *----------------------------------------------*/
int ADS833xInit(S_ADS833xCtrl stADS833xCtrl);
int ADS8332xGetChValue(int spi_dev_fd, uint16_t channel_cmd, uint16_t *value, S_ADS833xCtrl stADS833xCtrl);
void ADS8332xReset(S_ADS833xCtrl stADS833xCtrl);
float ADS8332xVolToValue(uint16_t value);
void ADS833xGetAllVoltage();
#endif
adc_ads8332.c
:
/****************************************************************************** * 文 件 名 称: adc_ads8332.c * 文件功能概述:实现ADS8332的接口 * 文 件 作 者: * 版 本:V1.0.0.0 * 修 订 记 录: ******************************************************************************/ /***************************相关配置******************************************* 通过SPI进行操作,ADS8332将在下降沿取数据 根据ADS8332的spi的极性要求配置: CPOL = 1,CHPA=0(mode = 2) 或 CPOL = 0,CHPA=1(mode = 1) ******************************************************************************/ #include "adc_ads833x.h" #include "xgpio.h" #include "spidev.h" #include
#include #include #include #include #include #include #define ADS8332_SPI "/dev/spidev2.0" #define ADS8332_RST (uint16_t)(906+54+10) #define ADS8332_CONVST (uint16_t)(906+54+11) #define ADS8332_EOC (uint16_t)(906+54+12) #define ADS833x_SPI_MODE SPI_MODE_2 //mode = 2 (CPOL = 1,CHPA=0) #define ADS833x_SPI_SPEED 40000000 //speed = 40MHz #define ADS833x_DelayUs(x) usleep(x) /* *SPI总线上挂接的设备类型不一样,每次使用前均需对总线进行初始化 */ static int AD833xInitSPIModule(S_ADS833xCtrl stADS833xCtrl) { int fd = 0; fd = spi_dev_open((char*)stADS833xCtrl.ads833x_spi, ADS833x_SPI_MODE, ADS833x_SPI_SPEED); if(fd < 0){ printf("%s: Failed!", __func__); } return fd; } static void AD833xInitGPIO(S_ADS833xCtrl stADS833xCtrl) { xgpio_export(stADS833xCtrl.ads833x_reset); xgpio_export(stADS833xCtrl.ads833x_convst); xgpio_export(stADS833xCtrl.ads833x_eoc); xgpio_set_direction(stADS833xCtrl.ads833x_reset, GPIO_DIR_HIGH); xgpio_set_direction(stADS833xCtrl.ads833x_convst, GPIO_DIR_OUT); xgpio_set_direction(stADS833xCtrl.ads833x_eoc, GPIO_DIR_IN); } static void ADS833x_RST_SET(S_ADS833xCtrl stADS833xCtrl) { xgpio_set_value(stADS833xCtrl.ads833x_reset, GPIO_LOW); } static void ADS833x_RST_CLEAR(S_ADS833xCtrl stADS833xCtrl) { xgpio_set_value(stADS833xCtrl.ads833x_reset, GPIO_HIGH); } static void ADS833x_CONST_SET(S_ADS833xCtrl stADS833xCtrl) { xgpio_set_value(stADS833xCtrl.ads833x_convst, GPIO_LOW); } static void ADS833x_CONST_CLEAR(S_ADS833xCtrl stADS833xCtrl) { xgpio_set_value(stADS833xCtrl.ads833x_convst, GPIO_HIGH); } static int ADS833x_EOC_READ(S_ADS833xCtrl stADS833xCtrl) { return xgpio_get_value(stADS833xCtrl.ads833x_eoc); } /******************************************************************************* * 函 数 名:static int ADS833xReadConfig(int spi_dev_fd, uint16_t data) * 参 数:int spi_dev_fd SPI设备文件描述符,AD8332InitSPIModule() 函数返回值 uint16_t data 存放配置信息的变量 * 返 回:0 :正常返回 -1 :发生错误 * 创 建 人: * 创建时间: * 详 述:ADS8332发送读取配置命令并读取配置数据 * 修改记录: *******************************************************************************/ static int ADS833xReadConfig(int spi_dev_fd, uint16_t *data) { uint8_t tx_buf[2] = { 0}; uint8_t rx_buf[2] = { 0}; uint16_t cmd=ADS8332_CMD_READ_CFR; int ret = 0; tx_buf[0] = (cmd >> 8) & 0xFF; tx_buf[1] = cmd & 0xFF; ret = spi_dev_transfer(spi_dev_fd, tx_buf, rx_buf, sizeof(tx_buf)); if(ret < 0){ printf("%s: Failed!", __func__); return ret; } *data = (rx_buf[0] << 8 | rx_buf[1]) & 0x0FFF; // printf("read config data = 0x%x, rx[0] = 0x%x, rx[1] = 0x%x\n", data & 0x0FFF, rx_buf[0], rx_buf[1]); return 0; } /******************************************************************************* * 函 数 名:static int ADS833xInitModule(int spi_dev_fd) * 参 数:int spi_dev_fd SPI设备文件描述符,AD8332InitSPIModule() 函数返回值 * 返 回:0 :正常返回 -1 :发生错误 * 创 建 人: * 创建时间: * 详 述:ADS8332模块初始化 选用内部时钟做CCLK时:范围为10.9MHz-12.6Mhz 典型值为11.5MHz 选用SPI的时钟做CCLK来源时,CCLK=sclk/2:0.5-10.5MHz,故SPI的SCLK的范围应在1MHz-21MHz之间 * 修改记录: *******************************************************************************/ static int ADS833xInitModule(int spi_dev_fd) { uint8_t tx_buf[2] = { 0}; uint8_t rx_buf[2] = { 0}; uint16_t data=0x0000; int ret = 0; uint16_t config = 0; /* chan:manual; clock:internal; trigger:manual; sample:500kps */ data = (WRITE_CFR | SELECT_INTERNAL_OSC | MANUAL_TRIGGER)<<8; // EOC/INT:active low; pin used:EOC; pin10: EOC/INT output; // Auto-NAP PWDN:disabled; Nap PWDN:disable; Deep PWDN:disable // TAG bit:output disable; operation:normal data |= EOC_ACTIVE_LOW | EOC_FUNCTION |PIN10_FOR_EOC | AUTO_PWDN_DISABLE | NAP_PWDN_DISABLE | DEEP_PWDN_DISABLE | TAG_BIT_OUTPUT | NORMAL_OPERATION; tx_buf[0] = (data >> 8) & 0xFF; tx_buf[1] = data & 0xFF; ret = spi_dev_transfer(spi_dev_fd, tx_buf, rx_buf, sizeof(tx_buf)); if(ret < 0){ printf("%s: Failed!", __func__); return ret; } // printf("write config data = 0x%x, tx[0] = 0x%x, tx[1] = 0x%x\n", data, tx_buf[0], tx_buf[1]); ret = ADS833xReadConfig(spi_dev_fd, &config); if(ret < 0){ printf("%s: Failed!", __func__); return ret; } if(config != (data & 0x0FFF)){ printf("%s: ADS8332 Init Failed! Config = 0x%x\n", __func__, config); return -1; } return 0; } /******************************************************************************* * 函 数 名:int ADS833xInit(S_ADS833xCtrl stADS833xCtrl) * 参 数:S_ADS833xCtrl stADS833xCtrl S_ADS833xCtrl 结构体定义 * 返 回:大 于0 : 初始化完成 -1 :SPI总线初始化失败 * 创 建 人: * 创建时间: * 详 述:ADS8332初始化 注意手册29页关于CPOL和CPHA的说明 * 修改记录: *******************************************************************************/ int ADS833xInit(S_ADS833xCtrl stADS833xCtrl) { int fd = 0; AD833xInitGPIO(stADS833xCtrl); fd = AD833xInitSPIModule(stADS833xCtrl); if(fd < 0){ return -1; } ADS833xInitModule(fd); return fd; } /******************************************************************************* * 函 数 名:static int ADS833xConvert(S_ADS833xCtrl stADS833xCtrl) * 参 数:S_ADS833xCtrl stADS833xCtrl S_ADS833xCtrl 结构体定义 * 返 回:0 : 转换正常完成 -1 :转换超时 * 创 建 人: * 创建时间: * 详 述:ADS8332开始转换并等待转换完成 * 修改记录: *******************************************************************************/ static int ADS833xConvert(S_ADS833xCtrl stADS833xCtrl) { int i = 0; ADS833x_CONST_SET(stADS833xCtrl); ADS833x_DelayUs(20); ADS833x_CONST_CLEAR(stADS833xCtrl); ADS833x_DelayUs(20); // ADS833x_CONST_SET(stADS833xCtrl); // ADS833_DelayUs(20); while(ADS833x_EOC_READ(stADS833xCtrl) == 0) { //wait eoc=1 if(i >= 1000) { return -1; } i ++; } return 0; } /******************************************************************************* * 函 数 名:static int ADS833xSelectChannel(int spi_dev_fd, uint16_t channel_cmd) * 参 数:int spi_dev_fd SPI设备文件描述符,AD8332InitSPIModule() 函数返回值 uint16_t channel_cmd 选择通道指令 * 返 回:0 : 正常完成 -1 :发生错误 * 创 建 人: * 创建时间: * 详 述:ADS8332选择通道 * 修改记录: *******************************************************************************/ static int ADS833xSelectChannel(int spi_dev_fd, uint16_t channel_cmd) { uint8_t tx_buf[2] = { 0}; uint8_t rx_buf[2] = { 0}; int ret = 0; tx_buf[0] = (channel_cmd >> 8) & 0xFF; tx_buf[1] = channel_cmd & 0xFF; ret = spi_dev_transfer(spi_dev_fd, tx_buf, rx_buf, sizeof(tx_buf)); if(ret < 0){ printf("%s: Failed!", __func__); return ret; } return 0; } /******************************************************************************* * 函 数 名:static int ADS833xReadValue(int spi_dev_fd, uint32_t data) * 参 数:int spi_dev_fd SPI设备文件描述符,AD8332InitSPIModule() 函数返回值 uint32_t data 存放读取数据的变量 * 返 回:0 : 正常完成 -1 :发生错误 * 创 建 人: * 创建时间: * 详 述:ADS8332发送数据读取命令并读取数据 * 修改记录: *******************************************************************************/ static int ADS833xReadValue(int spi_dev_fd, uint32_t *data) { uint8_t tx_buf[3] = { 0}; uint8_t rx_buf[3] = { 0}; uint16_t cmd=ADS8332_CMD_READ_DATA; int ret = 0; uint16_t config = 0; tx_buf[0] = (cmd >> 4) & 0xFF; tx_buf[1] = cmd & 0xFF; ret = spi_dev_transfer(spi_dev_fd, tx_buf, rx_buf, sizeof(tx_buf)); if(ret < 0){ printf("%s: Failed!", __func__); return ret; } ret = ADS833xReadConfig(spi_dev_fd, &config); if(ret < 0){ printf("%s: Failed!", __func__); return ret; } if(config & TAG_BIT_OUTPUT){ uint8_t ch = rx_buf[2] >> 5; *data = ch << 16| rx_buf[0] << 8 | rx_buf[1]; }else{ *data = 0xFF << 16 | rx_buf[0] << 8 | rx_buf[1]; } return 0; } /******************************************************************************* * 函 数 名:int ADS833xGetChValue(int spi_dev_fd, uint16_t channel_cmd, uint16_t value) * 参 数:int spi_dev_fd SPI设备文件描述符 uint16_t channel_cmd 选择通道指令 uint16_t value 存放数据变量 * 返 回:0 : 正常完成 -1 :发生错误 * 创 建 人: * 创建时间: * 详 述:ADS8332发送转换命令并读取转换结果 * 修改记录: *******************************************************************************/ int ADS833xGetChValue(int spi_dev_fd, uint16_t channel_cmd, uint16_t *value, S_ADS833xCtrl stADS833xCtrl) { uint32_t data = 0; uint8_t ch = 0; int ret = 0; ret = ADS833xSelectChannel(spi_dev_fd, channel_cmd); if(ret < 0){ printf("%s: Failed!", __func__); return ret; } if(ADS833xConvert(stADS833xCtrl) == -1) { return -1; } ret = ADS833xReadValue(spi_dev_fd, &data);