STMCU小助手
发布时间:2022-1-24 22:00
|
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: & g8 L! A& Y3 E) K6 S( e 下面的代码是完全调试通过的: #define ADC_SPI_CS_CLR GPIOB->BSRR=GPIO_Pin_12- I! `' v5 A- \ `2 {* O% p #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))0 b5 X% H& L9 S; s" G/ e3 D, B/ M #define ADC_OP_WRITE ((u8)(0x00)) ) x* i) P0 ^* U) C9 ]" c% Z7 ~( R6 G6 C #define ADC_REG_STAT ((u8)(0x00<<3)) #define ADC_REG_MODE ((u8)(0x01<<3))5 F1 b$ }+ r% ]; w) P #define ADC_REG_CONFIG ((u8)(0x02<<3)) #define ADC_REG_DATA ((u8)(0x03<<3)) #define ADC_REG_ID ((u8)(0x04<<3)). X8 Y* @1 Y) Z: {8 ]0 k+ g #define ADC_REG_IO ((u8)(0x05<<3))) a; ?2 ^% Z2 P. g5 P z #define ADC_REG_OFFSET ((u8)(0x06<<3))! o3 Q/ S( y1 w) G4 V( @ //#define ADC_REG_STAT ((unsigned char)(0x07)) #define ADC_CON_CH1 ((u8)(0x00)): g7 R/ X! z! M$ ]3 ~- F1 y #define ADC_CON_CH2 ((u8)(0x01)) #define ADC_CON_CH3 ((u8)(0x02))3 V8 r' G7 D7 D. P* e #define ADC_CON_AVDD ((u8)(0x07)) #define ADC_CON_GAIN1 ((u8)(0x00)); R6 O" _4 h+ w$ x5 K2 t/ d Z# |$ C #define ADC_CON_GAIN2 ((u8)(0x01)) #define ADC_CON_GAIN3 ((u8)(0x02)) #define ADC_CON_GAIN4 ((u8)(0x03)) #define ADC_CON_GAIN5 ((u8)(0x04))3 A# P. ~" ~/ g& @$ \& ^* ? #define ADC_CON_GAIN6 ((u8)(0x05)) #define ADC_CON_GAIN7 ((u8)(0x06))& G0 W8 a' F% x- U6 O #define ADC_CON_GAIN8 ((u8)(0x07)) #define ADC_SINGLE_POLAR ((u8)(1<<4)) //单双极性+ t/ f$ V- m# Q. Y( E" W9 P6 r7 x# F #define ADC_DOUBLE_POLAR ((u8)(0<<4)) #define ADC_MODE_CONTINUOUS ((u8)(0<<5)) #define ADC_MODE_ONCE ((u8)(1<<5)) 8 |9 V5 K% Q& ~" b* x7 o #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;7 X: z$ P7 I- \6 O GPIO_InitTypeDef GPIO_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);$ R) @: F. q( u( f$ A3 @ 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;% B6 ]& {. `3 A) B& t+ l/ s. c 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 */: r- @2 E7 `' @9 N* J, P4 m SPI_Cmd(SPI2, DISABLE); //必须先禁能,才能改变MODE SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //两线全双工- L$ X( K8 R0 W. g% C7 t* b SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主. X2 h7 l P0 ] SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8位" }& t6 f4 o( ?& T- X4 s+ T SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //CPOL=1 时钟悬空高. Z8 y$ M1 S% q$ d1 X SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //CPHA=1 数据捕获第2个; e# V' p" f/ }" D6 x SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //软件NSS' |4 g* j, j! Z' q SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //2分频 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位在前/ q9 I! \4 `4 {8 `% |% x3 l SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC7 SPI_Init(SPI2, &SPI_InitStructure);# o+ d4 R4 \5 n; G SPI_Cmd(SPI2, ENABLE); ! ^$ T \+ U. a8 Q5 N + }" C! ~6 k) \2 J. z( W( ^} u8 SPIByte(u8 byte) { /*等待发送寄存器空*/1 ?) o: v( ?8 ^+ X8 ?: p% { while((SPI2->SR & SPI_I2S_FLAG_TXE)==RESET);- l9 ^9 W4 H/ |6 h /*发送一个字节*/6 |% Y1 F) Q* D% {1 Y SPI2->DR = byte; /* 等待接收寄存器有效*/7 l. N. _4 C* g while((SPI2->SR & SPI_I2S_FLAG_RXNE)==RESET); return(SPI2->DR);6 G8 T9 D0 `' U3 V y } /*---------------------------------------------------------1 f9 [" \% V& X0 W& K0 Q Func: AD7799读取寄存器数据 Time: 2012-3-29 Ver.: V1.02 D: h' Z( {/ B6 m% m Note: ---------------------------------------------------------*/, | z6 V+ q( G void AD7799_ReadReg(u8 RegAddr,u8 *Buffer,u8 Length)7 F8 B- `1 p5 b) v5 S+ N, ] { u8 i;! A0 `$ w" c+ r) A ADC_SPI_CS_CLR; RegAddr|=ADC_OP_READ; S) ?, R1 Q G( n2 w //ADC_WriteBytes(&RegAddr,1);* ]7 O4 h$ N9 E/ @: y# Y9 q+ o //ADC_ReadBytes(Buffer,Length); SPIByte(RegAddr); for(i=0;i<Length;i++)" S( P, ]! f8 M0 @: a9 }& C. C {1 i- T4 n2 C; u8 x Buffer=SPIByte(Buffer);1 @( l. i' T2 E/ _& Q3 _! ~+ i } ADC_SPI_CS_SET; }) i- S3 B. s3 t0 F1 T! i 5 v9 A: n4 C1 p" b# i. d /*--------------------------------------------------------- Func: AD7799写入寄存器数据* e# Y* q2 _' W8 l) _ Time: 2012-3-29 Ver.: V1.0 Note: ---------------------------------------------------------*/. T# K z) v1 g void AD7799_WriteReg(u8 RegAddr,u8 *Buffer,u8 Length) {% {0 T- u; q: m1 h2 L. w% i2 E* C u8 i; ADC_SPI_CS_CLR; RegAddr|=ADC_OP_WRITE; //ADC_WriteBytes(&RegAddr,1); //ADC_WriteBytes(Buffer,Length); i/ O( V- P1 p+ ~, G& s! _+ h SPIByte(RegAddr); for(i=0;i<Length;i++)- t6 E* R/ p3 x { SPIByte(Buffer);, T9 o0 B9 V& ` } ADC_SPI_CS_SET;; \6 u3 W( j( i* ` } /*---------------------------------------------------------( e" ?7 p e: Z7 l' i. O5 i. ? Func: AD7799忙判断/ a) `, `) D, P/ b2 l9 i Time: 2012-3-29 Ver.: V1.0 L1 K& g* Z+ h Note: 0/OK >0/ERROR,timeout ---------------------------------------------------------*/3 S% ?3 p3 w- {8 Z1 _ u8 AD7799_WaitBusy() {$ p: r% ^+ Y5 h, e2 }2 L u16 i; ADC_SPI_CS_CLR;: Z* G4 {' d' z9 K3 o" j i=0; while(ADC_RDY_DAT>0) { i++; if(i>5000)1 h3 S/ \2 U# j& P/ t# D# A$ O return 1;% v# l! |8 [" C" i% b } ADC_SPI_CS_SET;! t' _& @: Y' A1 r5 ]( d return 0; } /*--------------------------------------------------------- Func: AD7799通道内部校准& v; t8 y3 m. H$ w Time: 2012-3-29 Ver.: V1.0 Note: 0/OK >0/Error ---------------------------------------------------------*/0 D) r4 N# l# L7 K- }3 k5 }, b u8 AD7799_Calibrate(u8 CHx,u8 Gain) { u8 R,Cmd[2];4 d( ^( s& e% H7 g Cmd[0]=0x30|Gain; //0x10代表配置寄存器高八位中单双极性位置1, K4 c' a2 J; G; C5 `5 \8 r) f Cmd[1]=0x30|CHx; AD7799_WriteReg(ADC_REG_CONFIG,Cmd,2); //设置配置寄存器 Cmd[0]=0x80;- S/ d1 A! t6 ~$ j( ^0 C+ t! U A Cmd[1]=0x0F;/ r7 l7 `% [% Z% J+ B ^8 v* G8 Z$ h AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行内部零度校准6 }9 {! y: t6 A* z& `4 S R|=AD7799_WaitBusy(); //等待校准完成 Cmd[0]=0xA0;# n5 v {$ Z( N Cmd[1]=0x0F;) D; o9 K7 v7 A4 b# B AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行内部满肚校准 R|=AD7799_WaitBusy(); //等待校准完成 /* Cmd[0]=0xC0;5 s. `" j! u) r( k7 f. H Cmd[1]=0x0F;1 v9 W: x3 l+ w* _4 t$ S3 R AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行系统零度校准 R|=AD7799_WaitBusy(); //等待校准完成 Cmd[0]=0xE0;% I, s9 @0 A( d Cmd[1]=0x0F; AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行系统满度校准: g/ p8 V6 A* F# U" r R|=AD7799_WaitBusy(); //等待校准完成 */ return R;- B \% \. l; ~1 ^- Y# W& d }: e+ K: Z1 V) F+ F9 ^ u8 AD7799_Read_STAT(void) { u8 Cmd[2]; AD7799_ReadReg(ADC_REG_STAT,Cmd,1); return Cmd[0];1 H8 [) y: O0 l9 f- v } u16 AD7799_Read_CONFIH(void)5 J: |( K1 W0 h& \ { u8 Cmd[2]; AD7799_ReadReg(ADC_REG_CONFIG,Cmd,2);# P1 m. s7 j- c$ x% k8 F8 V# q return (Cmd[0]<<8)+Cmd[1];: ~8 Y7 A3 ^6 J n. \& L }+ {1 F4 I c& C. } /*--------------------------------------------------------- Func: AD7799复位% y7 ~4 x2 L* T- ^ Time: 2012-3-29 Ver.: V1.0 Note: 0/OK >0/Error ---------------------------------------------------------*/ void AD7799_Reset(), P, [& ~' v% _# ?/ o. I, D7 b {. e u# u' m6 \; t1 }0 M // u8 Cmd[4]={0xFF,0xFF,0xFF,0xFF};* h. ^: }2 ^* d/ M' G w ADC_SPI_CS_CLR;! a0 `9 R5 g n2 H! X4 b W5 m // ADC_WriteBytes(Cmd,4);( r4 Z3 m& x5 f- @, D. e SPIByte(0xFF);$ e' A$ g7 e9 ~, l3 \ SPIByte(0xFF);) f; \, T4 J$ o$ L SPIByte(0xFF);6 ] c) s$ e" t6 E6 }; w SPIByte(0xFF); E; V# N7 ?& b( w6 ` ADC_SPI_CS_SET; }; {5 ]0 _* j1 c) A+ C q% C /*---------------------------------------------------------8 s$ U; T0 ?0 b( } Func: AD7799初始化 Time: 2012-3-29 Ver.: V1.08 w: }3 r, S. K( C# b Note: 0/OK >0/Error ---------------------------------------------------------*/5 z( u" t- l+ a u8 AD7799_Init(u8 Gain) {# I- G) O5 d2 q( U# y/ p X5 M y 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校准, ?) V* d+ N7 F& d5 q% l AD7799_Calibrate(ADC_CON_CH2,Gain); //通道2校准 //AD7799_Calibrate(ADC_CON_CH3,Gain); //通道3校准 // Cmd[0]=0<<6;. y7 O1 h+ m) K# ~; O \% G4 @ // AD7799_WriteReg(ADC_REG_IO,Cmd,1);9 v9 u: s, q% \! W return 0; } /*---------------------------------------------------------# D- g7 ], u x Func: AD7799开始转换% X; b- I' H( z/ ^0 p Time: 2012-3-298 Q3 p* ~# p. c0 J. [% u; f5 V6 R0 l; t& t Ver.: V1.0 Note: ---------------------------------------------------------*/0 e7 Q& f9 x) }, P3 S2 f void AD7799_Start(u8 CovChx,u8 CovGain,u8 CovRate,u8 CovMode)4 h2 ?7 ?2 m3 k. H, R+ G0 T { u8 Cmd[2];8 z) m/ U3 y0 K8 l! u Cmd[0]=0x30|CovGain;+ Y; D2 i1 G3 i, J) a/ { Cmd[1]=0x30|CovChx; //0x30 AD7799_WriteReg(ADC_REG_CONFIG,Cmd,2); Cmd[0]=0x10|CovMode;' G9 Z, N% l" v Cmd[1]=CovRate; AD7799_WriteReg(ADC_REG_MODE,Cmd,2); } /*---------------------------------------------------------1 u V0 p6 O/ v6 p& ] Func: AD7799读取转换结果: R7 I' ^. [3 ~0 w Time: 2012-3-29 Ver.: V1.0 Note:, z, W1 U. M; X6 l% P6 [ ---------------------------------------------------------*/ u32 AD7799_Read()* [) r; T4 J+ A$ C3 R7 S( T& F* k* z {) L+ `) N% I& e) T5 ?4 Y u8 Cmd[4];0 S# R' P, c# A! N- | u16 i=0;8 t5 f4 p' B" E0 P2 W u32 D;+ H: _ f! X1 \5 d* O: K2 \8 K Cmd[0]=0; while((AD7799_Read_STAT()&ad7799_StatRDY)!=0) { i++;0 u1 [/ m% S% t, d7 M/ U& M if(i>10000) break; } if((AD7799_Read_STAT()&ad7799_StatERR)==0)& f- F h8 x/ E9 O1 X/ Z$ L4 a& C { AD7799_ReadReg(ADC_REG_DATA,Cmd,3); D=(Cmd[0]<<16)+(Cmd[1]<<8)+Cmd[2]; return D;! Q$ U5 t# d: A5 _5 v4 m( C9 O2 S }& a' t* U/ Q( {; S) k& R. s; L) |3 C else6 p4 Z& U _3 ]. y' [ { AD7799_ReadReg(ADC_REG_DATA,Cmd,4);) O; @0 }5 a& R: n return 0xFFFFFF;9 `2 r+ Y& h2 S9 K2 M: A7 A8 K } ( S, F" N% B3 U, H: z7 {7 \ } void ADt7799test(); P. n& f( e7 t {: H* S; h0 C$ W; x/ \ u32 Val;2 w+ r1 }6 k( x0 V! S u16 a;. m/ E3 g9 Y1 |9 W. J AD7799_Init(ADC_CON_GAIN1);1 t9 s7 G; H/ S% ] AD7799_Start(ADC_CON_CH1,ADC_CON_GAIN1,0x01,ADC_MODE_CONTINUOUS); 5 n6 }1 c& g `) a. N2 | AD7799_Read();1 k7 A5 j' I$ O7 h, H0 \$ J3 M; h AD7799_Read();) b$ R# p9 `$ e9 e9 H4 e AD7799_Read();' s- c# r& N) {* P j* ^ AD7799_Read();8 `# L( s5 N' Q6 E AD7799_Read();9 R1 Z, h( V+ J/ u& M AD7799_Read(); # @. p2 V2 \9 S } 下面说一下自己遇到的问题: 一:增益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(-)配置寄存器单双极性选择 ?4 p1 k8 @% X* D$ i |
实战经验 | 基于STM32G0B1的全桥移相+同步整流PWM解决方案
DMA 原理从 0 到 1:为什么它能解放 CPU?一文讲透 DMA 的工作流程
【福利三:逢7发帖赢大礼】8、基于STM32G070板子的Ymodem串口协议IAP Bootloader开发
【福利三:逢7发帖赢大礼】7、基于STM32G070板子TF卡的FATFS移植
【福利三:逢7发帖赢大礼】6、基于STM32G070板子spiflash的FATFS移植
【福利三:逢7发帖赢大礼】5、基于STM32G070板子IRDA红外遥控接收解码
【福利三:逢7发帖赢大礼】4、基于STM32G070板子SPI flash 移植SFUD库
【福利三:逢7发帖赢大礼】基于STM32G070板子的uart shell移植设计
【福利三:逢7发帖赢大礼】3、基于STM32G070板子的YModem串口协议通信
【福利三:逢7发帖赢大礼】2、基于STM32G070板子的OLED移植U8G2库
微信公众号
手机版