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: 4 d0 P2 T- a; p; z7 L5 x 下面的代码是完全调试通过的: #define ADC_SPI_CS_CLR GPIOB->BSRR=GPIO_Pin_124 K! ^2 ?. H. `0 J8 _1 p2 e #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)) 1 n( o: Q- }3 Q #define ADC_REG_STAT ((u8)(0x00<<3))1 B3 L0 a0 X1 `0 y #define ADC_REG_MODE ((u8)(0x01<<3)) #define ADC_REG_CONFIG ((u8)(0x02<<3))+ X8 Y( r% e% H( }. K #define ADC_REG_DATA ((u8)(0x03<<3)) #define ADC_REG_ID ((u8)(0x04<<3))$ F7 `, ?/ ?# T! t7 G #define ADC_REG_IO ((u8)(0x05<<3)) #define ADC_REG_OFFSET ((u8)(0x06<<3))2 v z S8 o0 R3 S1 O; I; _ //#define ADC_REG_STAT ((unsigned char)(0x07)) ) V+ Q7 W- \) M" w #define ADC_CON_CH1 ((u8)(0x00)) #define ADC_CON_CH2 ((u8)(0x01))3 s; S0 y! C# g& s5 M #define ADC_CON_CH3 ((u8)(0x02)) #define ADC_CON_AVDD ((u8)(0x07)) #define ADC_CON_GAIN1 ((u8)(0x00)) #define ADC_CON_GAIN2 ((u8)(0x01))+ i5 Q+ G1 V- u' N #define ADC_CON_GAIN3 ((u8)(0x02))9 G( N' X# z% o. v #define ADC_CON_GAIN4 ((u8)(0x03))" f4 v3 x% u' T$ V' t. E #define ADC_CON_GAIN5 ((u8)(0x04))1 f/ ^, u9 x- a7 ]) H% e( F: V* T #define ADC_CON_GAIN6 ((u8)(0x05))- s% P1 |& q1 ~1 R4 m0 _ C #define ADC_CON_GAIN7 ((u8)(0x06))0 l' X* o) y- V) t3 s- E #define ADC_CON_GAIN8 ((u8)(0x07)) #define ADC_SINGLE_POLAR ((u8)(1<<4)) //单双极性* ]! Y# _3 j3 H! u #define ADC_DOUBLE_POLAR ((u8)(0<<4)) #define ADC_MODE_CONTINUOUS ((u8)(0<<5))- I, D$ C- ~% O9 V- X8 Q #define ADC_MODE_ONCE ((u8)(1<<5)) #define ad7799_StatRDY ((u8)(1<<7))! k2 N5 W% S0 e- `4 N2 f1 j' R7 E #define ad7799_StatERR ((u8)(1<<6)) #define ad7799_StatNOR ((u8)(1<<5)) void SPI_Config(void) {2 o& m2 ~6 `) ^2 a6 m" |0 f8 B! q SPI_InitTypeDef SPI_InitStructure;% w% p9 f4 D2 g) g1 K GPIO_InitTypeDef GPIO_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);9 z+ o _6 r3 f7 d7 e' D; F 8 \1 H: W: X$ S* ~, v; Q. s F9 F GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;* N( Z5 g! r9 a! S GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;. D9 ]' H* k8 N GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;3 c4 _2 M6 s+ n! c- g GPIO_Init(GPIOB, &GPIO_InitStructure); ! B! I, t9 q7 U, \ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 |GPIO_Pin_14|GPIO_Pin_15;6 p1 A! F2 A+ L/ p' H& S GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;7 h: r5 L& U. Z7 U GPIO_Init(GPIOB, &GPIO_InitStructure); /* SPI2 configuration */ SPI_Cmd(SPI2, DISABLE); //必须先禁能,才能改变MODE4 K; S. T# f/ @9 o8 C# k SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //两线全双工7 X2 x ]: o: S I SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主0 x2 f; s2 E1 `. E9 V9 c1 Y SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8位 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //CPOL=1 时钟悬空高! V0 U1 S! T+ ]( M SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //CPHA=1 数据捕获第2个 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //软件NSS& X0 |" \6 \) D1 E% W SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //2分频7 X8 Z2 o: w6 J1 n. ^. p; u" }2 N SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位在前 SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC7" Z8 w6 f" B5 M( K/ m SPI_Init(SPI2, &SPI_InitStructure); SPI_Cmd(SPI2, ENABLE); & y1 V5 ?% p( [+ [ } u8 SPIByte(u8 byte) { /*等待发送寄存器空*/" r# `( @# E7 d5 b' Z0 h while((SPI2->SR & SPI_I2S_FLAG_TXE)==RESET);: n3 L: I$ ?& I; N4 R /*发送一个字节*/ SPI2->DR = byte;4 ?2 u: K* f( e2 M4 J( ?+ M6 a i /* 等待接收寄存器有效*/# _& o; e' Y! x; \# C! V! ~ while((SPI2->SR & SPI_I2S_FLAG_RXNE)==RESET); return(SPI2->DR);) r' O; \; Y- x6 H+ s: X% X0 ?% ^ } /*---------------------------------------------------------7 v' G9 b7 d& q; r& D; j Func: AD7799读取寄存器数据7 c; l+ j& C2 B& I+ ~ Time: 2012-3-29 Ver.: V1.0 Note:! P; D5 u: G5 O ---------------------------------------------------------*/ void AD7799_ReadReg(u8 RegAddr,u8 *Buffer,u8 Length): q) L4 E* u. ?1 L { u8 i; m3 M9 q& c, w' g5 Q8 ^, P ADC_SPI_CS_CLR; RegAddr|=ADC_OP_READ;" x( b; L/ [- a //ADC_WriteBytes(&RegAddr,1);, F$ v; T# N) Z, m. k. X6 m //ADC_ReadBytes(Buffer,Length); SPIByte(RegAddr); for(i=0;i<Length;i++) {" Q5 u" X7 T2 e! D+ [8 K+ b1 \* p Buffer=SPIByte(Buffer);/ N* b# I3 M! f$ h }' l$ r7 C. c6 c( F- M8 S ADC_SPI_CS_SET;* t" |5 _8 `* Q, ]- M } ! l8 _1 y. q6 V, i /*---------------------------------------------------------1 D7 K1 r: q \: z# q" b, K3 f1 w( k Func: AD7799写入寄存器数据' b. ^, \+ O& y7 F( u D( B Time: 2012-3-29 Ver.: V1.0 Note:; E/ j0 u3 B: @( ` ---------------------------------------------------------*/4 K% K, p4 T9 X9 T; B. W. z% m void AD7799_WriteReg(u8 RegAddr,u8 *Buffer,u8 Length)8 T+ m* S+ v1 p) K" f { u8 i; ADC_SPI_CS_CLR;2 R* L) e0 p# N" \. c% p RegAddr|=ADC_OP_WRITE;8 \2 Y0 O6 B0 Y% G4 G2 c+ @ //ADC_WriteBytes(&RegAddr,1);+ o3 u( g0 U) P, R: g1 A- C& [ //ADC_WriteBytes(Buffer,Length);/ |% L4 |/ T" h: [6 j SPIByte(RegAddr); for(i=0;i<Length;i++) {% i0 H% b9 b$ E3 s; C SPIByte(Buffer); }- q5 f9 X, H p. o( W ADC_SPI_CS_SET; }& ^ h- [/ T% `" y /*---------------------------------------------------------5 [8 K* m. `. m! \ Func: AD7799忙判断 Time: 2012-3-29" L3 c" D, p& c) f/ J6 Y" |; Z) K5 a: ^ Ver.: V1.0 Note: 0/OK >0/ERROR,timeout ---------------------------------------------------------*/ u8 AD7799_WaitBusy() {1 O5 q! q, T) G9 ? X9 E5 k T1 s0 P u16 i; ADC_SPI_CS_CLR; i=0; while(ADC_RDY_DAT>0). C) R( y, k+ \4 X; D& E# y2 `% b {! n5 k" U5 T$ u; J; y5 ^, K6 l i++; if(i>5000) return 1; }7 P" q- Y$ i6 h( Z# w ADC_SPI_CS_SET; return 0; } /*---------------------------------------------------------3 s! q q8 K3 i2 i% Y: } Func: AD7799通道内部校准# n. Z6 u9 N- H- E1 Z* _! @ Time: 2012-3-29 Ver.: V1.0 Note: 0/OK >0/Error2 ?6 m- i1 Z _! ^ ---------------------------------------------------------*/ u8 AD7799_Calibrate(u8 CHx,u8 Gain) {" R( D- X. J1 ?3 V u8 R,Cmd[2];3 x, u- W* J8 x* B Cmd[0]=0x30|Gain; //0x10代表配置寄存器高八位中单双极性位置1 Cmd[1]=0x30|CHx;( g2 \6 C e3 ~/ e( z) x AD7799_WriteReg(ADC_REG_CONFIG,Cmd,2); //设置配置寄存器 Cmd[0]=0x80; Cmd[1]=0x0F; AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行内部零度校准 R|=AD7799_WaitBusy(); //等待校准完成) F8 k# h3 d! K" K& N0 D* ~ Cmd[0]=0xA0; Cmd[1]=0x0F;8 c( h7 G/ O# I/ p) O0 w* C AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行内部满肚校准 R|=AD7799_WaitBusy(); //等待校准完成 /* Cmd[0]=0xC0; Cmd[1]=0x0F;# S V; V5 d5 O( M# n AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行系统零度校准& j; [; z( X9 i; ]' N b5 o R|=AD7799_WaitBusy(); //等待校准完成 Cmd[0]=0xE0;6 @/ |& O! m) _7 r# o% ? Cmd[1]=0x0F; AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //进行系统满度校准 R|=AD7799_WaitBusy(); //等待校准完成 */ return R;' e$ E2 K3 E- i+ C+ m, b4 Q } u8 AD7799_Read_STAT(void)+ o# X9 N2 S$ D5 r. o { u8 Cmd[2];4 t& G, B" e3 G AD7799_ReadReg(ADC_REG_STAT,Cmd,1); return Cmd[0]; } u16 AD7799_Read_CONFIH(void)5 `! g- E, v- q; F7 A9 t { u8 Cmd[2]; AD7799_ReadReg(ADC_REG_CONFIG,Cmd,2); return (Cmd[0]<<8)+Cmd[1]; }0 D& V4 _" p5 ~$ y /*--------------------------------------------------------- Func: AD7799复位 Time: 2012-3-29 Ver.: V1.0, _0 D* \% n3 b+ ? Note: 0/OK >0/Error) A9 t, L& g$ x7 \' n( O ---------------------------------------------------------*/ void AD7799_Reset()$ C2 |2 |$ B8 ]" ~3 t7 d( {* \; M {4 ^" V( e ~$ y) O) _ // u8 Cmd[4]={0xFF,0xFF,0xFF,0xFF};; d% V* G0 L0 T# L ADC_SPI_CS_CLR; // ADC_WriteBytes(Cmd,4); SPIByte(0xFF); SPIByte(0xFF); SPIByte(0xFF);6 T7 ]6 f, h* }( ]8 d9 o SPIByte(0xFF); ADC_SPI_CS_SET;7 B: g9 ^* F' {; G( R }! y" l4 {9 X5 |/ T1 E /*--------------------------------------------------------- Func: AD7799初始化 Time: 2012-3-29 Ver.: V1.0 Note: 0/OK >0/Error ---------------------------------------------------------*/% v5 l7 N D8 i0 v T, e' \ 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校准 AD7799_Calibrate(ADC_CON_CH2,Gain); //通道2校准( G) [/ ]' H# V F5 r* ~/ k //AD7799_Calibrate(ADC_CON_CH3,Gain); //通道3校准, z# R6 B% J6 u8 N9 }7 b. Y) @+ Q' y // Cmd[0]=0<<6;' @& E7 [1 P6 |) { // AD7799_WriteReg(ADC_REG_IO,Cmd,1); return 0; } /*---------------------------------------------------------. U/ C3 O9 D5 f Func: AD7799开始转换 Time: 2012-3-29 Ver.: V1.0 e/ W% D, [; E: K- k% P, ~# l Note: ---------------------------------------------------------*/ m* D* `# x7 c. S9 a% F2 L9 J. V void AD7799_Start(u8 CovChx,u8 CovGain,u8 CovRate,u8 CovMode)/ ?! u R( F, ^' B8 h8 a0 h6 J { u8 Cmd[2];4 t4 k, n' t! b* h1 o! P Cmd[0]=0x30|CovGain; Cmd[1]=0x30|CovChx; //0x302 O+ k2 @1 j; z v4 b# l6 I AD7799_WriteReg(ADC_REG_CONFIG,Cmd,2);" T( I% j2 g N+ [8 c/ }" X Cmd[0]=0x10|CovMode; Cmd[1]=CovRate; AD7799_WriteReg(ADC_REG_MODE,Cmd,2); } /*--------------------------------------------------------- }( f4 [5 M* `4 _; Q( Q Func: AD7799读取转换结果0 W- x+ J) [: b6 B; C) e1 ]% b+ Q Time: 2012-3-29- U; R+ R2 E) J5 D+ P# [4 z3 y* i Ver.: V1.0' u n& ~( z y: u$ q, ~# t Note:/ r/ H M" b- p2 ?# G, i ---------------------------------------------------------*/; l( N6 R( q! o* E- y G u32 AD7799_Read()# w5 w& B7 N/ J { u8 Cmd[4]; u16 i=0;2 S. r5 g0 U! |0 ]$ W u32 D; Cmd[0]=0; while((AD7799_Read_STAT()&ad7799_StatRDY)!=0) { i++;0 j H* e7 S$ y! k% Y if(i>10000) break;* c5 q9 t2 H. y5 ~" X }7 y" M$ S t7 v if((AD7799_Read_STAT()&ad7799_StatERR)==0)& s4 Q3 }: x& z8 m { AD7799_ReadReg(ADC_REG_DATA,Cmd,3); D=(Cmd[0]<<16)+(Cmd[1]<<8)+Cmd[2];% C, r/ x1 y! E! C$ I return D; } else F: y5 J6 y0 T- k3 r { AD7799_ReadReg(ADC_REG_DATA,Cmd,4); return 0xFFFFFF; } } void ADt7799test() { u32 Val;1 w, F/ z+ k( Z+ q9 P u16 a; AD7799_Init(ADC_CON_GAIN1);3 s$ z$ P. `6 |! s7 X& ^ AD7799_Start(ADC_CON_CH1,ADC_CON_GAIN1,0x01,ADC_MODE_CONTINUOUS); % G% Y. ~# c; Y2 h# T6 Y" G; z AD7799_Read();9 Q& u( o- b; J( V: {% H- c& O AD7799_Read();! b( n. C+ k7 g8 m p3 i. d; e AD7799_Read();" b) }4 a5 ~" k AD7799_Read();! l% n, |% u/ Q1 h) } AD7799_Read();# g6 Q# c; V @' ?' E AD7799_Read(); 1 y b/ ~2 ]- j9 X! D1 B4 }& M 5 ?& i# _6 M+ |) P4 l} 下面说一下自己遇到的问题: 一:增益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(-)配置寄存器单双极性选择 |
实战经验 | 基于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库
微信公众号
手机版