(一)我的感受
其实第一次接触这一个题目的时候,觉得还是不难的,(因为相对于我实验室的同学做的其他的一些控制类题目)。我觉得在代码方面完成题目要求的不难,就是在机械结构方面要做好,我做的装置机械机构不算是很好,就是两块极板压下去不是完全对准的,比较容易产生误差,而且压下去的东西还是一个大电机。下面附图。
进入主题啦!!!ヾ(^ ▽ ^*)))
FDC2214的使用
电容式传感是一种低功耗、低成本且高分辨率的非接触式感测技术, 适用于从接近检测、手势识别到远程液位感测的各项应用。电容式传感系统中的传感器可以采用任意金属或导体,因此可实现高度灵活的低成本系统设计。
FDC2214寄存器的地址
我觉得这个寄存器部分是最重要的部分。(我在写代码时经常在这里犯病)
还有就是FDC2214是用IIC通信的,你可以直接使用正点原子的例程可以的。
FDC写操作
```c
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);
}
FDC读操作
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读高八位和第八位的操作
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];
}
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,且取内部时钟为参考时钟
}
还有源文件包括的变量
int FDC_Address_W =0x54;
int FDC_Address_R =0x55;
int Receive_Date[10000] ;//为接收数据的数组;
u16 C_Data; //为一个16位无符号的整型
头文件
#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,看一下这个文章,fdc2214使用注意事项
还有就是上面是我在精英板用的初始化,下面还有一种对FDC2214的初始化,如果上面的初始化不能用的话就直接用下面的初始化(大同小异,只是一些地方的读写地址是分开两部分)。
void FDC_IIC_Delay(void)
{
delay_us(2);
}
void FDC_IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//先使能外设IO PORTC时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;//端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
//GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIO
GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); //PC.4,PC.5 输出高
}
//产生IIC起始信号
void FDC_IIC_Start(void)
{
FDC_SDA_OUT(); //sda线输出
FDC_IIC_SDA=1;
FDC_IIC_SCL=1;
FDC_IIC_Delay();
FDC_IIC_SDA=0;//START:when CLK is high,DATA change form high to low
FDC_IIC_Delay();
FDC_IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void FDC_IIC_Stop(void)
{
FDC_SDA_OUT();//sda线输出
FDC_IIC_SCL=0;
FDC_IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
FDC_IIC_Delay();
FDC_IIC_SCL=1;
FDC_IIC_SDA=1;//发送I2C总线结束信号
FDC_IIC_Delay();
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
u8 FDC_IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
FDC_SDA_IN(); //SDA设置为输入
FDC_IIC_SDA=1;FDC_IIC_Delay();
FDC_IIC_SCL=1;FDC_IIC_Delay();
while(FDC_READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
FDC_IIC_Stop();
return 1;
}
}
FDC_IIC_SCL=0;//时钟输出0
return 0;
}
//产生ACK应答
//void FDC_IIC_Ack(void)
//{
// FDC_IIC_SCL=0;
// FDC_SDA_OUT();
// FDC_IIC_SDA=0;
// FDC_IIC_Delay();
// FDC_IIC_SCL=1;
// FDC_IIC_Delay();
// FDC_IIC_SCL=0;
//}
//debug
void FDC_IIC_Ack(void)
{
FDC_IIC_SCL=0;
FDC_IIC_SDA=0;
FDC_IIC_Delay();
FDC_IIC_SCL=1;
FDC_IIC_Delay();
FDC_IIC_SCL=0;
FDC_IIC_Delay();
FDC_IIC_SDA=1;
}
//不产生ACK应答
void FDC_IIC_NAck(void)
{
FDC_IIC_SCL=0;
FDC_SDA_OUT();
FDC_IIC_SDA=1;
FDC_IIC_Delay();
FDC_IIC_SCL=1;
FDC_IIC_Delay();
FDC_IIC_SCL=0;
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void FDC_IIC_Send_Byte(u8 txd)
{
u8 t;
FDC_SDA_OUT();
FDC_IIC_SCL=0;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
FDC_IIC_SDA=(txd&0x80)>>7;
txd<<=1;
FDC_IIC_SCL=1;
FDC_IIC_Delay();
FDC_IIC_SCL=0;
FDC_IIC_Delay();
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 FDC_IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
FDC_SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
FDC_IIC_SCL=0;
FDC_IIC_Delay();
FDC_IIC_SCL=1;
receive<<=1;
if(FDC_READ_SDA)receive++;
FDC_IIC_Delay();
}
if (!ack)
FDC_IIC_NAck();//发送nACK
else
FDC_IIC_Ack(); //发送ACK
return receive;
}
u8 Set_FDC2214(u8 reg,u8 MSB,u8 LSB)
{
FDC_IIC_Start();
FDC_IIC_Send_Byte((FDC2214_ADDR<<1)|0);//发送器件地址+写命令
if(FDC_IIC_Wait_Ack()) //等待应答
{
FDC_IIC_Stop();
return 1;
}
FDC_IIC_Send_Byte(reg);//写寄存器地址
FDC_IIC_Wait_Ack(); //等待应答
FDC_IIC_Send_Byte(MSB); //发送数据1
if(FDC_IIC_Wait_Ack()) //等待ACK
{
FDC_IIC_Stop();
return 1;
}
FDC_IIC_Send_Byte(LSB); //发送数据2
if(FDC_IIC_Wait_Ack()) //等待ACK
{
FDC_IIC_Stop();
return 1;
}
FDC_IIC_Stop();
return 0;
}
u16 FDC_Read(u8 reg)
{
u16 res;
FDC_IIC_Start();
FDC_IIC_Send_Byte((FDC2214_ADDR<<1)|0);//发送器件地址+写命令
FDC_IIC_Wait_Ack(); //等待应答
FDC_IIC_Send_Byte(reg); //写寄存器地址
FDC_IIC_Wait_Ack(); //等待应答
FDC_IIC_Start();
FDC_IIC_Send_Byte((FDC2214_ADDR<<1)|1);//发送器件地址+读命令
FDC_IIC_Wait_Ack(); //等待应答
res=FDC_IIC_Read_Byte(1)<<8;//读取数据,发送ACK
// FDC_IIC_Ack();
res|=FDC_IIC_Read_Byte(0);//读取数据,发送nACK
FDC_IIC_Stop(); //产生一个停止条件
return res;
}
u32 FCD2214_ReadCH(u8 index)//这个就是对不同通道的数据进行读取
{
u32 result;
switch(index)
{
case 0:
result = FDC_Read(DATA_CH0)&0x0FFF;
result = (result<<16)|(FDC_Read(DATA_LSB_CH0));//读取相对应通道的数值·
break;
case 1:
result = FDC_Read(DATA_CH1)&0x0FFF;
result = (result<<16)|(FDC_Read(DATA_LSB_CH1));
break;
case 2:
result = FDC_Read(DATA_CH2)&0x0FFF;
result = (result<<16)|(FDC_Read(DATA_LSB_CH2));
break;
case 3:
result = FDC_Read(DATA_CH3)&0x0FFF;
result = (result<<16)|(FDC_Read(DATA_LSB_CH3));
break;
default:break;
}
result =result&0x0FFFFFFF;//十六进制的数据转化成十进制的
return result;
}
/*FDC2214初始化函数
*返回值:0:初始化正常
* 1:不正常
*/
float FDC2214_Init(void)
{
FDC_GPIO_Init();
FDC_IIC_Init();
//设置Set_FDC2214寄存器
Set_FDC2214(RCOUNT_CH0,0x34,0xFB);//参考计数转换间隔时间(T=(RCOUNT_CH0*16)/Frefx)
Set_FDC2214(RCOUNT_CH1,0x34,0xFB);
Set_FDC2214(RCOUNT_CH2,0x34,0xFB);
Set_FDC2214(RCOUNT_CH3,0x34,0xFB);
Set_FDC2214(SETTLECOUNT_CH0,0x00,0x1B);//转换之前的稳定时间(T=(SETTLECOUNT_CHx*16)/Frefx)
Set_FDC2214(SETTLECOUNT_CH1,0x00,0x1B);
Set_FDC2214(SETTLECOUNT_CH2,0x00,0x1B);
Set_FDC2214(SETTLECOUNT_CH3,0x00,0x1B);
Set_FDC2214(CLOCK_DIVIDERS_C_CH0,0x20,0x02);//选择在0.01MHz ~ 10MHz的传感器频率
Set_FDC2214(CLOCK_DIVIDERS_C_CH1,0x20,0x02);//Frefx = Fclk = 43.4MHz/2(2分频)
Set_FDC2214(CLOCK_DIVIDERS_C_CH2,0x20,0x02);//CHx_REF_DIVIDER=2;CHx_FIN_SEL=2
Set_FDC2214(CLOCK_DIVIDERS_C_CH3,0x20,0x02);//CHx_REF_DIVIDER=2;CHx_FIN_SEL=2
Set_FDC2214(DRIVE_CURRENT_CH0,0x78,0x00);//0.146ma(传感器时钟建立+转换时间的驱动电流)
Set_FDC2214(DRIVE_CURRENT_CH1,0x78,0x00);
Set_FDC2214(DRIVE_CURRENT_CH2,0x78,0x00);
Set_FDC2214(DRIVE_CURRENT_CH3,0x78,0x00);
Set_FDC2214(ERROR_CONFIG,0x00,0x00);//全部禁止错误汇报
Set_FDC2214(MUX_CONFIG,0xC2,0x0D);//通道0,1,2 ,3;选择10Mhz为超过振荡槽振荡频率的最低设置,多通道,四通道
Set_FDC2214(CONFIG,0x14,0x01);//激活模式,使用内部振荡器做参考频率,INTB引脚会随状态寄存器更新被置位
}
头文件
#ifndef __FDC2214_H
#define __FDC2214_H
#include "sys.h"
//IO方向设置
#define FDC_SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}
#define FDC_SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}
//IO操作函数
#define FDC_IIC_SCL PBout(6) //SCL
#define FDC_IIC_SDA PBout(7) //输出SDA
#define FDC_READ_SDA PBin(7) //输入SDA
/*FDC2214 iic从地址
*ADDR = L , I2C Address = 0x2A
*ADDR = H , I2C Address = 0x2B*/
#define FDC2214_ADDR 0x2A
/*FDC2214各个寄存器地址*/
#define DATA_CH0 0x00 //数据寄存器
#define DATA_LSB_CH0 0x01
#define DATA_CH1 0x02
#define DATA_LSB_CH1 0x03
#define DATA_CH2 0x04
#define DATA_LSB_CH2 0x05
#define DATA_CH3 0x06
#define DATA_LSB_CH3 0x07
#define RCOUNT_CH0 0x08 //
#define RCOUNT_CH1 0x09
#define RCOUNT_CH2 0x0A
#define RCOUNT_CH3 0x0B
//#define OFFSET_CH0 0x0C //FDC2114
//#define OFFSET_CH1 0x0D
//#define OFFSET_CH2 0x0E
//#define OFFSET_CH3 0x0F
#define SETTLECOUNT_CH0 0x10
#define SETTLECOUNT_CH1 0x11
#define SETTLECOUNT_CH2 0x12
#define SETTLECOUNT_CH3 0x13
#define CLOCK_DIVIDERS_C_CH0 0x14 //时钟分频
#define CLOCK_DIVIDERS_C_CH1 0x15
#define CLOCK_DIVIDERS_C_CH2 0x16
#define CLOCK_DIVIDERS_C_CH3 0x17
#define STATUS 0x18 //状态寄存器
#define ERROR_CONFIG 0x19 //错误报告设置
#define CONFIG 0x1A
#define MUX_CONFIG 0x1B
#define RESET_DEV 0x1C
#define DRIVE_CURRENT_CH0 0x1E //电流驱动
#define DRIVE_CURRENT_CH1 0x1F
#define DRIVE_CURRENT_CH2 0x20
#define DRIVE_CURRENT_CH3 0x21
#define MANUFACTURER_ID 0x7E //读取值:0x5449
#define DEVICE_ID 0x7F //读取值:0x3055
//extern u16 Data_FDC;
//相关函数申明
u8 Set_FDC2214(u8 reg,u8 MSB,u8 LSB);
u16 FDC_Read(u8 reg);
//u16 FCD2214_ReadCH(u8 index);
u32 FCD2214_ReadCH(u8 index);
float FDC2214_Init(void);
void record(float Cap_final);
void Self_test(float Cap_final);
float Cap_Calculate(u8 chx);
#endif
还有就是在主函数里面读取相对应通道的数据
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);
}
上面的就是读取相对应的数值移位赋值给C_CHX_data_final这一个变量。因为在FDC2214的芯片上是有四个通道的,可以同时读取各通道的数值,而且还很精准,这一个芯片还是很厉害的。
然后就是对返回来的数值进行处理真正读出该状态下的电容值。
至于是怎么计算电容值的我就不解释了,贴个文章各位看吧,计算电容值
我用的计算电容函数
float Cap;
Data_FDC = FCD2214_ReadCH(index);
// Cap = 56645.763f/((float)Data_FDC);
// return ((Cap*Cap)-33);
Cap = 232021045.248/(Data_FDC);
return (Cap*Cap);
总结
这一道电赛题是不难的,读取纸张的电容值,通过函数拟合得出最合适的电容值,然后就是和查表法对比就行了。最后就是如果你的FDC2214读取出来的数据是65535或者是其他一些很奇怪的数值,检查一下接线,如果还是不行的话看一下代码是不是哪一个寄存器配置有问题。没啥好说的,第一次写博客,写的不是很好,见谅了各位,代码在我资源那里贴出来,ヾ(^ ▽ ^*)))!!!!!