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: " e. p2 ^- s. H$ ?7 i7 B3 \下面的代码是完全调试通过的: #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))' q: {! c! H& [ #define ADC_OP_WRITE ((u8)(0x00)) * Z4 o4 r0 s" _9 L #define ADC_REG_STAT ((u8)(0x00<<3)) #define ADC_REG_MODE ((u8)(0x01<<3)): s- V. c, ^; v6 D- @ #define ADC_REG_CONFIG ((u8)(0x02<<3)) #define ADC_REG_DATA ((u8)(0x03<<3)) #define ADC_REG_ID ((u8)(0x04<<3)): h* X5 D& w! H' K) j" t0 q #define ADC_REG_IO ((u8)(0x05<<3)) #define ADC_REG_OFFSET ((u8)(0x06<<3))3 X2 f. s5 p) F! v //#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)). m9 ~/ |4 x5 p- W' b #define ADC_CON_GAIN3 ((u8)(0x02)) #define ADC_CON_GAIN4 ((u8)(0x03))4 I h/ H6 z; q #define ADC_CON_GAIN5 ((u8)(0x04)): ~4 Q- G( o t$ h #define ADC_CON_GAIN6 ((u8)(0x05))/ {1 N0 T. K6 T0 M9 y #define ADC_CON_GAIN7 ((u8)(0x06))) p+ p. F l6 d! z9 }) s #define ADC_CON_GAIN8 ((u8)(0x07)) #define ADC_SINGLE_POLAR ((u8)(1<<4)) //单双极性, d- K3 C+ U" M( m4 d7 g7 G #define ADC_DOUBLE_POLAR ((u8)(0<<4)) #define ADC_MODE_CONTINUOUS ((u8)(0<<5))+ h5 J' o0 v7 _* |# _5 i! p; H #define ADC_MODE_ONCE ((u8)(1<<5)) + ?/ b0 \" Y8 e #define ad7799_StatRDY ((u8)(1<<7))$ _( B9 {, F! f l0 o* V #define ad7799_StatERR ((u8)(1<<6)) #define ad7799_StatNOR ((u8)(1<<5)) void SPI_Config(void) { SPI_InitTypeDef SPI_InitStructure;( | L$ p6 i& K3 a: w7 y& W GPIO_InitTypeDef GPIO_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); % e' e2 J" G, N: B5 X* |7 U7 a 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); 5 m q. {1 A9 x. @1 u# M GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 |GPIO_Pin_14|GPIO_Pin_15;+ y+ F/ I& f+ ?+ w5 b! ~ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;- v: L; U. U3 N d GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;7 S2 b/ B' y' w" Z) f1 ~: P) n GPIO_Init(GPIOB, &GPIO_InitStructure); /* SPI2 configuration */# }2 @( u4 z+ c7 A* e( { SPI_Cmd(SPI2, DISABLE); //必须先禁能,才能改变MODE SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //两线全双工 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主6 h4 B& y/ w) d SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8位! F1 f" B+ E9 p: ? 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; //软件NSS2 l7 D& D$ X ]* N' u$ | SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //2分频 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位在前 SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC78 ]; X$ u/ @1 A( e6 t* V SPI_Init(SPI2, &SPI_InitStructure); SPI_Cmd(SPI2, ENABLE); ; y& A+ h9 F' |9 L0 S } u8 SPIByte(u8 byte)$ A3 Q: _7 h. R6 J$ u& ? { H+ Q: R5 W5 a# |9 j /*等待发送寄存器空*/' u+ V0 a9 K( R while((SPI2->SR & SPI_I2S_FLAG_TXE)==RESET);7 M5 Z1 a; X! H$ [6 [6 s! |3 |. F /*发送一个字节*/ SPI2->DR = byte;& `$ g" O0 j3 g* r( A. s /* 等待接收寄存器有效*/ while((SPI2->SR & SPI_I2S_FLAG_RXNE)==RESET);0 ^1 s5 Y h, p- k4 ^ n R, _+ ? return(SPI2->DR);8 U5 f; d$ s. b- f8 S2 C1 L }1 s( E8 {5 w4 \# t /*---------------------------------------------------------6 c) v( J, M, b( B Func: AD7799读取寄存器数据! Z6 b# R9 v- }) E' J( P: g Time: 2012-3-29 Ver.: V1.0 Note:# s& e& g$ `$ z. m/ T" t ---------------------------------------------------------*/ void AD7799_ReadReg(u8 RegAddr,u8 *Buffer,u8 Length) {6 R2 q4 d1 c# F- ^ u8 i; ADC_SPI_CS_CLR;8 s+ F1 v. g1 h* ?+ s. v3 |- Z RegAddr|=ADC_OP_READ; //ADC_WriteBytes(&RegAddr,1); //ADC_ReadBytes(Buffer,Length);8 F6 j) J7 [" K3 N& |' o. D) Z SPIByte(RegAddr);$ S, p% g' _$ j, v: s# d# a1 P for(i=0;i<Length;i++)* r8 L, z7 K8 A) Q+ J0 G7 g' E6 } {' l8 v- a+ g$ V& L4 m- \0 ]1 n Buffer=SPIByte(Buffer);4 n+ o7 c4 b4 [- J }+ X2 \8 X) i5 o ADC_SPI_CS_SET;- d" j( g8 [1 \0 E6 t" U5 c& W9 f9 |7 ^ } _* B, r) v! ? /*--------------------------------------------------------- Func: AD7799写入寄存器数据- u6 r2 D! M; A" _9 n6 f# H Time: 2012-3-29 Ver.: V1.0 Note: ---------------------------------------------------------*/2 q: }( \6 \, g1 e3 h void AD7799_WriteReg(u8 RegAddr,u8 *Buffer,u8 Length). B1 c2 S/ q4 W: H7 ]3 p* w { u8 i; ADC_SPI_CS_CLR;% S4 s6 L! t$ U1 k% y RegAddr|=ADC_OP_WRITE;* h+ N6 k! K' V0 L+ H. g7 k //ADC_WriteBytes(&RegAddr,1); //ADC_WriteBytes(Buffer,Length); SPIByte(RegAddr);4 Z& H! G, k5 Q for(i=0;i<Length;i++) { SPIByte(Buffer);2 ?- I2 V$ @0 | }! s) r3 K+ t+ }+ E- m" H$ `$ b9 i4 R ADC_SPI_CS_SET; } /*---------------------------------------------------------1 f$ f9 G- ?- T7 i8 P Func: AD7799忙判断) E) u; C1 x, L$ X4 \ Time: 2012-3-29 Ver.: V1.0% [! u& {/ V( D Note: 0/OK >0/ERROR,timeout% g7 G5 T& x8 d P1 o$ ` ---------------------------------------------------------*/ u8 AD7799_WaitBusy()- E: T& `8 E o. O {: b4 H* C4 V6 C5 Z5 b3 m5 U3 V u16 i; ADC_SPI_CS_CLR; i=0;0 M( Q$ p9 f9 U. Q* ]% l, ~ while(ADC_RDY_DAT>0); }+ D6 |' m/ M {/ P1 I& ]! I5 l* @( b/ r i++; if(i>5000)) {, M9 R7 z$ ]; B7 x2 C4 ? return 1;1 S! K% o& M6 [- D" m8 M6 K4 t( ~ }# E( N+ N( u& W5 Y7 `4 s ADC_SPI_CS_SET; return 0;: c4 A" n* q& r& p } /*---------------------------------------------------------4 s. z4 m# v8 W; N Func: AD7799通道内部校准 Time: 2012-3-29 Ver.: V1.0 Note: 0/OK >0/Error; W) d) I w H9 X" b( e ---------------------------------------------------------*/3 p! e' J8 }) v: A u8 AD7799_Calibrate(u8 CHx,u8 Gain)) e7 D+ U) {" E, O {) {$ k& Z" s' U, J u8 R,Cmd[2]; Cmd[0]=0x30|Gain; //0x10代表配置寄存器高八位中单双极性位置1) @* G6 |: p% f: }& I/ a! w7 z Cmd[1]=0x30|CHx; AD7799_WriteReg(ADC_REG_CONFIG,Cmd,2); //设置配置寄存器 Cmd[0]=0x80; Cmd[1]=0x0F;, {, E+ ^ x; G AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行内部零度校准 ~1 c& |1 |- T6 b R|=AD7799_WaitBusy(); //等待校准完成8 I! a. ^# |( j/ i) K Cmd[0]=0xA0;6 v4 r$ r+ J6 J5 U( b 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(); //等待校准完成( ^5 m2 J8 Y6 J' b7 f. u" X j7 S Cmd[0]=0xE0; Cmd[1]=0x0F;7 X9 C: O0 R! o: A: O1 F$ J4 z AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行系统满度校准2 v( ~" A7 C. A' d7 o. q8 R- H R|=AD7799_WaitBusy(); //等待校准完成 */ return R;( X$ q6 _6 @, i }0 ^1 @3 g6 x3 A c. H u8 AD7799_Read_STAT(void); O" T) S5 z; K2 |7 o {% i b/ ?& f" |5 G% f6 t8 l3 O u8 Cmd[2]; AD7799_ReadReg(ADC_REG_STAT,Cmd,1); return Cmd[0];- s) o) B; s* `6 q } u16 AD7799_Read_CONFIH(void)1 _! K3 c+ O2 L4 L- @( { o { u8 Cmd[2];; C9 u# |$ U g$ Z4 m1 d; R AD7799_ReadReg(ADC_REG_CONFIG,Cmd,2); return (Cmd[0]<<8)+Cmd[1]; }* F7 J+ n: _4 }3 k1 m( _' }9 C! H; G" f: t /*--------------------------------------------------------- Func: AD7799复位 Time: 2012-3-29 Ver.: V1.0$ |0 d5 N0 z3 W! F Note: 0/OK >0/Error! m) z3 Q- L$ Z4 e: D7 q ---------------------------------------------------------*/ void AD7799_Reset(), y: N. T" C- d1 e { // u8 Cmd[4]={0xFF,0xFF,0xFF,0xFF}; ADC_SPI_CS_CLR;5 f1 o) s! A1 G+ z1 b: I // ADC_WriteBytes(Cmd,4);# _" ?( p) D# y* k SPIByte(0xFF);! R5 s) w+ d4 ` z5 P4 Z9 c/ ` SPIByte(0xFF);) g1 @' ?0 X9 M$ G8 K SPIByte(0xFF);- e1 l1 r$ r$ @3 |( F SPIByte(0xFF);. q6 ?% u7 t% q- u6 n ADC_SPI_CS_SET; } /*--------------------------------------------------------- Func: AD7799初始化 Time: 2012-3-29: h& z# W$ Q6 r/ U C' ^: R' i Ver.: V1.0 Note: 0/OK >0/Error! a+ }+ u7 ^4 w% m# _! R5 v3 Y ---------------------------------------------------------*/& E# \% S; Y# R/ u u8 AD7799_Init(u8 Gain) {! e8 e! A7 g- F& h. s- B) x u8 ID,Cmd[2];9 ~& k. m+ g: M. n2 c" d AD7799_Reset(); delay_ms(5);% J7 B$ w( r* D) ^: A // AD7799_ReadReg(ADC_REG_ID,&ID,1); //读取器件ID // if((ID==0xFF)||(ID==0x00))return 1;0 U0 F- F u5 g6 Q 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;9 p) e0 H% p/ s4 ^# b$ b5 f // AD7799_WriteReg(ADC_REG_IO,Cmd,1);( k1 c3 o3 M0 d) B return 0;1 l5 e9 D9 T8 Y I% q } /*--------------------------------------------------------- Func: AD7799开始转换 Time: 2012-3-29/ P5 O: W( q( e$ F% o% y Ver.: V1.07 B3 `, s- z; f! \6 V% D Note:3 r; G9 k9 X- q/ N$ n7 E1 u ---------------------------------------------------------*/+ L& J8 R4 W/ J2 T' H7 _ b void AD7799_Start(u8 CovChx,u8 CovGain,u8 CovRate,u8 CovMode) { u8 Cmd[2]; Cmd[0]=0x30|CovGain; Cmd[1]=0x30|CovChx; //0x30. Z# s3 \" e- n% F4 J AD7799_WriteReg(ADC_REG_CONFIG,Cmd,2);, z l7 B: p5 S$ I; a Cmd[0]=0x10|CovMode; Cmd[1]=CovRate; AD7799_WriteReg(ADC_REG_MODE,Cmd,2); } /*--------------------------------------------------------- Func: AD7799读取转换结果4 n2 e B; K' U3 i; s Time: 2012-3-29 Ver.: V1.0* l6 h6 j8 K b9 p) V1 Q3 M2 D Note: ---------------------------------------------------------*/4 v# W% |/ J+ @6 u5 }! j* Q( A C u32 AD7799_Read() {6 `& u d/ P) [$ p+ I9 E u8 Cmd[4];" y0 b5 k4 E8 j" p+ }2 k u16 i=0; u32 D;* G2 \; v) S1 D1 z( i m1 ` Cmd[0]=0;% _6 p! O0 K7 `. M1 e6 ]& x while((AD7799_Read_STAT()&ad7799_StatRDY)!=0) {. }$ o! u% }1 Q4 i. O/ F5 J i++; [+ k( J8 j% W if(i>10000)" \/ ^7 w/ X( z7 Q7 c break;& G$ n; F% B/ `# k" } }+ v0 S) J6 H! P: F% I' D if((AD7799_Read_STAT()&ad7799_StatERR)==0)2 k; h( X @# B! G4 O0 F { AD7799_ReadReg(ADC_REG_DATA,Cmd,3); D=(Cmd[0]<<16)+(Cmd[1]<<8)+Cmd[2]; return D;& f$ y: U8 H, m5 { } else! N. K' f. g6 C6 d- f: m. _ {; i% B+ G8 x" K# r* p: a1 N AD7799_ReadReg(ADC_REG_DATA,Cmd,4);9 S0 D% W: Y8 a( t. Y- J return 0xFFFFFF; } } void ADt7799test(). c3 N1 N$ m9 v6 `$ F {" k0 \- r% H4 i( c, j; ] u32 Val; u16 a; AD7799_Init(ADC_CON_GAIN1); AD7799_Start(ADC_CON_CH1,ADC_CON_GAIN1,0x01,ADC_MODE_CONTINUOUS); AD7799_Read(); O! f- ^% a, y$ u AD7799_Read();9 f: A4 Q, y |0 y AD7799_Read(); AD7799_Read();3 b. d; D' q- q* E0 _, m/ b 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(-)配置寄存器单双极性选择 |
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