FDC2214——電容感測器晶片的使用與配置(STM32控制)
這裡介紹Ti公司的一款低功耗高精度的電容感測器晶片FDC2214,這裡我們主要講的是其簡單配置及其使用。
以下大多數的圖片都來自於FDC2214的晶片手冊,本人只是用來講解,不做它用。(如有更多需要,自行前往Ti官網進行下載)
首先我們先來看一下晶片的主要特性:
從晶片手冊上可知,其供電電壓為2.7V到3.6V,諧振頻率從10kHz to 10MHz,資料位數為28位,即精度為1/2^28,FDC2214有4個採集通道,其與MCU的通訊方式為IIC。
這裡我們用軟體IIC來實現與FDC2214的通訊。
其次我們來看一下晶片的引腳圖:
通過晶片手冊我們可知寫時序為:
讀時序為:
讀完整個晶片手冊我們發現,其從機的地址說的並不是很清楚,這裡通過測試發現其從機地址(FDC2214為從機)如下
當ADDR為低時:0x2A,這裡二進位制表示為0010 1010,這裡我們需要移除最高位,即為010 1010_,這裡當最後一位為低時表示向FDC2214寫入資料,為高時,是讀出資料。
及ADDR = L:0x54為寫,0x55為讀;
同理當ADDR = H:0x56為寫,0x57為讀;
即通訊的原理框圖如下(以ADDR為低為例):
(1) 寫時序:
(2)讀時序:
瞭解完時序之後,我們就可以開始進行暫存器的配置,這裡晶片手冊上已經給出該怎麼配置,如下圖:
這裡不細說每個暫存器是幹什麼的,如有需要,可以參照晶片手冊,這裡,我們需要改一個暫存器的配置,就是地址為0x1A的暫存器,
該暫存器的值如下:
這裡我們需要配置第9位,將其配置為0,使用內部晶振,那麼0x1A暫存器的值改為 0x1401。
這裡我們開始寫程式,
這裡IIC的程式,我們使用的是正點原子的庫,其庫函式如下:
//IIC void IIC_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); //PB6,PB7 } // void IIC_Start(void) { SDA_OUT(); IIC_SDA=1; IIC_SCL=1; delay_us(4); IIC_SDA=0;//START:when CLK is high,DATA change form high to low delay_us(4); IIC_SCL=0; } // void IIC_Stop(void) { SDA_OUT(); IIC_SCL=0; IIC_SDA=0;//STOP:when CLK is high DATA change form low to high delay_us(4); IIC_SCL=1; IIC_SDA=1; delay_us(4); } u8 IIC_Wait_Ack(void) { u8 ucErrTime=0; SDA_IN(); IIC_SDA=1;delay_us(1); IIC_SCL=1;delay_us(1); while(READ_SDA) { ucErrTime++; if(ucErrTime>250) { IIC_Stop(); return 1; } } IIC_SCL=0; return 0; } //Set up ACK void IIC_Ack(void) { IIC_SCL=0; SDA_OUT(); IIC_SDA=0; delay_us(2); IIC_SCL=1; delay_us(2); IIC_SCL=0; } //Set up NACK(NO ACK) void IIC_NAck(void) { IIC_SCL=0; SDA_OUT(); IIC_SDA=1; delay_us(2); IIC_SCL=1; delay_us(2); IIC_SCL=0; } //IIC Send Byte void IIC_Send_Byte(u8 txd) { u8 t; SDA_OUT(); IIC_SCL=0; for(t=0;t<8;t++) { //IIC_SDA=(txd&0x80)>>7; if((txd&0x80)>>7) IIC_SDA=1; else IIC_SDA=0; txd<<=1; delay_us(2); IIC_SCL=1; delay_us(2); IIC_SCL=0; delay_us(2); } } //ack = 0 send Nack ack = 1 send ACK u8 IIC_Read_Byte(unsigned char ack) { unsigned char i,receive=0; SDA_IN(); for(i=0;i<8;i++ ) { IIC_SCL=0; delay_us(2); IIC_SCL=1; receive<<=1; if(READ_SDA)receive++; delay_us(1); } if (!ack) IIC_NAck(); else IIC_Ack(); return receive; }
void IIC_Init(void)函式是初始化 SCL,SDA兩個引腳;
void IIC_Start(void) 函式是產生START訊號;
void IIC_Stop(void)函式是產生STOP訊號;
u8 IIC_Wait_Ack(void)函式是接收從機發送的ACK;
void IIC_Ack(void)函式是主機讀完資料後產生應答訊號;
void IIC_NAck(void) 函式是主機讀完資料後不產生應答訊號;
void IIC_Send_Byte(u8 txd) 函式是傳送一個位元組的資料;
u8 IIC_Read_Byte(unsigned char ack)函式是接收一個位元組的資料;
這裡我們還需要配置與FDC2214通訊的函式,向FDC2214暫存器寫的函式為
//這裡 FDC_Address_W =0x54
void FDC_write_reg(u8 addr,u16 value) //addr 為暫存器地址,value為需要寫入的暫存器資料
{
IIC_Start(); //產生START訊號
IIC_Send_Byte(FDC_Address_W); //傳送從機地址和寫訊號
IIC_Wait_Ack(); //等待ACK
IIC_Send_Byte(addr); //傳送需要寫入的暫存器地址
IIC_Wait_Ack(); //等待ACK
IIC_Send_Byte(value>>8); //傳送高8位資料
IIC_Wait_Ack(); //等待ACK
IIC_Send_Byte(value&0xFF); //傳送低8位資料
IIC_Wait_Ack(); //等待ACK
IIC_Stop(); //產生STOP訊號
delay_ms(1);
}
從FDC2214讀16位資料的函式如下:
//FDC_Address_W =0x54
//FDC_Address_R =0x55
//Receive_Date[] 為接收資料的陣列
//C_Data 為一個16位無符號的整型
u16 FDC_read_reg(u8 addr)
{
IIC_Start(); //產生START訊號
IIC_Send_Byte(FDC_Address_W); //傳送寫命令
IIC_Wait_Ack();
IIC_Send_Byte(addr); //傳送需要讀的暫存器的地址
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(FDC_Address_R); //傳送讀命令
IIC_Wait_Ack();
Receive_Date[0]=IIC_Read_Byte(1); //讀高8位
Receive_Date[1]=IIC_Read_Byte(0); //讀低8位
IIC_Stop(); //產生STOP訊號
C_Data=(Receive_Date[0]<<8)+ Receive_Date[1];
return C_Data;
}
從FDC2214讀高8位資料的函式如下:
u8 FDC_read_reg_high(u8 addr)
{
IIC_Start();
IIC_Send_Byte(FDC_Address_W);
IIC_Wait_Ack();
IIC_Send_Byte(addr);
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(FDC_Address_R);
IIC_Wait_Ack();
Receive_Date[0]=IIC_Read_Byte(1);
Receive_Date[1]=IIC_Read_Byte(0);
IIC_Stop();
return Receive_Date[0];
}
從FDC2214讀低8位資料的函式如下:
u8 FDC_read_reg_low(u8 addr)
{
IIC_Start();
IIC_Send_Byte(FDC_Address_W);
IIC_Wait_Ack();
IIC_Send_Byte(addr);
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(FDC_Address_R);
IIC_Wait_Ack();
Receive_Date[0]=IIC_Read_Byte(1);
Receive_Date[1]=IIC_Read_Byte(0);
IIC_Stop();
return Receive_Date[1];
}
FDC2214初始化的函式如下:
void FDC_Start(void)
{
FDC_write_reg(0x08,0x8329); //(CHx_RCOUNT*16)/55M ==9.76ms,,每10ms左右可以讀一次值
FDC_write_reg(0x09,0x8329);
FDC_write_reg(0x0A,0x8329);
FDC_write_reg(0x0B,0x8329);
FDC_write_reg(0x10,0x000A); //設定4個通道最小穩定時間
FDC_write_reg(0x11,0x000A);
FDC_write_reg(0x12,0x000A);
FDC_write_reg(0x13,0x000A);
FDC_write_reg(0x14,0x1001); //時鐘除以1,設定感測器頻率在0.01M到8.5M之間
FDC_write_reg(0x15,0x1001);
FDC_write_reg(0x16,0x1001);
FDC_write_reg(0x17,0x1001);
FDC_write_reg(0x19,0x0000); //不設定中斷標誌位
FDC_write_reg(0x1B,0xC20D);//使能0,1,2,3通道,且頻寬設定為10M
FDC_write_reg(0x1E,0x8000); //設定4個通道的驅動電流
FDC_write_reg(0x1F,0x8000);
FDC_write_reg(0x20,0x8000);
FDC_write_reg(0x21,0x8000);
FDC_write_reg(0x1A,0x1401); //使能FDC2214,且取內部時鐘為參考時鐘
}
其標頭檔案的配置如下:
#ifndef __FDC2214_H
#define __FDC2214_H
#include <sys.h>
//IO口方向
#define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}
//IO操作函式
#define IIC_SCL PBout(6) //SCL
#define IIC_SDA PBout(7) //SDA
#define READ_SDA PBin(7) //SDA輸入
//IIC函式
void IIC_Init(void);
void IIC_Start(void);
void IIC_Stop(void);
void IIC_Send_Byte(u8 txd);
u8 IIC_Read_Byte(unsigned char ack);
u8 IIC_Wait_Ack(void);
void IIC_Ack(void);
void IIC_NAck(void);
void FDC_write_reg(u8 addr,u16 value);
void FDC_Start(void);
u16 FDC_read_reg(u8 addr);
u8 FDC_read_reg_high(u8 addr);
u8 FDC_read_reg_low(u8 addr);
#endif
這裡為了判斷我們的程式和FDC2214是沒有問題,先讀取相應的ID,這裡有兩個ID可以供我們讀取:
這裡我們在主函式中需要讀出相應的ID,來確定FDC2214和我們IIC的程式沒有問題。
其主函式如下:
int main()
{
u8 lcd_id[12];
u16 C_date=0;
u16 ID=0;
u16 C_CH0_data=0;
u16 C_CH0_data_low=0;
u32 C_CHO_data_final=0;
u16 C_CH1_data=0;
u16 C_CH1_data_low=0;
u32 C_CH1_data_final=0;
u16 C_CH2_data=0;
u16 C_CH2_data_low=0;
u32 C_CH2_data_final=0;
u16 C_CH3_data=0;
u16 C_CH3_data_low=0;
u32 C_CH3_data_final=0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init(); //初始化延時函式
LCD_Init(); //初始化LCD顯示
uart_init(115200); //初始化串列埠
IIC_Init(); //初始化 IIC
POINT_COLOR=BLUE;
sprintf((char*)lcd_id,"LCD ID:%04X",lcddev.id);
LCD_Clear(WHITE);
POINT_COLOR=BLUE;
LCD_ShowString(30,70,240,16,24,"C_Chan_0:");
LCD_ShowString(30,100,240,16,24,"C_Chan_1:");
LCD_ShowString(30,130,240,16,24,"C_Chan_2:");
LCD_ShowString(30,160,240,16,24,"C_Chan_3:");
LCD_ShowString(30,190,240,16,24,"DRDY:");
delay_ms(10);
FDC_Start(); //FDC初始化
while(1)
{
LCD_ShowString(30,40,240,16,24,"Successful!");
delay_ms(500);
C_date = FDC_read_reg(0x7E); //讀取ID
ID = FDC_read_reg(0x7F); //讀取ID
if(C_date!=0xFF00 && ID!=0xFF00) //顯示ID
{
LCD_ShowxNum(139,350,ID,5,24,0);
LCD_ShowxNum(139,380,C_date,5,24,0);
}
delay_ms(100);
}
}
FDC2214的數值是28位的,這裡我們需要讀兩個暫存器的值(以CH0為例)分別是0x00,和0x0A:
從晶片手冊可以知道(以CH0為例),0x01暫存器中為資料的低16位,0x00暫存器為資料的高12位,將兩個資料合起來,即為相應的資料,其程式如下:
if(C_date==0x5449 && ID==0x3055) //當為相應ID時才讀取暫存器的值
{
//讀通道0數值
C_CH0_data = FDC_read_reg(0x00)&0xFFF;
C_CH0_data_low = FDC_read_reg(0x01);
C_CHO_data_final =((C_CH0_data<<16)+C_CH0_data_low);
//讀通道1數值
delay_ms(50);
C_CH1_data = FDC_read_reg(0x02)&0xFFF;
C_CH1_data_low = FDC_read_reg(0x03);
C_CH1_data_final =((C_CH1_data<<16)+C_CH1_data_low);
//讀通道2數值
delay_ms(50);
C_CH2_data=FDC_read_reg(0x04)&0xFFF;
C_CH2_data_low = FDC_read_reg(0x05);
C_CH2_data_final =((C_CH2_data<<16)+C_CH2_data_low);
//讀通道3數值
delay_ms(50);
C_CH3_data=FDC_read_reg(0x06)&0xFFF;
C_CH3_data_low = FDC_read_reg(0x07);
C_CH3_data_final =((C_CH3_data<<16)+C_CH3_data_low);
//顯示相應通道讀出來的值
LCD_ShowxNum(139,70,C_CHO_data_final,9,24,0);
LCD_ShowxNum(139,100,C_CH1_data_final,9,24,0);
LCD_ShowxNum(139,130,C_CH2_data_final,9,24,0);
LCD_ShowxNum(139,160,C_CH3_data_final,9,24,0);
}
讀取完數值之後,其電容的計算公式以可在晶片手冊找到,如下:
這裡我們可以知道其電容的計算就是根據LC諧振頻率的變化來計算。
這裡,CHx_FIN_SEL和f_REFx可以從下圖得知,DATAx即為我們讀出的數值,這裡就可以算出電容值。
這裡我們如果發現ID讀得不對,可以將SCL和SDA通過電阻(本人用的4.7K)上拉,這樣就可以讀取正確的ID值。