【阅读整理】野火ADC_AD7192模块资料
文章目录
- 1 芯片介绍
- 1.1 模块简介
- 1.2 参数特性
- 2 模块接口
- 3 程序流程
- 3.1 通信寄存器(RS2, RS1, RS0 = 0, 0, 0)
- 3.2 RS2 至 RS0 寄存器地址位
- 4 程序流程
- 5 例程
- 5.1连线
- 5.2 例程说明
- 5.2.1 初始化
- 5.2.2 初始化 AD7192,模块软件复位
- 5.2.3 使用 ReadFromAD7192ViaSPI 函数对模块连接状态检测
- 5.2.4 根据MODE宏定义来选择模式进行运行
参考教程:EBF-AD7192
1 芯片介绍
1.1 模块简介
ADC_AD7192 24位 ADC模块是一款可配置为两路差分输入或四路伪差分输入的 24 位 Σ-Δ型模数转换模块。片内通道序列器可使能多个通道,AD7192 按顺序在各使能通道上执行转换,输出数据速率可设置范围为 4.7 Hz~4.8 KHz,使用 4.9152MHz 的外部晶振提供外部时钟,使用多层 PCB 设计, 搭配高精度电阻,测量范围可调
1.2 参数特性
◆ 使用芯片:AD7192
◆ 工作电压:5.0V
◆ 电流:10mA
◆ 分辨率:24位
◆ 可编程增益:1~128
◆ 输出数据速率:4.7Hz ~ 4.8KHz
◆ 工作温度范围:-40℃~+105℃
◆ 无噪声分辨率 :约16 位(2.4 kHz, G = 128)
◆ 输入通道:两个差分输入或四个伪差分输入
◆ 时钟:外部4.9152MHz 晶振/片内 4.92MHz 时钟
◆ 通信接口:三线式串行接口,与 SPI、QSPI、MICROWIRE和 DSP兼容
◆ 伪差分测量范围:AINX与AGND 0~+3.3V
◆ 差分测量范围:两条差分通道分别以AIN2或AIN4为电压基准,差分范围±3.3V
◆ 应用领域:压力测量、温度测量、数据采集等。
2 模块接口
注:1.以SPI接口为例介绍,接口兼容QSPI、MICROWIRE和 DSP
2.J2 引出J1的部分脚,J2可通过配套的灰色排线直插野火部分电机板的接口,方便连接
3.伪差分通道:AIN1和AGND、AIN2和AGND、AIN3和AGND以及AIN4和AGND
4.差分通道:AIN1和AIN2以及AIN3和AIN4
3 程序流程
AD7192内部具有多个寄存器,对AD7192的操作就是通过这些片内寄存器进行控制和数据寄存器/数据寄存器加状态信息配置。这些寄存器包括:通信寄存器、状态寄存器、模式寄存器、配置寄存器、ID寄存器、GPOCON 寄存器(数字输出引脚未引出)、失调寄存器以及满量程寄存器。其中通信寄存器和状态寄存器共享地址,读操作时针对的是状态寄存器,写操作时针对的是通信寄存器。
3.1 通信寄存器(RS2, RS1, RS0 = 0, 0, 0)
与AD7192的所有通信均必须以对通信寄存器的写操作开始。通信寄存器是一个 8 位只写寄存器,写入通信寄存器的数据决定下一个操作是读操作还是写操作,以及此操作发生在哪一个寄存器。
3.2 RS2 至 RS0 寄存器地址位
4 程序流程
使用硬件SPI:
1.初始化SPI接口对应的GPIO脚,MOSI、MISO和SCK配置为复用推挽输出模式,CS配置为推挽输出模式
2.配置SPI结构体,初始化硬件SPI
3.调用库函数编写SPI向寄存器写入多个字节、读取多个字节的函数(寄存器可达32位)
4.向 AD7192的DIN 输入连续写入40个1,实现软件复位AD7192
5.读取ID寄存器的值,判断通信接口是否正常通讯
6.执行内部零电平校准和内部满量程校准
7.根据数据手册寄存器表,按实际需求配置模式寄存器和配置寄存器
8.若配置为连续转换模式,直接等待DOUT为低电平即可读取数据寄存器,不用重复执行写入通信
寄存器读取数据寄存器
9.若使用多通道采集,ADC 按顺序采集各使能通道,建议将模式寄存器中的 DAT_STA 位设置为 1,状态寄存器值将会附加到转换结果上,以便用户能识别每次转换对应的通道
10.读取数据寄存器的值,按实际应用需求换算后打印,如用作采集电压,换算为电压值后打印查看 若使用软件SPI,所选的GPIO口需要模仿SPI接口的四根通信线,以拉高/拉低的动作来模拟SPI的硬件时序,完成起始信号、终止信号和交换字节等基本时序单元
5 例程
5.1连线
5.2 例程说明
5.2.1 初始化
例程初始化系统时钟,HAL 库初始化,LED 端口初始化,串口1初始化, 按键初始化
/* 系统时钟初始化成72MHz */SystemClock_Config();/* HAL 库初始化 */HAL_Init();/* LED 端口初始化 */LED_GPIO_Config();/* 配置串口1为:115200 8-N-1 */DEBUG_USART_Config();/* 按键初始化 */Key_GPIO_Config();
5.2.2 初始化 AD7192,模块软件复位
SPI_HandleTypeDef hspi_AD7192;/*** @brief AD7192初始化定义* @param * @retval */
void AD7192_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct;/* 使能SPI外设以及SPI引脚时钟 */AD7192_SPIx_CLK_ENABLE();AD7192_GPIO_CLK_ENABLE();AD7192_GPIO_CS_CLK_ENABLE();GPIO_InitStruct.Pin = AD7192_SCK_Pin;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(AD7192_SCK_GPIO_Port, &GPIO_InitStruct);GPIO_InitStruct.Pin = AD7192_MISO_Pin|AD7192_MOSI_Pin;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(AD7192_MISO_GPIO_Port, &GPIO_InitStruct);GPIO_InitStruct.Pin = AD7192_CS_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(AD7192_CS_GPIO_Port, &GPIO_InitStruct); /* SPI外设配置 */hspi_AD7192.Instance = AD7192_SPIx;hspi_AD7192.Init.Mode = SPI_MODE_MASTER;hspi_AD7192.Init.Direction = SPI_DIRECTION_2LINES;hspi_AD7192.Init.DataSize = SPI_DATASIZE_8BIT;hspi_AD7192.Init.CLKPolarity = SPI_POLARITY_HIGH;hspi_AD7192.Init.CLKPhase = SPI_PHASE_2EDGE;hspi_AD7192.Init.NSS = SPI_NSS_SOFT;hspi_AD7192.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;hspi_AD7192.Init.FirstBit = SPI_FIRSTBIT_MSB;hspi_AD7192.Init.TIMode = SPI_TIMODE_DISABLE;hspi_AD7192.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;hspi_AD7192.Init.CRCPolynomial = 7;HAL_SPI_Init(&hspi_AD7192);/* 片选使能 */AD7192_CS_ENABLE();
}
/*** @brief AD7192软件复位函数* @param * @retval */
void AD7192SoftwareReset(void) //AD7192软件复位函数;软件复位的原理,通过执行一个占用至少40个串行时钟周期的写操作,并式DIN处去高电平状态。
{/*分别定义一个读缓存和写缓存,再把写缓存赋值,然后通过调用void ADuC7026SpiOperation(unsigned char* WriteBuffer, unsigned char *ReadBuffer, unsigned char NumberOfByte)来执行,将写缓存数据传送至AD7192*/unsigned char WriteBuf[1]; //写缓存unsigned char ReadBuf[1]; //读缓存unsigned char i;WriteBuf[0] = 0xFF; for(i=0; i<10; i++){AD7192readdata(WriteBuf, ReadBuf, 1);//调用STM32F103SpiOperation()来实现数据传输,向AD7192的通信寄存器写40个1;0xFF是8个1,循环5次一共是40个1。}AD7192_SCLK_H;
}
/*** @brief AD7192读写函数* @param WriteBuffer :需要写入数据的缓冲区* @param ReadBuffer : 读出数据的缓冲区* @param NumberOfByte : 数据大小(字节)* @retval */
void AD7192readdata(unsigned char* WriteBuffer, unsigned char *ReadBuffer, unsigned char NumberOfByte)
{HAL_SPI_TransmitReceive(&hspi_AD7192, WriteBuffer, ReadBuffer, NumberOfByte, 1000);
}
5.2.3 使用 ReadFromAD7192ViaSPI 函数对模块连接状态检测
首先我们先讲解一下== ReadFromAD7192ViaSPI 函数==,这个函数的功能是读取 AD7192 内部寄存器的内容,我们可以看到它的第一个输入参数RegisterStartAddress 指的就是寄存器地址。 其他输入参数的意思在代码里已经注明里,在这里主要讲解一下这个函数的整体思路。
/*** @brief 通过SPI对AD7192执行一次读操作函数* @param RegisterStartAddress :要读寄存器的起始地址(取值范围是从0x00——0x07)* @param NumberOfRegistersToRead : 要读寄存器的个数* @param DataBuffer : 要读入的值* @param OffsetInBuffer : 缓存内偏移* @retval */
unsigned char ReadFromAD7192ViaSPI(const unsigned char RegisterStartAddress, const unsigned char NumberOfRegistersToRead, uint32_t *DataBuffer, const unsigned char OffsetInBuffer)
{
//形参包括要读寄存器的起始地址RegisterStartAddress(取值范围是从0x00——0x07),要读取寄存器的个数,指向将读取AD7192寄存器数据存入的数组的指针(DataBuffer才是要读入的值其他是中间变量),
//一般指向AD7192Registers[8],
//const unsigned char OffsetInBuffer,字面意思是缓存内偏移,是指AD7192Registers[8]数组内部偏移,注意是数组哦,之前我们说过AD7192Registers[8]之所以定义8个元素,
//一个寄存器对应AD7192Registers[8]数组的一个元素,互不干扰。unsigned char WriteBuf[4]={0,0,0,0}; //定义有4个元素的写缓存数组,每个数组元素占1个字节。unsigned char ReadBuf[4]={0,0,0,0}; //定义有4个元素的读缓存数组,每个数组元素占1个字节。unsigned char i;//Delay(0xFFFF);for(i=0; i < NumberOfRegistersToRead; i++){WriteBuf[0] = WEN|RW_R|((RegisterStartAddress + i)<<3)|CREAD_DIS; //写入通信寄存器;8位数据;下一个操作是对指定寄存器执行读操作。CREAD_DIS表示不使能连续读。//确定下一步进行寄存器读操作,那么写操作自然无效喽。AD7192readdata(WriteBuf, ReadBuf, 1);//首先通过写入通信寄存器来选定下一步要读取的寄存器//然后再将WriteBuf清空WriteBuf[0] = NOP;WriteBuf[1] = NOP;WriteBuf[2] = NOP;WriteBuf[3] = NOP; switch(RegisterStartAddress + i){case REG_ID : //ID寄存器(0x04,8位寄存器)case REG_COM_STA : //状态寄存器(0x00,8位寄存器)case REG_GPOCON : //通用数字输出控制寄存器(0x05,8位寄存器)AD7192readdata(WriteBuf, ReadBuf, 1); //此3种情况是读取一个字节DataBuffer[OffsetInBuffer + i ] = ReadBuf[0];break;case REG_MODE : //模式寄存器(0x01,24位)case REG_CONF : //配置寄存器(0x02,24位)case REG_OFFSET : //失调寄存器(0x06,24位)case REG_FS : //满量程寄存器(0x07,24位)AD7192readdata(WriteBuf, ReadBuf, 3); //此4种情况是读取3个字节DataBuffer[OffsetInBuffer + i ] = ReadBuf[0]; DataBuffer[OffsetInBuffer + i ] = (DataBuffer[OffsetInBuffer + i ]<<8) + ReadBuf[1];DataBuffer[OffsetInBuffer + i ] = (DataBuffer[OffsetInBuffer + i ]<<8) + ReadBuf[2];break;case REG_DATA : //数据寄存器(0x03,24位或32位) if (AD7192Registers[REG_MODE] & DAT_STA_EN) //多通道使能,将状态寄存器的内容附加到数据寄存器24位的数据上,所以是32位数据{AD7192readdata(WriteBuf, ReadBuf, 4); //所以此情况是读4个字节 DataBuffer[OffsetInBuffer + i ] = ReadBuf[0];DataBuffer[OffsetInBuffer + i ] = (DataBuffer[OffsetInBuffer + i ]<<8) + ReadBuf[1];DataBuffer[OffsetInBuffer + i ] = (DataBuffer[OffsetInBuffer + i ]<<8) + ReadBuf[2]; DataBuffer[OffsetInBuffer + i ] = (DataBuffer[OffsetInBuffer + i ]<<8) + ReadBuf[3];break;}else {AD7192readdata(WriteBuf, ReadBuf, 3); //do not transfer the status contents after read data register DataBuffer[OffsetInBuffer + i ] = ReadBuf[0];DataBuffer[OffsetInBuffer + i ] = (DataBuffer[OffsetInBuffer + i ]<<8) + ReadBuf[1];DataBuffer[OffsetInBuffer + i ] = (DataBuffer[OffsetInBuffer + i ]<<8) + ReadBuf[2];break;}default : break;}}return 0;
}
该函数首先定义两个读写缓存数组 WriteBuf 和 ReadBuf,它们都是4个字节32位的,根据AD7192数据手册等资料我们知道,AD7192 片内有一个8位的通信寄存器和其他8个长度不一的寄存器, 而读取这8个长度不一的寄存器当中的那个最长的寄存器需要32位也就是 4个字节的空间来保存读取到的数据,所以这两个数组的大小都是4个字节。
然后,写入通信寄存器,这是必要的步骤,因为与 AD7192 的所有通信都必须以对通信寄存器的写操作开始,写入通信寄存器的数据决定了下一个操作是对哪一个寄存器进行读操作还是写操作。 接着因为该函数是读寄存器数据,不需要写入数据,所以将 WriteBuf 数组的全部 4个字节都赋值为NOP,也就是0。在 switch 语句里面判断要读取的是哪一个寄存器, 因为这8个寄存器长度不一,所以实际上要根据寄存器长度分成三种情况:8 位寄存器(REG_ID、REG_COM_STA、REG_GPOCON)、24 位寄存器(REG_MODE、REG_CONF、REG_OFFSET、REG_FS)、 还有数据寄存器(REG_DATA),数据寄存器本是 24 位的寄存器,但在使用多通道的时候读取时会附加上状态寄存器的内容来指定是哪个通道的数据,所以读取时可能读取到的不止是24位的数据。 具体请参考数据手册里的相关内容。
5.2.4 根据MODE宏定义来选择模式进行运行
//在ad7192_test.h切换MODE宏定义来选择模式
#if MODE == SINGLE_CONVERSION/* 4路伪差分通道单次读实验 */single_conversion_voltage();
#elif MODE == SINGLE_CONTINUOUS_CONVERSION/* 4路伪差分通道连续读实验 */single_continuous_conversion_voltage();
#elif MODE == DIFFERENCE_CONVERSION/* 2路差分通道单次读实验 */difference_conversion_voltage();
#elif MODE == DIFFERENCE_CONTINUOUS_CONVERSION/* 2路差分通道连续读实验 */difference_continuous_conversion_voltage();
#elif MODE == ELECTRONIC_SCALE/* AD7192电子秤实验 */electronic_scale_test();
#endif
在2路差分通道连续读实验下,看 difference_continuous_conversion_voltage 这个函数其实非常简单。 它的功能是对2路差分通道:“AIN1_AIN2” 和 “AIN3_AIN4” 进行连续地AD转换,转换完成之后读取这两通道ADC转换结果并打印出来。 进入该函数里面首先会读取并打印8个 AD7192 片内寄存器的值,方便查看和调试,然后调用 ad7192_mode_cfg_reg 函数配置模式寄存器和配置寄存器, 进行内部校准,最后启动连续转换,开启连续读模式,在 while 循环里不断读取和打印两路差分通道的 ADC 电压值 。
/*** @brief 2路差分连续转换电压实验* @param 无* @retval 无*/
void difference_continuous_conversion_voltage(void)
{uint32_t ad_data = 0;float v = 0.0;uint32_t mode = 0, cfg = 0;printf("野火 AD9172 2路 差分连续转换读电压实验\r\n");/* 读 AD7192 寄存器 */ReadFromAD7192ViaSPI(REG_COM_STA, 8, AD7192Registers, REG_COM_STA);for(int i=0; i < 8; i++){printf("AD7192Register[%d] = 0x%06X \r\n", i+REG_COM_STA , AD7192Registers[i+REG_COM_STA]);}/* 单次转换|使能状态传输|外部时钟|sinc4滤波器|禁用奇偶校验|时钟不分频|禁用单周期转换|禁用60Hz陷波|输出数据速率 */mode = MODE_SING|DAT_STA_EN|EXT_XTAL|SINC_4|ENPAR_DIS|CLK_DIV_DIS|SINGLECYCLE_DIS|REJ60_DIS|FSPEED;cfg = CHOP_DIS|REF_IN1|AIN1_AIN2|BURN_DIS|REFDET_DIS|BUF_DIS|UB_BI|GAIN_1;/*禁用斩波|外部基准电压1|12差分通道(单)|禁用激励电流|禁用基准电压检测|禁用模拟输入缓冲|双极性模式|增益为1 */ad7192_mode_cfg_reg(mode, cfg); // 配置模式寄存器和配置寄存器/* 校准 */printf("内部校准中\r\n");AD7192InternalZeroScaleCalibration();AD7192InternalFullScaleCalibration(); printf("内部校准完成\r\n");AD7192StartContinuousConvertion(AIN1_AIN2|AIN3_AIN4); // 启动连续转换AD7192StartContinuousRead(); while(1){ad_data = AD7192ContinuousRead();switch(ad_data & 7){case 0:v = ((ad_data >> 8) / 8388608.0 - 1)*3.3;printf("AIN1_AIN2 = %fV\n", v);break;case 1:v = ((ad_data >> 8) / 8388608.0 - 1)*3.3;printf("AIN3_AIN4 = %fV\n", v);break;}// HAL_Delay(200);//解除注释,减慢打印速度}
}