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: 5 t5 z9 C( e+ E9 K# K5 t# S, d' s: q# z 下面的代码是完全调试通过的: #define ADC_SPI_CS_CLR GPIOB->BSRR=GPIO_Pin_12. B( M( p- ^/ w7 m c #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))' l& Q% i, A! ] #define ADC_REG_MODE ((u8)(0x01<<3)) #define ADC_REG_CONFIG ((u8)(0x02<<3))& n/ K$ n8 I1 M4 \# p# X$ x- U #define ADC_REG_DATA ((u8)(0x03<<3)) #define ADC_REG_ID ((u8)(0x04<<3)) #define ADC_REG_IO ((u8)(0x05<<3))4 @. h& t b4 E; `0 E, }1 B #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))* ~" P% e: [. r* \* ?' [2 X9 p #define ADC_CON_CH3 ((u8)(0x02)). Y0 T2 {& o3 `. m" M3 c# B #define ADC_CON_AVDD ((u8)(0x07)) #define ADC_CON_GAIN1 ((u8)(0x00)) #define ADC_CON_GAIN2 ((u8)(0x01)), s# b5 z2 Y ? #define ADC_CON_GAIN3 ((u8)(0x02))) K/ [9 }/ j1 [: l, E #define ADC_CON_GAIN4 ((u8)(0x03))! d f% }$ ]& W$ h #define ADC_CON_GAIN5 ((u8)(0x04)) #define ADC_CON_GAIN6 ((u8)(0x05)) #define ADC_CON_GAIN7 ((u8)(0x06))* Y0 {1 @5 [0 T1 ~ U #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))+ t8 y* G: W' Z4 l6 o" B3 ~ #define ad7799_StatERR ((u8)(1<<6)) #define ad7799_StatNOR ((u8)(1<<5)) void SPI_Config(void)% P2 L1 N& N1 _ { SPI_InitTypeDef SPI_InitStructure;' o% ]; a% o' M q8 m GPIO_InitTypeDef GPIO_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);& U U! n( h& c3 s9 c6 c Y" p, F' `' A9 k RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;0 |; V' Y7 j2 Q! E GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;# l( D5 V/ p' ^* v* N6 c) m GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;0 b- E- ], ^9 ?# G6 ? 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; ^; p8 V% i2 F( E+ B5 } GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOB, &GPIO_InitStructure); /* SPI2 configuration */( _# D! H; H t! D- R0 }3 ^- }9 \' C SPI_Cmd(SPI2, DISABLE); //必须先禁能,才能改变MODE0 B. G5 _# q# n: l6 F$ k SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //两线全双工% C1 h0 B2 F r# w SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主 SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8位. V- p3 p" \/ h SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //CPOL=1 时钟悬空高: _$ R$ b8 x1 C9 k* K) c" [+ p$ ` SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //CPHA=1 数据捕获第2个& A& U" `% R1 U! D! l' H: H6 l 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" p4 s! H" M- @0 Z/ p# j. c" B % l1 G' F4 K) J, q! M: W" R SPI_Init(SPI2, &SPI_InitStructure);8 W6 y7 w% \ T8 C7 n. w SPI_Cmd(SPI2, ENABLE); % b& O8 l/ b) J. F/ K 0 l. g' z& ^/ c" Z- s0 }$ O0 d9 u& n, W} 7 h# u$ P" ]+ c. c3 ^ u8 SPIByte(u8 byte) {* q2 ]: O6 P' ^% n /*等待发送寄存器空*/ while((SPI2->SR & SPI_I2S_FLAG_TXE)==RESET); /*发送一个字节*/7 H' t) p2 l' g0 X SPI2->DR = byte;4 x- v( y, D0 n/ h+ [6 e2 n4 } /* 等待接收寄存器有效*/ while((SPI2->SR & SPI_I2S_FLAG_RXNE)==RESET);. @! ]* b+ P; i" Q/ l return(SPI2->DR); } /*--------------------------------------------------------- Func: AD7799读取寄存器数据 Time: 2012-3-29 Ver.: V1.0 Note: ---------------------------------------------------------*/2 \/ X3 E" O/ N void AD7799_ReadReg(u8 RegAddr,u8 *Buffer,u8 Length) { u8 i;) Y+ l# l- `% i% b' u ADC_SPI_CS_CLR;1 m1 g e% L; [: U$ p- Y RegAddr|=ADC_OP_READ;0 G( k9 Z2 A5 x9 g //ADC_WriteBytes(&RegAddr,1); //ADC_ReadBytes(Buffer,Length);# y9 c0 o& k# z- g/ B3 e# F SPIByte(RegAddr); for(i=0;i<Length;i++)" O. T _3 o2 f- p B) h { Buffer=SPIByte(Buffer); }6 ~' J5 o4 I8 m( H. [# m5 m. Z ADC_SPI_CS_SET; }5 |6 M# x- D: H O8 U 1 ~& k1 K2 F' o* ] /*---------------------------------------------------------5 `7 W$ n8 s0 U. D9 m5 x4 } Func: AD7799写入寄存器数据! F' v) ^7 I( {! d/ w Time: 2012-3-29 Ver.: V1.0* k7 ?, G- \* H' \. ]4 h* q) B Note: ---------------------------------------------------------*/ void AD7799_WriteReg(u8 RegAddr,u8 *Buffer,u8 Length) {* Y# V5 e6 r) [# _9 E u8 i;' O. \8 T; |! E+ p" k2 T9 f ADC_SPI_CS_CLR;9 H6 Y: y/ k3 |0 W- _- R# N4 G RegAddr|=ADC_OP_WRITE; //ADC_WriteBytes(&RegAddr,1); //ADC_WriteBytes(Buffer,Length);+ H5 O" A' w% W, H SPIByte(RegAddr); for(i=0;i<Length;i++): m- c# b4 F1 t. ~, ? { SPIByte(Buffer); }; T& _4 V4 N- j5 [ h ADC_SPI_CS_SET; } /*--------------------------------------------------------- Func: AD7799忙判断$ ]5 x& ?' u4 E- X+ Y Time: 2012-3-29 Ver.: V1.0. c" g4 }" v) J9 K4 i! d1 \ Note: 0/OK >0/ERROR,timeout ---------------------------------------------------------*/ u8 AD7799_WaitBusy() { u16 i; ADC_SPI_CS_CLR;# u. j. \. G. L& z$ T i=0; while(ADC_RDY_DAT>0) { i++; if(i>5000) return 1; ?$ ~* k" x. _5 p/ Q. } }+ s$ `" O9 u* D+ n7 j ADC_SPI_CS_SET; return 0; } /*---------------------------------------------------------" {/ \# F9 T$ ?: P5 ^ Func: AD7799通道内部校准 Time: 2012-3-29. L J( P: B3 N1 j2 A1 a# |; W Ver.: V1.0 Note: 0/OK >0/Error ---------------------------------------------------------*/ u8 AD7799_Calibrate(u8 CHx,u8 Gain) { u8 R,Cmd[2];1 E' \1 M4 q3 Y7 B, }, @, G Cmd[0]=0x30|Gain; //0x10代表配置寄存器高八位中单双极性位置1 Cmd[1]=0x30|CHx;0 D! }( Q0 X! b# `5 ~0 J" ~ AD7799_WriteReg(ADC_REG_CONFIG,Cmd,2); //设置配置寄存器# d# L0 x5 x" ]6 U( w3 x Cmd[0]=0x80; Cmd[1]=0x0F;) @1 V# ^& z6 A0 m: L5 b AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行内部零度校准 R|=AD7799_WaitBusy(); //等待校准完成 Cmd[0]=0xA0;% M7 O A) c' f+ |+ X7 \% C Cmd[1]=0x0F; AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行内部满肚校准 R|=AD7799_WaitBusy(); //等待校准完成 /* Cmd[0]=0xC0;5 [2 z$ L; {+ p: A Cmd[1]=0x0F; AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行系统零度校准6 C& { N! S4 L- A/ y: H R|=AD7799_WaitBusy(); //等待校准完成' C2 y ^" W }: H; b, a Cmd[0]=0xE0;( B, E' X# |* r9 k+ w Cmd[1]=0x0F; AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行系统满度校准 R|=AD7799_WaitBusy(); //等待校准完成 */ return R;* m3 k% p% k) p } u8 AD7799_Read_STAT(void)) u$ k( a8 v. G {! ?6 u; e D8 c Y u8 Cmd[2]; AD7799_ReadReg(ADC_REG_STAT,Cmd,1);! u2 q3 A- C9 _- d/ Z* c return Cmd[0]; } u16 AD7799_Read_CONFIH(void) {$ P5 c/ H6 |: Y: p u8 Cmd[2]; AD7799_ReadReg(ADC_REG_CONFIG,Cmd,2); return (Cmd[0]<<8)+Cmd[1]; } /*--------------------------------------------------------- Func: AD7799复位0 p- G: o, Q2 x/ ~ Time: 2012-3-297 I9 X& u h4 C9 c! u: H0 z) h6 E Ver.: V1.0# B4 `1 h2 I L, {) K$ V Note: 0/OK >0/Error& e) Y! d% q$ v# ] ---------------------------------------------------------*/! Z/ J* v- }) b/ I void AD7799_Reset() { // u8 Cmd[4]={0xFF,0xFF,0xFF,0xFF}; ADC_SPI_CS_CLR;# f+ u) @) x) |- z% z& f2 o // ADC_WriteBytes(Cmd,4);5 \4 m% u. Z& _% A1 `9 { SPIByte(0xFF); SPIByte(0xFF);, O1 z+ m) ~+ l) j/ u0 U SPIByte(0xFF);4 u6 o) J$ z: w! j" o* o; j SPIByte(0xFF);+ C: [( ?" {, n: u ADC_SPI_CS_SET; }# Y N7 \& E! z( H* S* _: N /*---------------------------------------------------------# b) ]# [" a' K. ? Func: AD7799初始化 ?1 a& G3 _+ l$ @+ H Time: 2012-3-29! r; G% [ m; D+ A* H6 R4 P Ver.: V1.0* V. j! }4 R; d Note: 0/OK >0/Error ---------------------------------------------------------*/ u8 AD7799_Init(u8 Gain)/ c9 l; c( \1 l4 j, \ { u8 ID,Cmd[2]; AD7799_Reset(); delay_ms(5);: m0 I9 _( i) M // AD7799_ReadReg(ADC_REG_ID,&ID,1); //读取器件ID, a7 h6 c9 E |7 r4 L // if((ID==0xFF)||(ID==0x00))return 1; AD7799_Calibrate(ADC_CON_CH1,Gain); //通道1校准8 ]( `( e+ i0 w2 C$ F AD7799_Calibrate(ADC_CON_CH2,Gain); //通道2校准; h9 \ d0 {* B4 _8 n. x, J //AD7799_Calibrate(ADC_CON_CH3,Gain); //通道3校准' C: I* F/ W+ H! n: T5 F2 G t // Cmd[0]=0<<6; // AD7799_WriteReg(ADC_REG_IO,Cmd,1);2 ~4 F" ?6 [# s return 0;( W! l3 @( W1 S } /*---------------------------------------------------------2 N4 _; n& {2 `9 C" P* \ Func: AD7799开始转换 Time: 2012-3-29 Ver.: V1.0- {5 B9 t4 a% I* W% {/ K) }( D3 t; F Note: ---------------------------------------------------------*/ void AD7799_Start(u8 CovChx,u8 CovGain,u8 CovRate,u8 CovMode)3 y7 R, N$ }/ } {! Q8 w6 @! P6 i$ g9 u( C2 u- P u8 Cmd[2]; Cmd[0]=0x30|CovGain; Cmd[1]=0x30|CovChx; //0x30+ V! I6 e' k: t0 W* S AD7799_WriteReg(ADC_REG_CONFIG,Cmd,2); Cmd[0]=0x10|CovMode; Cmd[1]=CovRate; AD7799_WriteReg(ADC_REG_MODE,Cmd,2); } /*---------------------------------------------------------$ X+ I, P2 k0 f3 P Func: AD7799读取转换结果 Time: 2012-3-29- N* F0 v* j2 i6 M: i1 F9 D2 \5 i Ver.: V1.0 Note:- H) R4 z$ F* w/ a$ ~; |: I4 O9 ~! M ---------------------------------------------------------*/+ b& i M4 J C" r* U u32 AD7799_Read()0 i9 _ Z1 F# }( B+ j$ V8 v { u8 Cmd[4]; u16 i=0; u32 D;: e+ E9 F' T: }, o* x% K4 k. L Cmd[0]=0;# E, }6 ~. L0 \, E while((AD7799_Read_STAT()&ad7799_StatRDY)!=0)+ `' z6 \$ ^% k4 {' `1 T! c. a X {* e# r7 ~9 l. m3 w3 [ i++;8 D3 C: U7 s6 v$ M! I, p if(i>10000) break; }3 s2 u7 J( x% Q! i2 L- r if((AD7799_Read_STAT()&ad7799_StatERR)==0) { AD7799_ReadReg(ADC_REG_DATA,Cmd,3);2 G$ S) V/ w8 E2 X* a' P0 s' | D=(Cmd[0]<<16)+(Cmd[1]<<8)+Cmd[2]; return D; }4 F% P+ T% U% z8 t6 F0 E6 v4 [ else4 b* P: B" t' `4 q { AD7799_ReadReg(ADC_REG_DATA,Cmd,4); return 0xFFFFFF; }, u0 ]+ f ?- v9 ^8 M. n/ T9 y : Z" e% T5 C& l) d2 v$ C } void ADt7799test() { u32 Val; u16 a;8 F; {7 q% u6 `: N! _ AD7799_Init(ADC_CON_GAIN1);9 T. F& m, ^/ a1 k2 t2 Z+ q AD7799_Start(ADC_CON_CH1,ADC_CON_GAIN1,0x01,ADC_MODE_CONTINUOUS); ; t t9 U9 m) k/ m1 ^9 x AD7799_Read();2 |; W2 V; s* F6 a: o$ C AD7799_Read();1 v0 E& g! m3 I% I AD7799_Read();6 u, u6 Y* @( D. ^$ _: u; q AD7799_Read();, K/ a* k$ _+ o- H AD7799_Read();- W9 P! Y T- h c% d AD7799_Read(); ( t% h" N$ C, k1 D% w } 下面说一下自己遇到的问题: 一:增益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(-)配置寄存器单双极性选择 6 c5 ]! v0 k8 J. Q |
实战经验 | 基于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库
微信公众号
手机版