最近做了一個項目用到AD7799,參考別人的博客完成了,下面說說自己遇到的問題以及對芯片的理解,里面可能有借鑒的地方也有自己的補充,在這里對原文的博主表示感謝。
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:
下面的代碼是完全調試通過的:
#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))
#define ADC_CON_CH3 ((u8)(0x02))
#define ADC_CON_AVDD ((u8)(0x07))
#define ADC_CON_GAIN1 ((u8)(0x00))
#define ADC_CON_GAIN2 ((u8)(0x01))
#define ADC_CON_GAIN3 ((u8)(0x02))
#define ADC_CON_GAIN4 ((u8)(0x03))
#define ADC_CON_GAIN5 ((u8)(0x04))
#define ADC_CON_GAIN6 ((u8)(0x05))
#define ADC_CON_GAIN7 ((u8)(0x06))
#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))
#define ad7799_StatERR ((u8)(1<<6))
#define ad7799_StatNOR ((u8)(1<<5))
void SPI_Config(void)
{
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
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;
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 */
SPI_Cmd(SPI2, DISABLE); //必須先禁能,才能改變MODE
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //兩線全雙工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8位
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; //軟件NSS
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //2分頻
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位在前
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC7
SPI_Init(SPI2, &SPI_InitStructure);
SPI_Cmd(SPI2, ENABLE);
}
u8 SPIByte(u8 byte)
{
/*等待發送寄存器空*/
while((SPI2->SR & SPI_I2S_FLAG_TXE)==RESET);
/*發送一個字節*/
SPI2->DR = byte;
/* 等待接收寄存器有效*/
while((SPI2->SR & SPI_I2S_FLAG_RXNE)==RESET);
return(SPI2->DR);
}
/*---------------------------------------------------------
Func: AD7799讀取寄存器數據
Time: 2012-3-29
Ver.: V1.0
Note:
---------------------------------------------------------*/
void AD7799_ReadReg(u8 RegAddr,u8 *Buffer,u8 Length)
{
u8 i;
ADC_SPI_CS_CLR;
RegAddr|=ADC_OP_READ;
//ADC_WriteBytes(&RegAddr,1);
//ADC_ReadBytes(Buffer,Length);
SPIByte(RegAddr);
for(i=0;i<Length;i++)
{
Buffer[i]=SPIByte(Buffer[i]);
}
ADC_SPI_CS_SET;
}
/*---------------------------------------------------------
Func: AD7799寫入寄存器數據
Time: 2012-3-29
Ver.: V1.0
Note:
---------------------------------------------------------*/
void AD7799_WriteReg(u8 RegAddr,u8 *Buffer,u8 Length)
{
u8 i;
ADC_SPI_CS_CLR;
RegAddr|=ADC_OP_WRITE;
//ADC_WriteBytes(&RegAddr,1);
//ADC_WriteBytes(Buffer,Length);
SPIByte(RegAddr);
for(i=0;i<Length;i++)
{
SPIByte(Buffer[i]);
}
ADC_SPI_CS_SET;
}
/*---------------------------------------------------------
Func: AD7799忙判斷
Time: 2012-3-29
Ver.: V1.0
Note: 0/OK >0/ERROR,timeout
---------------------------------------------------------*/
u8 AD7799_WaitBusy()
{
u16 i;
ADC_SPI_CS_CLR;
i=0;
while(ADC_RDY_DAT>0)
{
i++;
if(i>5000)
return 1;
}
ADC_SPI_CS_SET;
return 0;
}
/*---------------------------------------------------------
Func: AD7799通道內部校准
Time: 2012-3-29
Ver.: V1.0
Note: 0/OK >0/Error
---------------------------------------------------------*/
u8 AD7799_Calibrate(u8 CHx,u8 Gain)
{
u8 R,Cmd[2];
Cmd[0]=0x30|Gain; //0x10代表配置寄存器高八位中單雙極性位置1
Cmd[1]=0x30|CHx;
AD7799_WriteReg(ADC_REG_CONFIG,Cmd,2); //設置配置寄存器
Cmd[0]=0x80;
Cmd[1]=0x0F;
AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //進行內部零度校准
R|=AD7799_WaitBusy(); //等待校准完成
Cmd[0]=0xA0;
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(); //等待校准完成
Cmd[0]=0xE0;
Cmd[1]=0x0F;
AD7799_WriteReg(ADC_REG_MODE,Cmd,2); //進行系統滿度校准
R|=AD7799_WaitBusy(); //等待校准完成 */
return R;
}
u8 AD7799_Read_STAT(void)
{
u8 Cmd[2];
AD7799_ReadReg(ADC_REG_STAT,Cmd,1);
return Cmd[0];
}
u16 AD7799_Read_CONFIH(void)
{
u8 Cmd[2];
AD7799_ReadReg(ADC_REG_CONFIG,Cmd,2);
return (Cmd[0]<<8)+Cmd[1];
}
/*---------------------------------------------------------
Func: AD7799復位
Time: 2012-3-29
Ver.: V1.0
Note: 0/OK >0/Error
---------------------------------------------------------*/
void AD7799_Reset()
{
// u8 Cmd[4]={0xFF,0xFF,0xFF,0xFF};
ADC_SPI_CS_CLR;
// ADC_WriteBytes(Cmd,4);
SPIByte(0xFF);
SPIByte(0xFF);
SPIByte(0xFF);
SPIByte(0xFF);
ADC_SPI_CS_SET;
}
/*---------------------------------------------------------
Func: AD7799初始化
Time: 2012-3-29
Ver.: V1.0
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校准
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);
return 0;
}
/*---------------------------------------------------------
Func: AD7799開始轉換
Time: 2012-3-29
Ver.: V1.0
Note:
---------------------------------------------------------*/
void AD7799_Start(u8 CovChx,u8 CovGain,u8 CovRate,u8 CovMode)
{
u8 Cmd[2];
Cmd[0]=0x30|CovGain;
Cmd[1]=0x30|CovChx; //0x30
AD7799_WriteReg(ADC_REG_CONFIG,Cmd,2);
Cmd[0]=0x10|CovMode;
Cmd[1]=CovRate;
AD7799_WriteReg(ADC_REG_MODE,Cmd,2);
}
/*---------------------------------------------------------
Func: AD7799讀取轉換結果
Time: 2012-3-29
Ver.: V1.0
Note:
---------------------------------------------------------*/
u32 AD7799_Read()
{
u8 Cmd[4];
u16 i=0;
u32 D;
Cmd[0]=0;
while((AD7799_Read_STAT()&ad7799_StatRDY)!=0)
{
i++;
if(i>10000)
break;
}
if((AD7799_Read_STAT()&ad7799_StatERR)==0)
{
AD7799_ReadReg(ADC_REG_DATA,Cmd,3);
D=(Cmd[0]<<16)+(Cmd[1]<<8)+Cmd[2];
return D;
}
else
{
AD7799_ReadReg(ADC_REG_DATA,Cmd,4);
return 0xFFFFFF;
}
}
void ADt7799test()
{
u32 Val;
u16 a;
AD7799_Init(ADC_CON_GAIN1);
AD7799_Start(ADC_CON_CH1,ADC_CON_GAIN1,0x01,ADC_MODE_CONTINUOUS);
AD7799_Read();
AD7799_Read();
AD7799_Read();
AD7799_Read();
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(-)配置寄存器單雙極性選擇