
AD7799是早些前ADI公司推出的一款高精度低速24位ADC器件,主要应用于低功耗精密测量场合。最近开发与气压检测相关的产品,选择了这个芯片,经过PCB的合理布线,感觉这颗芯片的效果还不错。 AD7799内部数字部分和模拟部分的供电是分开的,数字部分由DVCC供电,模拟部分由AVCC供电,经过实验,在只有DVCC而不加AVCC的时候芯片的数字接口部分是可以工作的,这样就可以把AIN3+和AIN3-作为数字信号来启动模拟电源输出AVCC,不知道这样描述是否清楚,主要是为低功耗和省电考虑。 AD7799内部有三个差分通道,可以分别配置成为差分模式和单端模式,在单端模式下需要保证AINx(+)电压高于AIN(-)电压,否则转换结果为零,这很显然。差分模式下实际上的量化等级只有2的23次方,因为有一位做了符号位,在差分方式下应当注意24位值的符号位处理,应当将其扩展到第32位,做为一个字来处理。 芯片内部有一个增益可编程的放大器,可以设定增益为1/2//4/8/16/32/64/128倍增益。经本人实际使用其增益还比较精确,只是在高增益时实际测量的值偏差变大。由于分辨率太高,轻微的信号波动和引线布局都对转换结果影响较大,所以在使用前需要对通道进行零度和满度校准。 零度校准时,芯片内部将差分通道的两个输入端内部短接,这时得到一个转换值存放于内部对应通道的零度偏差寄存器中,满度校准时,芯片内部将两个输入端接到参考电压上,这时得到的转换值存放于内部相应通道的满度寄存器中。至于系统误差校准,这个没做研究。 利用STM32的SPI接口与相连接,非常完美,它的SPI不以CS线的上升沿做为结束同步标志,CS线仅仅只是做为片选使用,STM32可以工作于硬件CS管理模式。每个字节都可以有CS的复位、置位变化,也可以多个字节只有一次cS的复位、置位变化,很灵活,我还是采用了软件管理CS线的方式。 编程时,需要特别注意SPI的模式,它的特点(看AD7799的DS中给出的时序图)是SCLK在空闲时保持高电平,数据在SCLK半个周期之后送到MOSI线上,与一般器件的SPI时序有所不同,当然这也是标准SPI时序之后,只是一般器件不采用这种方式。以下是STM32单片机的SPI配置,我用到的是SPI2: 下面的代码是完全调试通过的: #define ADC_SPI_CS_CLR GPIOB->BSRR=GPIO_Pin_12 #define ADC_SPI_CS_SET GPIOB->BSRR=GPIO_Pin_12 #define ADC_RDY_DAT (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)) #define ADC_OP_READ ((u8)(0x40)) #define ADC_OP_WRITE ((u8)(0x00)) #define ADC_REG_STAT ((u8)(0x00<<3)) #define ADC_REG_MODE ((u8)(0x01<<3)) #define ADC_REG_CONFIG ((u8)(0x02<<3)) #define ADC_REG_DATA ((u8)(0x03<<3)) #define ADC_REG_ID ((u8)(0x04<<3)) #define ADC_REG_IO ((u8)(0x05<<3)) #define ADC_REG_OFFSET ((u8)(0x06<<3)) //#define ADC_REG_STAT ((unsigned char)(0x07)) #define ADC_CON_CH1 ((u8)(0x00)) #define ADC_CON_CH2 ((u8)(0x01)) #define ADC_CON_CH3 ((u8)(0x02)) #define ADC_CON_AVDD ((u8)(0x07)) #define ADC_CON_GAIN1 ((u8)(0x00)) #define ADC_CON_GAIN2 ((u8)(0x01)) #define ADC_CON_GAIN3 ((u8)(0x02)) #define ADC_CON_GAIN4 ((u8)(0x03)) #define ADC_CON_GAIN5 ((u8)(0x04)) #define ADC_CON_GAIN6 ((u8)(0x05)) #define ADC_CON_GAIN7 ((u8)(0x06)) #define ADC_CON_GAIN8 ((u8)(0x07)) #define ADC_SINGLE_POLAR ((u8)(1<<4)) //单双极性 #define ADC_DOUBLE_POLAR ((u8)(0<<4)) #define ADC_MODE_CONTINUOUS ((u8)(0<<5)) #define ADC_MODE_ONCE ((u8)(1<<5)) #define ad7799_StatRDY ((u8)(1<<7)) #define ad7799_StatERR ((u8)(1<<6)) #define ad7799_StatNOR ((u8)(1<<5)) void SPI_Config(void) { SPI_InitTypeDef SPI_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 |GPIO_Pin_14|GPIO_Pin_15; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOB, &GPIO_InitStructure); /* SPI2 configuration */ SPI_Cmd(SPI2, DISABLE); //必须先禁能,才能改变MODE SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //两线全双工 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主 SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8位 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //CPOL=1 时钟悬空高 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //CPHA=1 数据捕获第2个 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //软件NSS SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //2分频 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位在前 SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC7 SPI_Init(SPI2, &SPI_InitStructure); SPI_Cmd(SPI2, ENABLE); } u8 SPIByte(u8 byte) { /*等待发送寄存器空*/ while((SPI2->SR & SPI_I2S_FLAG_TXE)==RESET); /*发送一个字节*/ SPI2->DR = byte; /* 等待接收寄存器有效*/ while((SPI2->SR & SPI_I2S_FLAG_RXNE)==RESET); return(SPI2->DR); } /*--------------------------------------------------------- Func: AD7799读取寄存器数据 Time: 2012-3-29 Ver.: V1.0 Note: ---------------------------------------------------------*/ void AD7799_ReadReg(u8 RegAddr,u8 *Buffer,u8 Length) { u8 i; ADC_SPI_CS_CLR; RegAddr|=ADC_OP_READ; //ADC_WriteBytes(&RegAddr,1); //ADC_ReadBytes(Buffer,Length); SPIByte(RegAddr); for(i=0;i<Length;i++) { Buffer=SPIByte(Buffer); } ADC_SPI_CS_SET; } /*--------------------------------------------------------- Func: AD7799写入寄存器数据 Time: 2012-3-29 Ver.: V1.0 Note: ---------------------------------------------------------*/ void AD7799_WriteReg(u8 RegAddr,u8 *Buffer,u8 Length) { u8 i; ADC_SPI_CS_CLR; RegAddr|=ADC_OP_WRITE; //ADC_WriteBytes(&RegAddr,1); //ADC_WriteBytes(Buffer,Length); SPIByte(RegAddr); for(i=0;i<Length;i++) { SPIByte(Buffer); } ADC_SPI_CS_SET; } /*--------------------------------------------------------- Func: AD7799忙判断 Time: 2012-3-29 Ver.: V1.0 Note: 0/OK >0/ERROR,timeout ---------------------------------------------------------*/ u8 AD7799_WaitBusy() { u16 i; ADC_SPI_CS_CLR; i=0; while(ADC_RDY_DAT>0) { i++; if(i>5000) return 1; } ADC_SPI_CS_SET; return 0; } /*--------------------------------------------------------- Func: AD7799通道内部校准 Time: 2012-3-29 Ver.: V1.0 Note: 0/OK >0/Error ---------------------------------------------------------*/ u8 AD7799_Calibrate(u8 CHx,u8 Gain) { u8 R,Cmd[2]; Cmd[0]=0x30|Gain; //0x10代表配置寄存器高八位中单双极性位置1 Cmd[1]=0x30|CHx; AD7799_WriteReg(ADC_REG_CONFIG,Cmd,2); //设置配置寄存器 Cmd[0]=0x80; Cmd[1]=0x0F; AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行内部零度校准 R|=AD7799_WaitBusy(); //等待校准完成 Cmd[0]=0xA0; Cmd[1]=0x0F; AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行内部满肚校准 R|=AD7799_WaitBusy(); //等待校准完成 /* Cmd[0]=0xC0; Cmd[1]=0x0F; AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行系统零度校准 R|=AD7799_WaitBusy(); //等待校准完成 Cmd[0]=0xE0; Cmd[1]=0x0F; AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行系统满度校准 R|=AD7799_WaitBusy(); //等待校准完成 */ return R; } u8 AD7799_Read_STAT(void) { u8 Cmd[2]; AD7799_ReadReg(ADC_REG_STAT,Cmd,1); return Cmd[0]; } u16 AD7799_Read_CONFIH(void) { u8 Cmd[2]; AD7799_ReadReg(ADC_REG_CONFIG,Cmd,2); return (Cmd[0]<<8)+Cmd[1]; } /*--------------------------------------------------------- Func: AD7799复位 Time: 2012-3-29 Ver.: V1.0 Note: 0/OK >0/Error ---------------------------------------------------------*/ void AD7799_Reset() { // u8 Cmd[4]={0xFF,0xFF,0xFF,0xFF}; ADC_SPI_CS_CLR; // ADC_WriteBytes(Cmd,4); SPIByte(0xFF); SPIByte(0xFF); SPIByte(0xFF); SPIByte(0xFF); ADC_SPI_CS_SET; } /*--------------------------------------------------------- Func: AD7799初始化 Time: 2012-3-29 Ver.: V1.0 Note: 0/OK >0/Error ---------------------------------------------------------*/ u8 AD7799_Init(u8 Gain) { u8 ID,Cmd[2]; AD7799_Reset(); delay_ms(5); // AD7799_ReadReg(ADC_REG_ID,&ID,1); //读取器件ID // if((ID==0xFF)||(ID==0x00))return 1; AD7799_Calibrate(ADC_CON_CH1,Gain); //通道1校准 AD7799_Calibrate(ADC_CON_CH2,Gain); //通道2校准 //AD7799_Calibrate(ADC_CON_CH3,Gain); //通道3校准 // Cmd[0]=0<<6; // AD7799_WriteReg(ADC_REG_IO,Cmd,1); return 0; } /*--------------------------------------------------------- Func: AD7799开始转换 Time: 2012-3-29 Ver.: V1.0 Note: ---------------------------------------------------------*/ void AD7799_Start(u8 CovChx,u8 CovGain,u8 CovRate,u8 CovMode) { u8 Cmd[2]; Cmd[0]=0x30|CovGain; Cmd[1]=0x30|CovChx; //0x30 AD7799_WriteReg(ADC_REG_CONFIG,Cmd,2); Cmd[0]=0x10|CovMode; Cmd[1]=CovRate; AD7799_WriteReg(ADC_REG_MODE,Cmd,2); } /*--------------------------------------------------------- Func: AD7799读取转换结果 Time: 2012-3-29 Ver.: V1.0 Note: ---------------------------------------------------------*/ u32 AD7799_Read() { u8 Cmd[4]; u16 i=0; u32 D; Cmd[0]=0; while((AD7799_Read_STAT()&ad7799_StatRDY)!=0) { i++; if(i>10000) break; } if((AD7799_Read_STAT()&ad7799_StatERR)==0) { AD7799_ReadReg(ADC_REG_DATA,Cmd,3); D=(Cmd[0]<<16)+(Cmd[1]<<8)+Cmd[2]; return D; } else { AD7799_ReadReg(ADC_REG_DATA,Cmd,4); return 0xFFFFFF; } } void ADt7799test() { u32 Val; u16 a; AD7799_Init(ADC_CON_GAIN1); AD7799_Start(ADC_CON_CH1,ADC_CON_GAIN1,0x01,ADC_MODE_CONTINUOUS); AD7799_Read(); AD7799_Read(); AD7799_Read(); AD7799_Read(); AD7799_Read(); AD7799_Read(); } 下面说一下自己遇到的问题: 一:增益1和2起作用,4-128不起作用,增益1与2不通过内部放大器,4-128时通过内部放大器,问题出在电路设计上: 当使用IN-AMP时,AIN+与AIN-的工作范围为(GND+300mV)~(AVDD-1.1V); 共模电压>0.5V是指[(AIN+)+(AIN-)]/2>0.5V即可 如果你的AIN-一定要接GND,那一定是无法使用In-Amp,只能使用unbuffer模式。 我们输入用的单端模式0-5v,所以无法使用4-128增益 二:编程中用到的管教 CS、SCLK、DIN、DOUT(注意时钟频率的选择,查看片选是否生效,输入输出高低电平是否正确) 三:三路差分输入,第三路能设置成IO口,如果不用最好设成模拟输入 四:根据REFIN(+)和REFIN(-)配置寄存器单双极性选择 |