广州网站建设信科便宜,品质网站设,宁波网站制作公司推荐,成全视频免费观看在线看128集前面我们讨论了AD7705这种ADC器件的驱动开发#xff0c;在实际中我们使用更多的是AD719x系列的ADC芯片、包括有AD7191、AD7192和AD7193等。接下来我们就来设计并开发AD719x的驱动程序。
1、功能概述
AD7192是一款适合高精密测量应用的低噪声完整模拟前端#xff0c;内置一个…前面我们讨论了AD7705这种ADC器件的驱动开发在实际中我们使用更多的是AD719x系列的ADC芯片、包括有AD7191、AD7192和AD7193等。接下来我们就来设计并开发AD719x的驱动程序。
1、功能概述
AD7192是一款适合高精密测量应用的低噪声完整模拟前端内置一个低噪声、 24 位Σ-Δ型模数转换器 (ADC)。片内低噪声增益级意味着可直接输入小信号。
1.1、硬件结构
AD7192可配置为两路差分输入或四路伪差分输入。片内通道序列器可以使能多个通道AD7192 按顺序在各使能通道上执行转换这可以简化与器件的通信。 片内 4.92 MHz时钟可以用作 ADC 的时钟源 或者也可以使用外部时钟或晶振。 该器件的输出数据速率可在 4.7 Hz 至 4.8 kHz 的范围内变化。
AD7192提供两种数字滤波器选项。 滤波器的选择会影响以编程输出数据速率工作时的均方根噪声和无噪声分辨率、建立时间以及 50 Hz/60 Hz 抑制。 针对要求所有转换均需建立的应用 AD7192 具有零延迟特性。
其功能结构图如下 1.2、内部寄存器
AD7192内部具有多个寄存器对AD7192的操作就是通过这些片内寄存器进行控制和数据寄存器/数据寄存器加状态信息配置。这些寄存器包括通信寄存器、状态寄存器、模式寄存器、配置寄存器、ID寄存器、GPOCON寄存器、失调寄存器以及满量程寄存器。其中通信寄存器和状态寄存器共享地址读操作时针对的是状态寄存器写操作时针对的是通讯寄存器。对任何寄存器的操作都是从写通讯寄存器开始。
1.2.1、通信寄存器
通信寄存器是一个 8 位只写寄存器。与该器件的所有通信均必须以对通信寄存器的写操作开始。写入通信寄存器的数据决定下一个操作是读操作还是写操作以及此操作发生在哪一个寄存器。通讯寄存器的格式如下 其中RS2、RS1、RS0这些位用于指示下一次操作的寄存器是哪一个寄存器具体如下 我们使用比较多的就是状态寄存器、模式寄存器、配置寄存器以及数据寄存器。后续会进一步了解这些寄存器。
1.2.2、状态寄存器
状态寄存器是一个8位只读寄存器。要访问ADC状态寄存器用户必须写入通信寄存器选择下一个操作为读操作并将 0 载入位RS2、位RS1 和位RS0。状态寄存器的格式如下 CHD0、CHD1、CHD2这些位指示哪一通道对应数据寄存器的内容。这些位不是指示目前正在转换哪一通道而是指示产生数据寄存器所含转换结果时选定了哪一通道。
1.2.3、模式寄存器
模式寄存器是一个24位寄存器可以从中读取数据也可以将数据写入其中。此寄存器用来选择工作模式、输出数据速率和时钟源。模式寄存器的格式如下 AD7192的工作模式有模式寄存器的MD2、MD1、MD0这几位来决定。具体的配置如下 我们配置什么样的工作模式决定了后续的操作。默认情况下为连续模式应用比较多的有单次模式、校准模式等。
1.2.4、配置寄存器
配置寄存器是一个 24 位寄存器可以从中读取数据也可以将数据写入其中。此寄存器用来配置 ADC 的单极性或双极性模式使能或禁用缓冲器使能或禁用激励电流选择增益以及选择模拟输入通道。 其中G2、G1、G0等位用于配置通道的增益。可以实现1倍、8倍、16倍、32倍、64倍、128倍等增益输入的范围也对应调整具体如下 位CH7、CH6、CH5、CH4、CH3、CH2、CH1、CH0用于设置使用的通道。每一位对应一个通道具体配置方式如下 其中CH0与CH4、CH5不可同时使用CH1与CH6、CH7不能同时使用。CH0和CH1是差分输入CH4、CH5、CH6、CH7是单端输入。
余下的ID寄存器、GPOCON寄存器、失调寄存器以及满量程寄存器比较容易理解不再说明。
2、驱动设计与实现
我们已经了解AD7192的基本情况接下来我们将据此来开发AD7192的驱动程序。
2.1、对象设计
我们的驱动程序依然设计为基于对象的操作。所以首先我们需要先设计对象。而关于对象的设计过程包括两方面内容对象类型的定义对象的初始化。
2.1.1、抽象对象类型
对于一个AD7192对象我们首先考虑到它有8个寄存器这些寄存器决定了它的操作特性所以我们记录这些寄存器的值用以标识它的状态。此外增益与极性虽然存在于配置寄存器中但我们希望在初始化的时候明确的指定它所以我们也将其作为属性以标识AD7192对象的配置状态。
而对于片选信号的控制、转换就绪信号的读取总线数据的输入输出这些依赖于硬件电路的操作我们都将其作为对象的操作来实现。此外延时操作在不同的硬件平台、有无操作系统的情况下实现方式大相径庭因此我们也将其作为对象的操作以便灵活处理。
据此我们可以抽象得AD7192对象类型为
/*定义用于操作的结构体*/
typedef struct Ad7192Object {uint32_t polar; //通道的极性uint32_t gain; //通道增益uint32_t Registers[8]; //存放寄存器值的数组void (*ReadWrite)(uint8_t *wDatauint8_t *rDatauint16_t size); //实现读写操作void (*ChipSelect)(AD7192CSType cs); //实现片选uint16_t (*GetReadyInput)(void); //实现Ready状态监视void (*Delay)(volatile uint32_t nTime); //实现ms延时操作
}Ad7192ObjectType;
2.1.2、初始化函数
在使用AD7192前先对其实行初始化所以我们需要一个对象初始化函数。初始化函数主要包括三个方面的内容检查对象即各种初始输入是否有效为对象的属性赋初值以及为操作指定函数指针对AD7192做前期配置。
关于AD7192的前期配置首先是软件复位连续写入40个1就可对AD7192实现复位。复位完成后对零点和量程进行校准。而后获取各寄存器的当前状态。初始化函数的具体实现代码如下
/*AD7192初始化配置*/
AD7192ErrorType AD7192Initialization(Ad7192ObjectType *adObjuint32_t ChannelsAD7192PolarType polarAD7192GainType gainAD7192ReadWriteType readWriteAD7192ChipSelectType csAD7192GetReadyInputType readyAD7192DelaymsType delayms)
{uint32_t polarity[]{UB_UNIUB_BI};uint32_t gains[]{GAIN_1GAIN_8GAIN_16GAIN_32GAIN_64GAIN_128};if((adObjNULL)||(readWriteNULL)||(readyNULL)||(delaymsNULL)){return AD7192_InitError;}if(csNULL){adObj-ChipSelectAD719xChipSelect;}else{adObj-ChipSelectcs;}adObj-polarpolarity[polar];adObj-gaingains[gain];adObj-Registers[REG_COM_STA]0x00;adObj-Registers[REG_MODE]0x00;adObj-Registers[REG_CONF]0x00;adObj-Registers[REG_DATA]0x00;adObj-Registers[REG_ID]0x00;adObj-Registers[REG_GPOCON]0x00;adObj-Registers[REG_OFFSET]0x00;adObj-Registers[REG_FS]0x00;adObj-ReadWritereadWrite;adObj-GetReadyInputready;adObj-Delaydelayms;AD7192SoftwareReset(adObj);adObj-Delay(1);AD7192InternalZeroScaleCalibration(adObjChannels);adObj-Delay(1);AD7192InternalFullScaleCalibration(adObjChannels);/*读取并存储全部寄存器的值*/ReadAD7192Register(adObjREG_COM_STA 8 REG_COM_STA);return AD7192_OK;
}
2.2、对象操作
对象初始化后就可对其进行操作。我们无论对AD7192进行何种操作其目的都是为了得到我们想要的数据。而从AD7192读取转换的结果有2种方式单次获取和连续获取。接下来我们就按此来说明AD7192的驱动设计。
2.2.1、实现单次数据转换
单次转换模式下AD7192 在完成转换后处于关断模式。 将模式寄存器中的MD2、MD1和MD0分别设置为0、0、1便可启动单次转换此时AD7192将上电执行单次转换然后返回关断模式。时序图如下所示 单次转换数据获取具体实现代码如下
/*单次转换数据获取*/
uint32_t GetSingleConvertionValue(Ad7192ObjectType *adObjuint32_t Channels)
{uint32_t dataCode0;AD7192StartSingleConvertion(adObjChannels);adObj-Delay(1);dataCode AD7192ReadConvertingData(adObj);dataCode dataCode 0x00FFFFFF;ReadAD7192Register(adObjREG_DATA 1REG_DATA);return dataCode;
}
2.2.2、实现连续数据转换
连续转换模式是上电后的默认转换模式。AD7192连续转换每次完成转换后状态寄存器中的RDY位变为低电平。如果CS为低电平则完成一次转换时DOUT/RDY 线路也会变为低电平。若要读取转换结果用户需要写入通信寄存器指示下一操作为读取数据寄存器。从数据寄存器中读取数据字后DOUT/RDY变为高电平。时序图如下所示 连续转换数据获取具体实现代码如下
/*连续转换数据获取dataCodes为8个元素的数组对应8个通道*/
void GettContinuousConvertionValue(Ad7192ObjectType *adObjuint32_t Channelsuint32_t *dataCodesint number)
{uint32_t dataCode0;uint8_t status255;AD7192StartContinuousConvertion(adObjChannels);for(int i0;inumber;i){dataCode AD7192ReadConvertingData(adObj);status((uint8_t)dataCode)0x07;dataCode (dataCode8) 0x00FFFFFF;dataCodes[status]dataCode;}
}
2.2.3、零点和量程校准
零点和量程校准包括内部校准和外部校准我们这里使用内部校准。
/*内部零点校准*/
static void AD7192InternalZeroScaleCalibration(Ad7192ObjectType *adObjuint32_t Channels)
{//配置寄存器斩波禁用基准电压1AI1-AI4单通道4路禁用激励电流禁用基准电压检测禁用缓冲器adObj-Registers[REG_CONF] 0;adObj-Registers[REG_CONF] CHOP_DIS|REF_IN1|Channels|BURN_DIS|REFDET_DIS|BUF_DIS|adObj-polar|adObj-gain;WriteAD7192Register(adObjREG_CONF1);//模式寄存器内部零点校准禁用状态同传内部时钟输出斩波4使能奇偶校验不分频仅用单周期转换使能禁用60Hz陷波FS128adObj-Registers[REG_MODE] 0;adObj-Registers[REG_MODE] MODE_INZCL|DAT_STA_DIS|INCLK_MCLK2EN|SINC_4|ENPAR_EN|CLK_DIV_DIS|SINGLECYCLE_DIS|REJ60_DIS|0x080;WriteAD7192Register(adObjREG_MODE 1);adObj-ChipSelect(AD7192CS_Enable);while(adObj-GetReadyInput() 1){;} //等待RDY为0;adObj-ChipSelect(AD7192CS_Disable);
}/*内部量程校准*/
static void AD7192InternalFullScaleCalibration(Ad7192ObjectType *adObjuint32_t Channels)
{//配置寄存器斩波禁用基准电压1AI1-AI4单通道4路禁用激励电流禁用基准电压检测禁用缓冲器adObj-Registers[REG_CONF] 0;adObj-Registers[REG_CONF] CHOP_DIS|REF_IN1|Channels|BURN_DIS|REFDET_DIS|BUF_DIS|adObj-polar|adObj-gain;WriteAD7192Register(adObjREG_CONF 1);//模式寄存器内部量程校准禁用状态同传内部时钟输出斩波4使能奇偶校验不分频禁用单周期转换使能禁用60Hz陷波FS128adObj-Registers[REG_MODE] 0;adObj-Registers[REG_MODE] MODE_INFCL|DAT_STA_DIS|INCLK_MCLK2EN|SINC_4|ENPAR_EN|CLK_DIV_2|SINGLECYCLE_DIS|REJ60_DIS|0x080; WriteAD7192Register(adObjREG_MODE 1);adObj-ChipSelect(AD7192CS_Enable);while(adObj-GetReadyInput() 1){;} //等待RDY为0;adObj-ChipSelect(AD7192CS_Disable);
}
2.2.4、实现内部温度转换
AD7192内置一个温度传感器。利用配置寄存器中的CH2位可以选择温度传感器。如果CH2位设置为1就会使能温度传感器。使用温度传感器并选择双极性模式时如果温度为0K器件应返回0x800000码。为使传感器发挥最佳性能需要执行单点校准。因此应记录25°C 时的转换结果并计算灵敏度。 灵敏度约为2815码 /°C。温度传感器的计算公式为
温度 (K) ( 转换结果 – 0x800000)/2815 K
温度 (°C) 温度 (K) – 273
单点校准之后内部温度传感器的精度典型值为 ±2°C。具体的实现代码如下
/*读取内部温度数据返回摄氏度温度*/
float GetTemperatureValue(Ad7192ObjectType *adObj)
{uint32_t temperatureCode0;float temp 0.0;//模式寄存器单次转换模式禁用状态同传内部时钟输出斩波4使能奇偶校验不分频禁用单周期转换使能禁用60Hz陷波FS128adObj-Registers[REG_MODE] 0;adObj-Registers[REG_MODE] MODE_SING|DAT_STA_DIS|INCLK_MCLK2EN|SINC_4|ENPAR_EN|CLK_DIV_DIS|SINGLECYCLE_DIS|REJ60_DIS|0x080;WriteAD7192Register(adObjREG_MODE 1);//配置寄存器斩波禁用基准电压1内部温度禁用激励电流禁用基准电压检测禁用缓冲器adObj-Registers[REG_CONF] 0; adObj-Registers[REG_CONF] CHOP_DIS|REF_IN1|TEMP|BURN_DIS|REFDET_DIS|BUF_DIS|UB_BI|GAIN_1;WriteAD7192Register(adObjREG_CONF 1);temperatureCode AD7192ReadConvertingData(adObj);temp (temperatureCode-0x800000)/2815.0-273;return temp;
}
3、驱动的使用
我们已经设计并实现了AD7192模数转换器的驱动。现在我们将考虑如何使用这一驱动实现基于AD7192模数转换器的简单应用。
3.1、声明并初始化对象
使用基于对象的操作我们需要先得到这个对象所以我们先要使用前面定义的AD7192模数转换器对象类型声明一个AD7192模数转换器对象变量具体操作格式如下
Ad7192ObjectType ad7192;
声明了这个对象变量并不能立即使用我们还需要使用驱动中定义的初始化函数对这个变量进行初始化。这个初始化函数所需要的输入参数如下
Ad7192ObjectType *adObj所要初始化的对象变量
uint32_t Channels需要初始化的通道
AD7192PolarType polar通道初始化的极性
AD7192GainType gain通道的增益
AD7192ReadWriteType readWrite读写操作函数
AD7192ChipSelectType cs片选信号操作函数
AD7192GetReadyInputType ready就绪信号检测函数
AD7192DelaymsType delayms毫秒延时函数
对于这些参数对象变量我们已经定义了。所需要初始化的通道、通道的信号极性、采用的增益倍数根据实际情况选择通道为宏定义、极性和增益为枚举。主要的是我们需要定义几个函数并将函数指针作为参数。这几个函数的类型如下
/*定义读写操作函数指针类型*/
typedef void (*AD7192ReadWriteType)(uint8_t *wDatauint8_t *rDatauint16_t size);/*实现片选*/
typedef void (*AD7192ChipSelectType)(AD7192CSType cs);/*实现Ready状态监视*/
typedef uint16_t (*AD7192GetReadyInputType)(void);/*实现ms延时操作*/
typedef void (*AD7192DelaymsType)(volatile uint32_t nTime);
对于这几个函数我们根据样式定义就可以了具体的操作可能与使用的硬件平台有关系。片选操作函数用于多设备需要软件操作时如采用硬件片选可以传入NULL即可。具体函数定义如下
/*定义读写操作函数指针类型*/
void AD7192ReadWrite(uint8_t *wDatauint8_t *rDatauint16_t size)
{HAL_SPI_TransmitReceive(ad77192hspiwDatarDatasize1000);
}/*实现片选*/
void AD7192ChipSelect(AD7192CSType cs)
{if(AD7192CS_Enablecs){HAL_GPIO_WritePin(GPIOF GPIO_PIN_4 GPIO_PIN_RESET);}else{HAL_GPIO_WritePin(GPIOF GPIO_PIN_4 GPIO_PIN_SET);}
}/*实现Ready状态监视*/
uint16_t AD7192GetReadyInput(void)
{return HAL_GPIO_ReadPin(GPIOCGPIO_PIN_0);
}
对于延时函数我们可以采用各种方法实现。我们采用的STM32平台和HAL库则可以直接使用HAL_Delay()函数。于是我们可以调用初始化函数如下
AD7192Initialization(ad7192AIN1_AIN2|AIN3_AIN4AD7192_UnipolarAD7192Gain1AD7192ReadWriteAD7192ChipSelectAD7192GetReadyInputHAL_Delay);
3.2、基于对象进行操作
我们定义了对象变量并使用初始化函数给其作了初始化。接着我们就来考虑操作这一对象获取我们想要的数据。在驱动中我们已经封装了单次或是连续获取某一通道模数转换数据的函数。接下来我们就采用我们封装的AD7192数模转换其的驱动获取通道的数据。
/* 单次获取两个差分通道的值 */
void GetDifferentialData(void)
{uint16_t dataCode[2];dataCode[0]GetSingleConvertionValue(ad7192AIN1_AIN2);dataCode[1]GetSingleConvertionValue(ad7192AIN3_AIN4);
}
因为初始化函数初始化为两个差分通道所以我们的函数就是获取了两个差分通道的转换值。获取了ADC的数据后就可以根据每个通道所对应的物理量量程范围计算得到物理量值。
4、应用总结
在这一篇中我们设计并实现了AD7192数模转换器的驱动程序。并使用这一驱动程序实现了单次获取两个差分通道转换数据的简单应用。得到了我们想要的结果这说明我们的驱动设计是正确的。
在使用驱动时需要注意。选择通道时由于可以是2个差分通道和4个单端通道所以差分通道一不可以与AIN1、AIN2同时使用同样的差分通道二不能与AIN3、AIN4同时使用。
在使用驱动时需注意采用SPI接口的器件需要考虑片选操作的问题。如果片选信号是通过硬件电路来实现的我们在初始化时给其传递NULL值。如果是软件操作片选则传递我们编写的片选操作函数。
完整的源代码可在GitHub下载https://github.com/foxclever/ExPeriphDriver
欢迎关注