参考文章

  1. ZYNQ Linux使用SPI驱动
  2. STM32之ADS8332
  3. 详解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 = 1),通道选择也可以自动进行。如果设备被编程为自动通道选择模式,那么所有通道的信号将以固定的顺序获取。 在自动通道选择模式下,进入此模式后的第一次转换总是来自启用此模式之前完成的最后一次转换的通道。 然后依次扫描通道,直到并包括最后一个通道(即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_D11设置为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后添加三个地址位,以指示哪个通道对应结果。 这些地址位是通道0的000,通道1的001,通道2的010,通道3的011,通道4的100,通道5的101,通道6的110和通道7的111。 当启用TAG模式时,转换器需要至少19个sclk来传输16位转换结果和3个TAG位。

5. ADS8332转换结果

转换结果是16位的二进制格式数据,如表6所示。 通常需要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

在这里插入图片描述

三、内核配置

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 <stdint.h>

/*----------------------------------------------*
 * 宏定义                                       *
 *----------------------------------------------*/
 
/******************内部寄存器 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 <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/types.h>
#include <time.h>

#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);
	if(ret < 0){
		printf("%s: Failed!", __func__);
		return ret;
	}

	ch = data >> 16;
	if(ch != 0xFF && ch != channel_cmd >> 12){
		printf("%s: Get Channel Value Failed!\n", __func__);
		return -1;
	}
	*value = data & 0xFFFF;
	
  	return 0;
}
 
/*******************************************************************************
 * 函 数 名:void ADS833xReset(S_ADS833xCtrl stADS833xCtrl)
 * 参    数:S_ADS833xCtrl stADS833xCtrl
 * 返    回:
 * 创 建 人:
 * 创建时间:
 * 详    述:ADS8332复位   拉低至少25ns
 * 修改记录:
*******************************************************************************/
void ADS833xReset(S_ADS833xCtrl stADS833xCtrl)
{
	ADS833x_RST_SET(stADS833xCtrl);

	ADS833x_DelayUs(10);

	ADS833x_RST_CLEAR(stADS833xCtrl);
}
 
/*******************************************************************************
 * 函 数 名:float ADS8332xVolToValue(uint16 value)
 * 参    数:uint16 value: 需要转化的值
 * 返    回:对应的电压值
 * 创 建 人:
 * 创建时间:
 * 详    述:获得指定值对应的电压值,单位是mv
 * 修改记录:
*******************************************************************************/
float ADS833xVolToValue(uint16_t value)
{
	float temp = 0;

	temp = (float)((float)(value) / ADS8332_RATIO);

	return  (float)(temp * ADS8332_REF);
}

/*
* 读取0-7通道电压值
*/
void ADS833xGetAllVoltage()
{
	int fd = 0;
	uint16_t value = 0;
	float voltage = 0;
	int ret = 0;

	/*
	 * 用于记录ADS8332的端口信息
	 */
	S_ADS833xCtrl stADS8332Ctrl = 
	{
		ADS8332_SPI,
		ADS8332_CONVST,    //CONST
		ADS8332_EOC,	//EOC
		ADS8332_RST,	//RST
	};
	
	
	fd = ADS833xInit(stADS8332Ctrl);

	ret = ADS833xGetChValue(fd, ADS8332_CMD_SEL_Ch_0, &value, stADS8332Ctrl);
	voltage = ADS833xVolToValue(value);
	printf("\tCh_%d_DDR_1V5: Voltage = %f mV\n", (ADS8332_CMD_SEL_Ch_0 >> 12), voltage);

	ret = ADS833xGetChValue(fd, ADS8332_CMD_SEL_Ch_1, &value, stADS8332Ctrl);
	voltage = ADS833xVolToValue(value);
	printf("\tCh_%d_1V8: Voltage = %f mV\n", (ADS8332_CMD_SEL_Ch_1 >> 12), voltage);

	ret = ADS833xGetChValue(fd, ADS8332_CMD_SEL_Ch_2, &value, stADS8332Ctrl);
	voltage = ADS833xVolToValue(value) * 2; //根据原理图中 ADS8332 输入通道的分压电阻计算
	printf("\tCh_%d_2V5: Voltage = %f mV\n", (ADS8332_CMD_SEL_Ch_2 >> 12), voltage);

	ret = ADS833xGetChValue(fd, ADS8332_CMD_SEL_Ch_3, &value, stADS8332Ctrl);
	voltage = ADS833xVolToValue(value) * 2; //根据原理图中 ADS8332 输入通道的分压电阻计算
	printf("\tCh_%d_3V3: Voltage = %f mV\n", (ADS8332_CMD_SEL_Ch_3 >> 12), voltage);

	ret = ADS833xGetChValue(fd, ADS8332_CMD_SEL_Ch_4, &value, stADS8332Ctrl);
	voltage = ADS833xVolToValue(value);
	printf("\tCh_%d_1V0: Voltage = %f mV\n", (ADS8332_CMD_SEL_Ch_4 >> 12), voltage);

	ret = ADS833xGetChValue(fd, ADS8332_CMD_SEL_Ch_5, &value, stADS8332Ctrl);
	voltage = ADS833xVolToValue(value);
	printf("\tCh_%d_1V2: Voltage = %f mV\n", (ADS8332_CMD_SEL_Ch_5 >> 12), voltage);

	ret = ADS833xGetChValue(fd, ADS8332_CMD_SEL_Ch_6, &value, stADS8332Ctrl);
	voltage = ADS833xVolToValue(value);
	printf("\tCh_%d_AUX_1V8: Voltage = %f mV\n", (ADS8332_CMD_SEL_Ch_6 >> 12), voltage);

	ret = ADS833xGetChValue(fd, ADS8332_CMD_SEL_Ch_7, &value, stADS8332Ctrl);
	voltage = ADS833xVolToValue(value) * 2; //根据原理图中 ADS8332 输入通道的分压电阻计算
	printf("\tCh_%d_5V0: Voltage = %f mV\n", (ADS8332_CMD_SEL_Ch_7 >> 12), voltage);

}

int main()
{
	ADS833xGetAllVoltage();

	return 0;
}

spidev.h:

#ifndef __SPIDEV_H__
#define __SPIDEV_H__

#include <stdlib.h>

#define	SPI_CPHA	0x01			/* clock phase */
#define	SPI_CPOL	0x02			/* clock polarity */
#define	SPI_MODE_0	(0|0)			/* (original MicroWire) */
#define	SPI_MODE_1	(0|SPI_CPHA)
#define	SPI_MODE_2	(SPI_CPOL|0)
#define	SPI_MODE_3	(SPI_CPOL|SPI_CPHA)
#define	SPI_CS_HIGH	0x04			/* chipselect active high? */
#define	SPI_LSB_FIRST	0x08			/* per-word bits-on-wire */
#define	SPI_3WIRE	0x10			/* SI/SO signals shared */
#define	SPI_LOOP	0x20			/* loopback mode */
#define	SPI_NO_CS	0x40			/* 1 dev/bus, no chipselect */
#define	SPI_READY	0x80			/* slave pulls low to pause */
#define	SPI_TX_DUAL	0x100			/* transmit with 2 wires */
#define	SPI_TX_QUAD	0x200			/* transmit with 4 wires */
#define	SPI_RX_DUAL	0x400			/* receive with 2 wires */
#define	SPI_RX_QUAD	0x800			/* receive with 4 wires */
#define	SPI_CS_WORD	0x1000			/* toggle cs after each word */
#define	SPI_TX_OCTAL	0x2000			/* transmit with 8 wires */
#define	SPI_RX_OCTAL	0x4000			/* receive with 8 wires */

#define	SPI_SHOW_BUF 1	//打印收发buf数据
#define	SPI_NOT_SHOW_BUF 0 //关闭打印收发buf数据

void spi_dev_set_verbose(int show);

int spi_dev_transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len);

int spi_dev_open(char* spi_device, int spi_mode, int spi_speed);

void spi_dev_close(int spi_dev_fd);

#endif /*__SPIDEV_H__*/

spidev.c:

/*
 * SPI testing utility (using spidev driver)
 *
 * Copyright (c) 2007  MontaVista Software, Inc.
 * Copyright (c) 2007  Anton Vorontsov <avorontsov@ru.mvista.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License.
 *
 * Cross-compile with cross-gcc -I/path/to/cross-kernel/include
 */

#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/types.h>
#include <getopt.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#include <sys/stat.h>
#include <linux/spi/spidev.h>


#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))

static uint32_t mode;
static uint8_t bits = 8;
static uint16_t delay;
static uint32_t speed = 500000;
static int verbose;
static char *input_file;
static char *output_file;

static void pabort(const char *s)
{
	perror(s);
//	abort();
}

static void hex_dump(const void *src, size_t length, size_t line_size,
		     char *prefix)
{
	int i = 0;
	const unsigned char *address = src;
	const unsigned char *line = address;
	unsigned char c;

	printf("%s | ", prefix);
	while (length-- > 0) {
		printf("%02X ", *address++);
		if (!(++i % line_size) || (length == 0 && i % line_size)) {
			if (length == 0) {
				while (i++ % line_size)
					printf("__ ");
			}
			printf(" | ");  /* right close */
			while (line < address) {
				c = *line++;
				printf("%c", (c < 33 || c == 255) ? 0x2E : c);
			}
			printf("\n");
			if (length > 0)
				printf("%s | ", prefix);
		}
	}
}

/*
*  Unescape - process hexadecimal escape character
* 	 converts shell input "\x23" -> 0x23
*/
static int unescape(char *_dst, char *_src, size_t len)
{
	int ret = 0;
	int match;
	char *src = _src;
	char *dst = _dst;
	unsigned int ch;

	while (*src) {
		if (*src == '\\' && *(src+1) == 'x') {
			match = sscanf(src + 2, "%2x", &ch);
			if (!match){
				pabort("malformed input string");
				return -1;
			}

			src += 4;
			*dst++ = (unsigned char)ch;
		} else {
			*dst++ = *src++;
		}
		ret++;
	}
	return ret;
}

/*
* 设置打印TX和RXBUF数据
* 1:打印, 0:不打印
*/
void spi_dev_set_verbose(int show)
{
	verbose = show;
}

int spi_dev_transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
{
	int ret;
	int out_fd;
	struct spi_ioc_transfer tr = {
		.tx_buf = (unsigned long)tx,
		.rx_buf = (unsigned long)rx,
		.len = len,
		.delay_usecs = delay,
		.speed_hz = speed,
		.bits_per_word = bits,
	};

	if (mode & SPI_TX_QUAD)
		tr.tx_nbits = 4;
	else if (mode & SPI_TX_DUAL)
		tr.tx_nbits = 2;
	if (mode & SPI_RX_QUAD)
		tr.rx_nbits = 4;
	else if (mode & SPI_RX_DUAL)
		tr.rx_nbits = 2;
	if (!(mode & SPI_LOOP)) {
		if (mode & (SPI_TX_QUAD | SPI_TX_DUAL))
			tr.rx_buf = 0;
		else if (mode & (SPI_RX_QUAD | SPI_RX_DUAL))
			tr.tx_buf = 0;
	}

	ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
	if (ret < 1){
		pabort("can't send spi message");
		return -1;
	}

	if (verbose)
		hex_dump(tx, len, 32, "TX");

	if (output_file) {
		out_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
		if (out_fd < 0){
			pabort("could not open output file");
			return -1;
		}

		ret = write(out_fd, rx, len);
		if ((size_t)ret != len){
			close(out_fd);
			pabort("not all bytes written to output file");
			return -1;
		}

		close(out_fd);
	}

	if (verbose)
		hex_dump(rx, len, 32, "RX");

	return 0;
}

int spi_dev_transfer_file(int fd, char *filename)
{
	ssize_t bytes;
	struct stat sb;
	int tx_fd;
	uint8_t *tx;
	uint8_t *rx;
	int ret;

	if (stat(filename, &sb) == -1){
		pabort("can't stat input file");
		return -1;
	}

	tx_fd = open(filename, O_RDONLY);
	if (tx_fd < 0){
		pabort("can't open input file");
		return -1;
	}

	tx = malloc(sb.st_size);
	if (!tx){
		pabort("can't allocate tx buffer");
		return -1;
	}

	rx = malloc(sb.st_size);
	if (!rx){
		pabort("can't allocate rx buffer");
		return -1;
	}

	bytes = read(tx_fd, tx, sb.st_size);
	if (bytes != sb.st_size){
		pabort("failed to read input file");
		return -1;
	}

	ret = spi_dev_transfer(fd, tx, rx, sb.st_size);

	free(rx);
	free(tx);
	close(tx_fd);
	
	return ret;
}

int spi_dev_transfer_escaped_string(int fd, char *str)
{
	size_t size = strlen(str);
	uint8_t *tx;
	uint8_t *rx;
	int ret;

	tx = malloc(size);
	if (!tx){
		pabort("can't allocate tx buffer");
		return -1;
	}

	rx = malloc(size);
	if (!rx){
		pabort("can't allocate rx buffer");
		return -1;
	}

	size = unescape((char *)tx, str, size);
	ret = spi_dev_transfer(fd, tx, rx, size);
	free(rx);
	free(tx);

	return ret;
}

int spi_dev_open(char* spi_device, int spi_mode, int spi_speed)
{
	int ret = 0;
	int fd = 0;

	mode = spi_mode;
	bits = 8;
	speed = spi_speed;


	fd = open(spi_device, O_RDWR);
	if (fd < 0){
		pabort("can't open device");
		return -1;
	}

	/*
	 * spi mode
	 */
	ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
	if (ret == -1){
		pabort("can't set spi mode");
		return ret;
	}

	ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
	if (ret == -1){
		pabort("can't get spi mode");
		return ret;
	}

	/*
	 * bits per word
	 */
	ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
	if (ret == -1){
		pabort("can't set bits per word");
		return ret;
	}

	ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
	if (ret == -1){
		pabort("can't get bits per word");
		return ret;
	}

	/*
	 * max speed hz
	 */
	ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
	if (ret == -1){
		pabort("can't set max speed hz");
		return ret;
	}

	ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
	if (ret == -1){
		pabort("can't get max speed hz");
		return ret;
	}

//	printf("spi device: %s\n", spi_device);
//	printf("spi mode: 0x%x\n", mode);
//	printf("bits per word: %d\n", bits);
//	printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);

	return fd;
}

void spi_dev_close(int spi_dev_fd)
{
	close(spi_dev_fd);
}

xgpio.h:

#ifndef __XGPIO_H__
#define __XGPIO_H__


#define GPIO_HIGH	 	1
#define GPIO_LOW	 	0

#define GPIO_DIR_IN   	"in"
#define GPIO_DIR_OUT  	"out"
#define GPIO_DIR_HIGH	"high"
#define GPIO_DIR_LOW  	"low"

int xgpio_get_value(int gpio);

int xgpio_set_value(int gpio, int value);

int xgpio_get_direction(int gpio, char* direction);

int xgpio_set_direction(int gpio, char* direction);

int xgpio_export(int gpio);

unsigned int xgpio_get_chn_value(int gpio_start_num, int gpio_width);

void xgpio_set_chn_value(int gpio_start_num, int gpio_width, unsigned int value);

#endif /*__XGPIO_H__*/

xgpio.c:

#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <string.h>
#include "xgpio.h"

#define GPIO_BASE_PATH 		    "/sys/class/gpio/gpio"
#define SYSFS_GPIO_EXPORT          "/sys/class/gpio/export"

int xgpio_get_value(int gpio)
{
	int result;
	int fd = 0;
	char value;
	char path[100];
	ssize_t n;

	memset(path, 0, sizeof(path));
	sprintf(path, "%s%d%s", GPIO_BASE_PATH, gpio, "/value");

	fd = open(path, O_RDWR);
	if (fd < 0) {
		perror(path);
		return -1;
	}

	lseek(fd, 0, SEEK_SET);
	n = read(fd, &value, 1);
	if(n < 0){
		perror("read");
		close(fd);
		return -1;
	}
	if(value == '1')
	{
		result = GPIO_HIGH;
	}
	else 
		result = GPIO_LOW;

	close(fd);
	
  	return result;
}

int xgpio_set_value(int gpio, int value)
{
	int fd = 0;
	char val, buf;
	ssize_t n;
	char path[100];

	memset(path, 0, sizeof(path));
	sprintf(path, "%s%d%s", GPIO_BASE_PATH, gpio, "/value");

	fd = open(path, O_RDWR);
	if (fd < 0) {
		perror(path);
		return -1;
	}
	lseek(fd, 0, SEEK_SET);

	if(value == 0)
		buf = '0';
	else
		buf = '1';

	n = write(fd, &buf, 1);
	if(n != 1){
		perror("write");
		close(fd);
		return -1;
	}

	lseek(fd, 0, SEEK_SET);

	n = read(fd, &val, 1);
	if(n != 1 || val != buf)
	{
		perror("read");
		close(fd);
		return -1;
	}

	close(fd);

	return 0;
}

int xgpio_set_direction(int gpio, char* direction)
{
	int fd = 0;
	char buf[5] = {0,};
	ssize_t n=0, wr_n=0;
	char path[100];


	memset(path, 0, sizeof(path));
	sprintf(path, "%s%d%s", GPIO_BASE_PATH, gpio, "/direction");

	fd = open(path, O_RDWR);
	if (fd < 0) {
		perror(path);
		return -1;
	}
	lseek(fd, 0, SEEK_SET);

	strcpy(buf, direction);
	wr_n = strlen(buf);

	n = write(fd, &buf, wr_n);
	if(n != wr_n){
		perror("write");
		close(fd);
		return -1;
	}
		
	close(fd);
	return 0;

}


int xgpio_get_direction(int gpio, char* direction)
{
	int fd = 0;
	char buf[4] = {0};
	ssize_t n=0;
	char path[100];


	memset(path, 0, sizeof(path));
	sprintf(path, "%s%d%s", GPIO_BASE_PATH, gpio, "/direction");

	fd = open(path, O_RDWR);
	if (fd < 0) {
		perror(path);
		return -1;
	}
	lseek(fd, 0, SEEK_SET);

	n = read(fd, &buf, 3);
	if(n != 3 && n != 2){
		perror("write");
		close(fd);
		return -1;
	}
	strcpy(direction, buf);

	close(fd);
	return 0;
}


int xgpio_export(int gpio)
{
	int fd = 0;
	char path[100];
	char gpio_num[5];
	ssize_t n;
	static int flag = 0;

	sprintf(path, "%s%d%s", GPIO_BASE_PATH, gpio, "/value");
	if((access(path, F_OK)) == 0){//检查文件存在性
	  	//printf("%s: gpio%d  The file already exists!\n", __func__, gpio);
	  	return 0;//文件存在
  	}

  	if(flag == 0){
  		system("chmod 777 /sys/class/gpio/export");
  		flag = 1;
  	}
  
	fd = open(SYSFS_GPIO_EXPORT, O_WRONLY);
  	if (fd < 0) {
      	perror(SYSFS_GPIO_EXPORT);
      	return -1;
  	}
  
  	sprintf(gpio_num, "%d", gpio);
  	n = write(fd, gpio_num, 4);
	if(n < 0){
		perror("write");
		close(fd);
		return -1;
	}
	
	close(fd);
  	return 0;
}

unsigned int xgpio_get_chn_value(int gpio_start_num, int gpio_width)
{
	int i;
  	unsigned int value = 0;
	
	for(i = 0; i < gpio_width; i++)
	{
		value |= (xgpio_get_value(gpio_start_num + i) & 0x01) << i;
	}
	
  	return value;
}

void xgpio_set_chn_value(int gpio_start_num, int gpio_width, unsigned int value)
{
	int i;
	
	for(i = 0; i < gpio_width; i++)
	{
		xgpio_set_value((gpio_start_num + i), (value >> i) & 0x01);
	}
}

说明:

  1. SPI 配置(spidev.c 参考 kernel/tools/spi/spidev_test.c):

    • SPI模式配置为 SPI_MODE_2 (CPOL = 1,CHPA=0)
    • SPI速率配置为 40000000(40MHz)
    • SPI BITS_PER_WORD 默认为 8bits
  2. ADS8332配置:

    • 通道选择:手动选择;
    • 时钟: 内部 OSC 时钟
    • 触发方式(转换开始):手动触发
    • 自动触发模式的采样率:500kps
    • EOC/INT active low
    • Pin used as EOC
    • Pin 10 is used as EOC/INT output
    • Auto-NAP Power-Down mode disabled
    • Nap Power-Down disabled
    • Deep Power-Down disabled
    • TAG bit output enabled
    • Normal operation
  3. 电压转换

    • 转换结果为 16 bits, 所有测量精度为 0xFFFF(65535)
    • VA = VBD = 5 V 时,VREF (REF+ – REF–) = 4.096 V
    #define ADS8332_RATIO  (0xFFFF)  //精度是65535
    #define ADS8332_REF    (4096)   //单位是mv
    
    float ADS833xVolToValue(uint16_t value)
    {
    	float temp = 0;
    	temp = (float)((float)(value) / ADS8332_RATIO);
    	return  (float)(temp * ADS8332_REF);
    }
    
    • 根据硬件原理图,采集电压为分压后的,所以需根据分压电阻计算分压前电压。
    voltage = ADS833xVolToValue(value) * 2; //根据原理图中 ADS8332 输入通道的分压电阻计算
    

    在这里插入图片描述

六、测试

  1. 查看 spidev 设备:

    ls /dev/spidev*
    

    在这里插入图片描述

  2. 运行测试程序
    在这里插入图片描述

Logo

更多推荐