
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: 5 U: Z4 }# t, _下面的代码是完全调试通过的: ! c2 l# ? v- c6 b# {#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)); }( V7 X) `' Z6 O3 X! j #define ADC_REG_CONFIG ((u8)(0x02<<3))) U9 T+ }" {( v5 U: g% V #define ADC_REG_DATA ((u8)(0x03<<3))( h r4 B! F1 g9 L #define ADC_REG_ID ((u8)(0x04<<3))' ^4 L4 I* ^$ |6 D8 I #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))$ R% F8 d, ~, O7 r" l #define ADC_CON_CH3 ((u8)(0x02))# H8 h/ v! i' ]3 p2 V #define ADC_CON_AVDD ((u8)(0x07)) #define ADC_CON_GAIN1 ((u8)(0x00)) #define ADC_CON_GAIN2 ((u8)(0x01))% u6 L' s' i# u #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)) $ E% f- s$ n- i #define ad7799_StatRDY ((u8)(1<<7)) #define ad7799_StatERR ((u8)(1<<6))5 \+ W8 t7 j3 J) H #define ad7799_StatNOR ((u8)(1<<5)) void SPI_Config(void) {2 ?2 P# Y2 R& r0 z SPI_InitTypeDef SPI_InitStructure;' s3 v M- a5 P$ L' n8 Q$ f g1 O$ h GPIO_InitTypeDef GPIO_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);# v3 Y7 P; [* e9 ?5 R$ Q RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);' l; G' { g: O + G9 ?8 ~8 p' V GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;% ?0 @$ L' f. B$ ? GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;( M$ Z' F) n% m% h9 R' r+ O0 v 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;! b' h* Y% ]- Z7 _/ O# {/ a GPIO_Init(GPIOB, &GPIO_InitStructure); /* SPI2 configuration */7 z( Z1 z0 Y$ F1 @+ {) z- w" W SPI_Cmd(SPI2, DISABLE); //必须先禁能,才能改变MODE: R3 b1 O; G! o, N5 q) K5 K SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //两线全双工: H+ ?0 Q* b5 `% t' Q7 K SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主1 Y- X- B5 i% o6 l0 ^6 H1 }- G0 B SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8位 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //CPOL=1 时钟悬空高( r; e5 t2 O- p9 w6 } SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //CPHA=1 数据捕获第2个( s2 E; ~& |& |+ {: g* n SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //软件NSS SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //2分频 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位在前' j; m p# k& E, ^# o' F SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC7 SPI_Init(SPI2, &SPI_InitStructure);) J% L' |; Q: w5 @+ ~, l SPI_Cmd(SPI2, ENABLE); } $ f; w6 s. K7 F: |" q( g u8 SPIByte(u8 byte) { /*等待发送寄存器空*/. W8 \0 j4 S, U/ l4 e; X while((SPI2->SR & SPI_I2S_FLAG_TXE)==RESET);4 f2 V7 k0 g6 K2 ?; z /*发送一个字节*/ SPI2->DR = byte; /* 等待接收寄存器有效*/' g2 B$ }/ `8 Q0 `5 M2 J5 o while((SPI2->SR & SPI_I2S_FLAG_RXNE)==RESET); return(SPI2->DR); }6 C3 |7 J. c; ~) t /*--------------------------------------------------------- Func: AD7799读取寄存器数据 Time: 2012-3-29 Ver.: V1.0: D, S+ {- v0 E* \. x4 _& |5 z: C; z Note: ---------------------------------------------------------*/' w7 Q9 E- g3 x void AD7799_ReadReg(u8 RegAddr,u8 *Buffer,u8 Length)& O" `# I& c4 V2 _( J* ] {; x% h: u6 M5 v, k, x+ |0 z u8 i;( x0 T% u; U3 z5 C& N ADC_SPI_CS_CLR;" y; z' D! w& I9 O1 m3 D RegAddr|=ADC_OP_READ; //ADC_WriteBytes(&RegAddr,1); //ADC_ReadBytes(Buffer,Length);9 e6 c- w+ J( u( B/ v SPIByte(RegAddr); H% s( W% e: |" x for(i=0;i<Length;i++) {5 w* @) w/ A# |( F Buffer=SPIByte(Buffer); } ADC_SPI_CS_SET; }$ w( h1 v' u+ x% t) J 7 U& Z7 ?* s2 ~* ^3 |- } /*---------------------------------------------------------* U/ a( Q k. f: i! K; d+ Q* V$ J Func: AD7799写入寄存器数据* _7 M* U# X: f; X: {( [; ?* j Time: 2012-3-29 Ver.: V1.03 @' G2 i3 b/ q4 | Note:6 i# ]( B: `; I' ]1 S ---------------------------------------------------------*/' g' d0 p5 _7 \7 A5 ^* y7 Q void AD7799_WriteReg(u8 RegAddr,u8 *Buffer,u8 Length) { u8 i; ADC_SPI_CS_CLR; RegAddr|=ADC_OP_WRITE;5 o6 X. |* w# `$ ~; y; j# L //ADC_WriteBytes(&RegAddr,1); //ADC_WriteBytes(Buffer,Length); SPIByte(RegAddr); for(i=0;i<Length;i++)3 [4 B9 L! ^1 C+ e2 f) d { SPIByte(Buffer);0 k8 F4 x7 i/ v' X# v ]9 M }/ k, A5 y8 S7 o' k( f p4 i ADC_SPI_CS_SET;( J- g1 Y0 q' i4 `1 F } /*--------------------------------------------------------- Func: AD7799忙判断* J- A! q- F) ]8 E0 [ Time: 2012-3-29! b. T1 G+ ?2 E4 a0 W" J+ r Ver.: V1.0% K% O5 o: O' y$ A# B6 _ Note: 0/OK >0/ERROR,timeout6 y/ c1 O2 w R* `1 u8 _8 f ---------------------------------------------------------*/8 i# P2 z# M7 w# u u8 AD7799_WaitBusy() {7 F" ?9 _. `8 z# {& [1 e# i u16 i;1 ?& P, i+ x; { ADC_SPI_CS_CLR;9 P) e/ N: B8 v+ ] i=0;6 v3 x. |# f! B while(ADC_RDY_DAT>0) { i++; if(i>5000)" z$ ?5 \9 k& l1 G return 1;& e8 F4 L3 B ?6 d" G2 h: l } ADC_SPI_CS_SET; return 0; } /*---------------------------------------------------------& I" ^1 R' I" a! x$ W Func: AD7799通道内部校准 Time: 2012-3-29* l4 t; B& F8 r Ver.: V1.0% ^. G" G; D* a; `- z# T Note: 0/OK >0/Error ---------------------------------------------------------*/ u8 AD7799_Calibrate(u8 CHx,u8 Gain)) U1 v& |3 H: G' L7 F {% t; _4 @) B# R7 `5 I# f8 M u8 R,Cmd[2];( r3 e T% m, c6 k2 ] Cmd[0]=0x30|Gain; //0x10代表配置寄存器高八位中单双极性位置1 Cmd[1]=0x30|CHx; AD7799_WriteReg(ADC_REG_CONFIG,Cmd,2); //设置配置寄存器: F7 z6 z0 p- q. ^ Cmd[0]=0x80; Cmd[1]=0x0F;* ^& I9 t! X1 G* q0 t: Z AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行内部零度校准 R|=AD7799_WaitBusy(); //等待校准完成$ G' S: X- Z- N Q- L6 } Cmd[0]=0xA0;, M7 H7 ?/ O% h% ?# f; ]7 a Cmd[1]=0x0F; AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行内部满肚校准 R|=AD7799_WaitBusy(); //等待校准完成 /* Cmd[0]=0xC0;' C3 g" S u7 I2 R* N8 y. f 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); //进行系统满度校准' b1 u0 S2 P, R8 O R|=AD7799_WaitBusy(); //等待校准完成 */ return R;# Y+ u; ]" H8 o } u8 AD7799_Read_STAT(void) { u8 Cmd[2]; AD7799_ReadReg(ADC_REG_STAT,Cmd,1);5 m/ N% U y( A- f return Cmd[0];0 u5 V% q" y/ q) C) T } u16 AD7799_Read_CONFIH(void) { u8 Cmd[2]; AD7799_ReadReg(ADC_REG_CONFIG,Cmd,2); return (Cmd[0]<<8)+Cmd[1];; \# u% Q! F# b' Z* C& | } /*---------------------------------------------------------+ K6 b! k6 H" Y7 @( B Func: AD7799复位0 K% i! J2 E! J3 @5 U Time: 2012-3-298 ?; ~! M1 i. E' x Ver.: V1.01 G. q3 q7 H, h9 q Note: 0/OK >0/Error ---------------------------------------------------------*/# F9 ^: K" i/ {1 H; U void AD7799_Reset()! ^* n9 w: U* b O { // u8 Cmd[4]={0xFF,0xFF,0xFF,0xFF}; ADC_SPI_CS_CLR; // ADC_WriteBytes(Cmd,4); SPIByte(0xFF);. Q. y7 Z+ J6 ]; R% k SPIByte(0xFF);+ L8 y5 g" D' c4 k ]4 D SPIByte(0xFF); SPIByte(0xFF); ADC_SPI_CS_SET;( r( Z3 I) h5 h# s }0 R# Y' B- g# d* a9 V- ~ /*---------------------------------------------------------3 W/ G8 D) O# H+ E. { Func: AD7799初始化: M, F/ z! |+ _7 | Time: 2012-3-29 Ver.: V1.0% p5 M4 U& g. T. D0 z% K Note: 0/OK >0/Error ---------------------------------------------------------*/% y& W# u& s9 P u8 AD7799_Init(u8 Gain) {( z+ Z* |0 |( v4 H- Q! o/ H u8 ID,Cmd[2];- W1 s6 l+ G; o/ t2 S, [+ a& f AD7799_Reset();6 h. d7 }, l( V4 P: V, G5 j 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;* }2 }8 q+ G8 L0 Y // AD7799_WriteReg(ADC_REG_IO,Cmd,1); return 0; } /*--------------------------------------------------------- Func: AD7799开始转换- P2 }) Q& Y* d- ?5 `8 A1 u, G Time: 2012-3-29- r9 J1 e" M3 n- T; H* S# r Ver.: V1.0 Note:' D5 X* g) L! S ---------------------------------------------------------*/ void AD7799_Start(u8 CovChx,u8 CovGain,u8 CovRate,u8 CovMode) {% D+ j' J# Y7 [ u8 Cmd[2];9 x0 ^' p( y: F e Cmd[0]=0x30|CovGain; Cmd[1]=0x30|CovChx; //0x30& ]4 E0 t$ B! T! w8 z. x+ u AD7799_WriteReg(ADC_REG_CONFIG,Cmd,2); Cmd[0]=0x10|CovMode; Cmd[1]=CovRate; AD7799_WriteReg(ADC_REG_MODE,Cmd,2);, z8 z9 f+ G! p, ]$ Z } /*--------------------------------------------------------- Func: AD7799读取转换结果0 ]; ?, k! c% x2 P# R- U: V6 A2 A2 | Time: 2012-3-29 Ver.: V1.0+ j4 h) p8 t: \: u" ~8 ] Note: ---------------------------------------------------------*/ u32 AD7799_Read() {( C; ^' D9 _% e! E u8 Cmd[4]; u16 i=0;1 h+ F' }! I* Z) O e1 x/ Y u32 D; Cmd[0]=0; while((AD7799_Read_STAT()&ad7799_StatRDY)!=0) {2 Y% z5 O3 m8 P$ A) V i++;$ V4 ~; G5 b; M' q if(i>10000) break;* Z+ t3 i0 Q) ]: q }9 c5 Y% w* E6 q# D9 ^7 t; ^- Z if((AD7799_Read_STAT()&ad7799_StatERR)==0)+ B% f5 }0 e, Z% n! n/ `* f { AD7799_ReadReg(ADC_REG_DATA,Cmd,3);5 k0 U: `* u4 Q) z D=(Cmd[0]<<16)+(Cmd[1]<<8)+Cmd[2]; `) a9 \) I0 M( K2 @* k; ` return D; }* `" q; d. `, x9 T' H4 y else7 j9 v u v' h4 a r& Q# _ B { AD7799_ReadReg(ADC_REG_DATA,Cmd,4);2 O! b- g ~' A return 0xFFFFFF; }' C+ _8 o( ^; n. E8 c% T: p3 I- ]" v) k } void ADt7799test()# D9 M* ~7 M: d/ b& C {+ M( O9 j. B( p$ s" ~! Z0 z+ ~. t u32 Val; u16 a; AD7799_Init(ADC_CON_GAIN1);9 n! n- j E- e: H" N A AD7799_Start(ADC_CON_CH1,ADC_CON_GAIN1,0x01,ADC_MODE_CONTINUOUS); & l' o2 o" \* d4 E: f AD7799_Read();4 O' w- @- r* s0 \5 k AD7799_Read();+ u2 x; r1 ~! G AD7799_Read();9 [3 u4 M' t( l" B AD7799_Read();( G$ @/ R# c* m% W7 n0 H! E 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(-)配置寄存器单双极性选择 |