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: $ }8 x$ G$ [* ?$ s6 @4 K 下面的代码是完全调试通过的: #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))7 }5 p" O5 t9 D6 l1 Y" A5 z% a7 p #define ADC_CON_CH3 ((u8)(0x02))4 f$ d2 z3 B& V: b7 Z* Q k' a: W* y+ s #define ADC_CON_AVDD ((u8)(0x07)) #define ADC_CON_GAIN1 ((u8)(0x00)) #define ADC_CON_GAIN2 ((u8)(0x01))& s; F+ z U' g% b( ] l #define ADC_CON_GAIN3 ((u8)(0x02)) #define ADC_CON_GAIN4 ((u8)(0x03))( u8 Z2 o7 ]$ U3 d6 I! [+ S #define ADC_CON_GAIN5 ((u8)(0x04)) #define ADC_CON_GAIN6 ((u8)(0x05)) #define ADC_CON_GAIN7 ((u8)(0x06))! V, s8 q$ i3 d6 B. r& \ W #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))6 T V* S: O9 ], h #define ADC_MODE_ONCE ((u8)(1<<5)) - ^$ R- p @6 k) Y7 }( k7 J #define ad7799_StatRDY ((u8)(1<<7))( j+ B: O) G1 Z( B2 J# y* ` c #define ad7799_StatERR ((u8)(1<<6))7 N, @* n; o' `. A4 m/ ^ #define ad7799_StatNOR ((u8)(1<<5)) void SPI_Config(void) { SPI_InitTypeDef SPI_InitStructure;4 c1 t" W( K5 f" F( [ GPIO_InitTypeDef GPIO_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); I- m4 w: g6 d- B% Q 9 a8 F1 m. M! W Q GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;6 k" u* q) G! u. u GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOB, &GPIO_InitStructure); 8 h" f/ F8 K4 k1 t GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 |GPIO_Pin_14|GPIO_Pin_15;2 u! T) k9 N3 `6 D/ z$ L1 p GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;0 u9 v! s) A' N( R GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;3 \* ?+ m2 A4 h, [$ z GPIO_Init(GPIOB, &GPIO_InitStructure); /* SPI2 configuration */ SPI_Cmd(SPI2, DISABLE); //必须先禁能,才能改变MODE SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //两线全双工) d% [: y, w" v# `( \ SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主$ L, X9 d9 [; g SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8位 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //CPOL=1 时钟悬空高! U4 a- E3 H) u/ H" O6 L SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //CPHA=1 数据捕获第2个- i ?3 H. u* h0 [( @* ]0 p SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //软件NSS O& ?: ^7 H9 d3 L9 G SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //2分频: S- B3 H; Y# V5 Z7 `" ?2 l SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位在前. c2 Y! z/ U: {. c) g+ T# Y SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC7( F& s4 ~$ m/ D1 y7 W; H SPI_Init(SPI2, &SPI_InitStructure); SPI_Cmd(SPI2, ENABLE); } u8 SPIByte(u8 byte)+ ?! C9 W6 ]! T' h0 c( B& z; k( L { `! P* z3 q( ?1 |7 Q1 I# e9 I /*等待发送寄存器空*/* g5 J2 J- M W2 {& a; p while((SPI2->SR & SPI_I2S_FLAG_TXE)==RESET);6 {% C- G3 l1 @) w$ g$ p3 t P' P /*发送一个字节*/: W1 x, d l% n- A SPI2->DR = byte;9 ?1 J# V: J+ d% M% ]) H: Z/ h$ m, }$ \ /* 等待接收寄存器有效*/ while((SPI2->SR & SPI_I2S_FLAG_RXNE)==RESET); return(SPI2->DR);& t! m; x k ?# G9 q } /*--------------------------------------------------------- Func: AD7799读取寄存器数据 Time: 2012-3-296 V! K3 d% ]$ D5 p Ver.: V1.0 Note: ---------------------------------------------------------*/ void AD7799_ReadReg(u8 RegAddr,u8 *Buffer,u8 Length)3 P6 K$ i2 L( z, w- {% {1 @ {. ^$ S- i/ t O# Q u8 i;6 M! l7 p3 v! F5 p s* k* \8 B ADC_SPI_CS_CLR; RegAddr|=ADC_OP_READ;! ]: V+ v! J' Z% H _ //ADC_WriteBytes(&RegAddr,1); //ADC_ReadBytes(Buffer,Length);6 v& \. n R" F$ w SPIByte(RegAddr); for(i=0;i<Length;i++) { Buffer=SPIByte(Buffer);( ~8 j; j, E! y( q0 L. B } ADC_SPI_CS_SET; }- J4 U3 l8 w% ]- J9 E /*--------------------------------------------------------- Func: AD7799写入寄存器数据- r8 z( O3 C9 w# o* Y Time: 2012-3-29 Ver.: V1.0, I. f5 F) b5 h; c! B Note: ---------------------------------------------------------*/! I6 x8 n8 ^" r, } void AD7799_WriteReg(u8 RegAddr,u8 *Buffer,u8 Length)7 j- n+ w( ]$ c { u8 i; ADC_SPI_CS_CLR; RegAddr|=ADC_OP_WRITE;' s3 m# w/ n8 b' L/ } //ADC_WriteBytes(&RegAddr,1); //ADC_WriteBytes(Buffer,Length);3 O4 y. {( x; u( K% O SPIByte(RegAddr);& S2 _- _4 A0 z/ o6 o for(i=0;i<Length;i++) {/ p& g9 M! T- W+ Y! U SPIByte(Buffer); }! k! Z7 A( y/ ]$ t% F+ O ADC_SPI_CS_SET;6 E0 m8 u& j# X | } /*---------------------------------------------------------1 P0 X, e, M& w) L Func: AD7799忙判断 Time: 2012-3-29' n s A; O3 l Ver.: V1.0 Note: 0/OK >0/ERROR,timeout" {# c0 v. {2 i9 b ---------------------------------------------------------*/; ?5 l9 e: ?3 g6 ^: O) ] u8 AD7799_WaitBusy()9 W, G2 w) i8 v) y% ^( X' | { u16 i;; Z1 ~7 c; F; D$ e6 K, F! J& ]0 t @ ADC_SPI_CS_CLR;& ?7 r7 A8 [8 q. V/ { i=0;, `' E5 j1 K) d( H. Y9 B" O1 W while(ADC_RDY_DAT>0) {& x" s" y& Y N; Y" Y6 V% I i++;, C! I+ S& f2 `) D4 R2 b7 J- h if(i>5000)3 F' J; d6 U; Z% t5 y8 V' E# D return 1;5 D% E* \; e9 @5 I* p+ ^8 L2 ?0 b } ADC_SPI_CS_SET; return 0; } /*---------------------------------------------------------! m0 O% c% P5 `7 x Func: AD7799通道内部校准2 p/ i0 u0 \/ Q Time: 2012-3-29 Ver.: V1.0 Note: 0/OK >0/Error- S2 b0 w) e* }% ]4 ` ---------------------------------------------------------*/+ c6 l6 z8 `/ {" a) L1 y# L: ^ u8 AD7799_Calibrate(u8 CHx,u8 Gain) {' L# e" m: s( a5 ?! z% _: s9 j u8 R,Cmd[2]; Cmd[0]=0x30|Gain; //0x10代表配置寄存器高八位中单双极性位置1 Cmd[1]=0x30|CHx;8 |) s; F; v5 a+ v9 f2 k; c AD7799_WriteReg(ADC_REG_CONFIG,Cmd,2); //设置配置寄存器 Cmd[0]=0x80; Cmd[1]=0x0F; AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行内部零度校准( a- f1 C# q9 n- i0 L( g) e# F R|=AD7799_WaitBusy(); //等待校准完成3 E; G$ W, Q" M) d4 Z6 c Cmd[0]=0xA0; Cmd[1]=0x0F;1 J Z+ S( U5 H8 F3 L6 j8 x AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行内部满肚校准, o" t' l" ^; J- A& C/ n1 z 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); //进行系统满度校准2 @8 R, R% o+ U R|=AD7799_WaitBusy(); //等待校准完成 */ return R; }" y3 S, ~1 \7 a- L# @+ k: e u8 AD7799_Read_STAT(void) { u8 Cmd[2];% h8 m+ n6 C& N- Y AD7799_ReadReg(ADC_REG_STAT,Cmd,1);+ I) T0 q* ^5 K! T! d2 S a) e% V return Cmd[0];; q, d1 M! z9 E! I# k } u16 AD7799_Read_CONFIH(void)" S% K& t: c- ~% W# a { u8 Cmd[2]; AD7799_ReadReg(ADC_REG_CONFIG,Cmd,2); return (Cmd[0]<<8)+Cmd[1]; } /*--------------------------------------------------------- Func: AD7799复位! U# F( ~" _+ Q& d1 `1 [) A( c Time: 2012-3-298 E$ k2 J/ q" \ Ver.: V1.0 Note: 0/OK >0/Error ---------------------------------------------------------*/8 C% E1 u- \1 r& o8 ?+ \ void AD7799_Reset()/ x6 R: b4 c6 M9 ^. n, ~ { // u8 Cmd[4]={0xFF,0xFF,0xFF,0xFF}; ADC_SPI_CS_CLR; // ADC_WriteBytes(Cmd,4); SPIByte(0xFF);2 _3 v. C# X c+ ~! `8 n, H, w- _2 L SPIByte(0xFF); SPIByte(0xFF);0 o) M, h3 E) w SPIByte(0xFF); ADC_SPI_CS_SET; } /*---------------------------------------------------------4 s2 q: P* R7 p# S Func: AD7799初始化 Time: 2012-3-29 Ver.: V1.00 ]6 e. L0 U- X9 r# \' X. `! a0 A 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校准: c0 N# z0 q# [ t 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);' r1 z' L$ k2 q* [' R7 x2 N4 H" X return 0; } /*--------------------------------------------------------- Func: AD7799开始转换+ Y! H0 Q3 S/ |, {! O0 c Time: 2012-3-29 Ver.: V1.0& k t+ H5 K# p1 C Note: ---------------------------------------------------------*/3 x. m t% ~& ]- c void AD7799_Start(u8 CovChx,u8 CovGain,u8 CovRate,u8 CovMode) { u8 Cmd[2];8 ^7 ~% d6 ]+ M# h: s Cmd[0]=0x30|CovGain;4 [) X o l& c* W* k7 M8 p Cmd[1]=0x30|CovChx; //0x30$ x9 N- J: @* i9 P AD7799_WriteReg(ADC_REG_CONFIG,Cmd,2);( m7 h0 e) k& g4 ]' f J1 y$ ? Cmd[0]=0x10|CovMode;" y+ r6 \4 P; X( M- I1 u. M Cmd[1]=CovRate;0 Y+ t8 c L- F" t" F% Q* ^ AD7799_WriteReg(ADC_REG_MODE,Cmd,2);5 `& z) X) [% \( A. {: j- o+ m } /*---------------------------------------------------------4 n+ I8 |3 A8 ` Func: AD7799读取转换结果 Time: 2012-3-29 Ver.: V1.0 Note: ---------------------------------------------------------*/ u32 AD7799_Read()$ ^- Q7 A$ H6 ~% ^6 i# h { u8 Cmd[4];" u8 E7 I4 w& q- l9 m3 D0 c0 a! N u16 i=0; u32 D; Cmd[0]=0; while((AD7799_Read_STAT()&ad7799_StatRDY)!=0)& T v' H% c0 H9 y' x( R3 d" D { i++; if(i>10000)$ a. h$ ?$ Q- Y/ h& b! } break;! G9 u" }& Z& U! c7 F/ x* t } if((AD7799_Read_STAT()&ad7799_StatERR)==0) { AD7799_ReadReg(ADC_REG_DATA,Cmd,3);/ ]" {# d" C) F0 z D=(Cmd[0]<<16)+(Cmd[1]<<8)+Cmd[2];+ k: s9 q- T, j5 p return D;4 Y1 M. i, K9 J6 I Y } else5 G% i& D* _4 x& K; @ { AD7799_ReadReg(ADC_REG_DATA,Cmd,4);0 `" _/ Q v( I: p return 0xFFFFFF; } } void ADt7799test()+ i& s5 i! U5 |) B {# e% S' o$ `5 i" g4 m4 O u32 Val; u16 a;( h! e: R4 j" r* s AD7799_Init(ADC_CON_GAIN1); AD7799_Start(ADC_CON_CH1,ADC_CON_GAIN1,0x01,ADC_MODE_CONTINUOUS);5 s9 u6 u; Q$ |5 t2 m: l 5 [/ P+ q& |5 J) d7 @ AD7799_Read();* j( {/ i; H8 w AD7799_Read();2 r6 N$ H1 K- s& x9 K- N AD7799_Read(); AD7799_Read(); AD7799_Read();8 _% |1 f. _2 a, o4 L7 } 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(-)配置寄存器单双极性选择 $ h' b) s1 B/ {- B' F' X' a" U |
STM32固件库分享,超全系列整理
STM32G030F6P6基于HAL库模拟SPI驱动1.8寸TFT LCD屏幕
STM32的CAN FD位定时设置注意事项
基于STM32将移植 SBSFU 到 STM32G070过程分享
基于STM32G030 RAM不够用经验分享
STM32G070在OLED上移植U8G2单色GUI
【经验分享】STM32 IAP+Ymodem功能实现(参考官方代码)
【经验分享】STM32的SPI问题
【经验分享】STM32 的加密实现
STM32G070—使用platformio+arduino