一、FDC2214介绍
(1)这个图片截自FDC2214的数据手册,可以看出FDC2214与MCU之前的通讯是靠I2C协议来完成的,该芯片的外围电路设计在手册中已给出。
(2)该模块I2C接口最大速度为400 kbit/s。遵循标准I2C7位从机地址,当FDC2214芯片的ADDR引脚为低电平,该芯片地址为0x2A,当引脚为高电平时,地址为0x2B,当FDC开始工作时,不要更改ADDR引脚的电平。
(3)I2C的时序就不多做介绍了。
二、FDC2214寄存器介绍(截图都是手册上的)
(1)FDC2214寄存器的地址介绍
这个也是从手册中截取的
(a) 0x00-0x07为通道0-3的数据寄存器,读取数据的时候使用
只介绍DATA_CH0,剩下三个都是一样的。
其中地址为0x00的寄存器,[15:14]:保留,设为00;[13:12]:为ERR信息,可以读取到Channel 0的错误状态。[11:0]Channel 0的数据转换结果(高位)。
地址为0x01的寄存器,[15:0]:Channel 0的数据转换结果(低位)。与上面的高位数据组合起来就是Channel 0所测得的结果。
(b) 0x08-0x0B为通道0-3计数转换间隔
放个公式的图片吧,这角标用公式敲不出来。。
这个是手册中最后应用部分给出的值。具体为什么这么设置,可以参看下这个手册截图
其中Tsample是我们写程序时,每隔10ms就去读取一下各个通道的DATA,settling time后面会说到,通道转换延迟时间引用下手册里面的。
The channel switching delay is ~1μs for fREF = 40 MHz
N是通道数,用到的三个通道,所以N=3。将3.3ms代入上面公式,可以得出RCOUNT的值应改为0x2089。
(c)0x0C-0x0F为转换偏移,此项一般不做配置。只在FDC2112/FDC2114中设置。
(d) 0x10-0x13 为通道0-3设置转换开始前的稳定时间,根据手册介绍,该寄存器的值最小应大于10。稳定时间的计算公式为:
将10代入公式得,SettleTime)为4μs。所以SETTLECOUNT的值应该设为0x000A。
(e) 0x14-0x17通道0-3的频率选择,当采用差分传感器时,应配置为[13:12]为应设为b01。当采用单端传感器的时候,[13:12]应配置为b10。[9:0]配置的参数用来缩放最大转换频率,公式为:
此项不要设置为[9:0]00 0000 0000,应保证CH0_(FREF_DIVIDER )≥b^’ 0000000001。设为[9:0]1时,f_CLK 为40MHz。
所以当采用单端输入时,设置0x14的值为0x2001。单端连接如图所示
当采用差分输入时,设置0x14的值为0x1001。差分输入如图所示
(f) )0x18:设备状态报告。读取该寄存器的值,获取错误信息。
(g) 0x19:设备状态报告配置。使用默认值0x0000来配置0x19,默认情况下,不允许中断启用。
(h)0x1A:转换配置。[15:14]:当0x1B的自动扫描模式配置为0的时候可以选择通道,默认00。[13]:使能睡眠模式,将其置一为01。[12]:必须置1。[11]:传感器激活模式选择,0:全电流启动模式,缩短传感器激活时间。1:低功率激活模式,激活以最小化功耗。[10]:必须置1。[9]:选择参考频率源,0:使用内部振荡频率作为参考源,1:参考频率由CLK引脚提供。[8]:必须置0。[7]:INTB失能,默认配置0。[6]:高电流传感器驱动,0:FDC将以正常的传感器电流驱动所有通道。1:FDC将以大于1.5mA的电流驱动通道0,当多通道模式开启时,则不支持此模式。[5:0]:必须设置为00 0001
由于我的是CLKIN引脚接入的晶振,所以设置0x1A为0x1601。
(i) 0x1B:信道复用配置。[15]:自动扫描模式使能,将其设置为1,以启动顺序模式(自动),[14:13]设置为10,启动4个通道的数据转换。[12:3]:必须设置为00 0100 0001。[2:0]:设置输入抗尖峰滤波带宽,选择超出震荡槽的最低设置振荡频率,设置为101(10MHz)。故0x1B寄存器的值为0xC20D。
(j)0x1C:复位配置。目前使用不到,不做配置。
(k)0x1E-0x21:通道0-3传感器电流驱动配置。0x8000(0.169mA),0x7800(0.146mA)此值更合适。这个参数怎么设置的我也不太了解,就是查资料发现大家都是设置的这两个值。。。 我用的是0x7800。
那么到这里寄存器就介绍的差不多了,有些地方我没弄太明白,希望有人可以给些提示啥的,如果有错误也麻烦指出了,慢慢学习,共同进步,先谢谢大家了。
总结一下需要配置的寄存器,及寄存器值。
三、FDC2214程序
声明下我的I2C程序是移植的野火I2C模拟程序,感谢火哥。。
#include "fdc2214.h"
#include "stm32f10x.h"
static void i2c_CfgGpio(void);
/*
*********************************************************************************************************
* 函 数 名: i2c_Delay
* 功能说明: I2C总线位延迟,最快400KHz
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
static void i2c_Delay(void)
{
uint8_t i;
/*
下面的时间是通过逻辑分析仪测试得到的。
工作条件:CPU主频72MHz ,MDK编译环境,1级优化
循环次数为10时,SCL频率 = 205KHz
循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us
循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us
*/
for (i = 0; i < 10; i++);
}
/*
*********************************************************************************************************
* 函 数 名: i2c_Start
* 功能说明: CPU发起I2C总线启动信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_Start(void)
{
/* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
FDC2214_I2C_SDA_1();
FDC2214_I2C_SCL_1();
i2c_Delay();
FDC2214_I2C_SDA_0();
i2c_Delay();
FDC2214_I2C_SCL_0();
i2c_Delay();
}
/*
*********************************************************************************************************
* 函 数 名: i2c_Start
* 功能说明: CPU发起I2C总线停止信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_Stop(void)
{
/* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
FDC2214_I2C_SDA_0();
FDC2214_I2C_SCL_1();
i2c_Delay();
FDC2214_I2C_SDA_1();
}
/*
*********************************************************************************************************
* 函 数 名: i2c_SendByte
* 功能说明: CPU向I2C总线设备发送8bit数据
* 形 参:_ucByte : 等待发送的字节
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_SendByte(uint8_t _ucByte)
{
uint8_t i;
/* 先发送字节的高位bit7 */
for (i = 0; i < 8; i++)
{
if (_ucByte & 0x80)
{
FDC2214_I2C_SDA_1();
}
else
{
FDC2214_I2C_SDA_0();
}
i2c_Delay();
FDC2214_I2C_SCL_1();
i2c_Delay();
FDC2214_I2C_SCL_0();
if (i == 7)
{
FDC2214_I2C_SDA_1(); // 释放总线
}
_ucByte <<= 1; /* 左移一个bit */
i2c_Delay();
}
}
/*
*********************************************************************************************************
* 函 数 名: i2c_ReadByte
* 功能说明: CPU从I2C总线设备读取8bit数据
* 形 参:无
* 返 回 值: 读到的数据
*********************************************************************************************************
*/
uint8_t i2c_ReadByte(void)
{
uint8_t i;
uint8_t value;
/* 读到第1个bit为数据的bit7 */
value = 0;
for (i = 0; i < 8; i++)
{
value <<= 1;
FDC2214_I2C_SCL_1();
i2c_Delay();
if (FDC2214_I2C_SDA_READ())
{
value++;
}
FDC2214_I2C_SCL_0();
i2c_Delay();
}
return value;
}
/*
*********************************************************************************************************
* 函 数 名: i2c_WaitAck
* 功能说明: CPU产生一个时钟,并读取器件的ACK应答信号
* 形 参:无
* 返 回 值: 返回0表示正确应答,1表示无器件响应
*********************************************************************************************************
*/
uint8_t i2c_WaitAck(void)
{
uint8_t re;
FDC2214_I2C_SDA_1(); /* CPU释放SDA总线 */
i2c_Delay();
FDC2214_I2C_SCL_1(); /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
i2c_Delay();
if (FDC2214_I2C_SDA_READ()) /* CPU读取SDA口线状态 */
{
re = 1;
}
else
{
re = 0;
}
FDC2214_I2C_SCL_0();
i2c_Delay();
return re;
}
/*
*********************************************************************************************************
* 函 数 名: i2c_Ack
* 功能说明: CPU产生一个ACK信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_Ack(void)
{
FDC2214_I2C_SDA_0(); /* CPU驱动SDA = 0 */
i2c_Delay();
FDC2214_I2C_SCL_1(); /* CPU产生1个时钟 */
i2c_Delay();
FDC2214_I2C_SCL_0();
i2c_Delay();
FDC2214_I2C_SDA_1(); /* CPU释放SDA总线 */
}
/*
*********************************************************************************************************
* 函 数 名: i2c_NAck
* 功能说明: CPU产生1个NACK信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_NAck(void)
{
FDC2214_I2C_SDA_1(); /* CPU驱动SDA = 1 */
i2c_Delay();
FDC2214_I2C_SCL_1(); /* CPU产生1个时钟 */
i2c_Delay();
FDC2214_I2C_SCL_0();
i2c_Delay();
}
/*
*********************************************************************************************************
* 函 数 名: i2c_CfgGpio
* 功能说明: 配置I2C总线的GPIO,采用模拟IO的方式实现
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
static void i2c_CfgGpio(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(FDC2214_I2C_SCL_GPIO_CLK, ENABLE); /* 打开GPIO时钟 */
GPIO_InitStructure.GPIO_Pin = FDC2214_I2C_SCL_GPIO_PIN | FDC2214_I2C_SDA_GPIO_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; /* 开漏输出 */
GPIO_Init(FDC2214_I2C_SCL_GPIO_PORT, &GPIO_InitStructure);
/* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */
i2c_Stop();
}
/*
*********************************************************************************************************
* 函 数 名: i2c_CheckDevice
* 功能说明: 检测I2C总线设备,CPU向发送设备地址,然后读取设备应答来判断该设备是否存在
* 形 参:_Address:设备的I2C总线地址
* 返 回 值: 返回值 0 表示正确, 返回1表示未探测到
*********************************************************************************************************
*/
uint8_t i2c_CheckDevice(uint8_t _Address)
{
uint8_t ucAck;
i2c_CfgGpio(); /* 配置GPIO */
i2c_Start(); /* 发送启动信号 */
/* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
i2c_SendByte(_Address | FDC2214_I2C_WR);
ucAck = i2c_WaitAck(); /* 检测设备的ACK应答 */
i2c_Stop(); /* 发送停止信号 */
return ucAck;
}
// FDC2214
/**
* 成功返回0,失败返回1
*/
uint8_t Set_FDC2214(uint8_t reg_addr,uint8_t MSB,uint8_t LSB)
{
i2c_Start();
i2c_SendByte((FDC2214_ADDR << 1)|0);
if(i2c_WaitAck() == 1)
{
// 无应答
i2c_Stop();
printf("FDC2214无应答\n");
return 1;
}
i2c_SendByte(reg_addr); // 写寄存器地址
if(i2c_WaitAck() == 1)
{
// 无应答
i2c_Stop();
printf("向FDC2214%x写入失败\n",reg_addr);
return 1;
}
i2c_SendByte(MSB);
if(i2c_WaitAck() == 1)
{
// 无应答
i2c_Stop();
printf("向FDC2214%x写入高八位数据失败\n",reg_addr);
return 1;
}
i2c_SendByte(LSB);
if(i2c_WaitAck() == 1)
{
// 无应答
i2c_Stop();
printf("向FDC2214%x写入低八位数据失败\n",reg_addr);
return 1;
}
i2c_Stop();
return 0;
}
uint16_t FDC_Read(uint8_t reg_addr)
{
uint16_t recData;
i2c_Start();
i2c_SendByte((FDC2214_ADDR << 1)|0);
if(i2c_WaitAck() == 1)
{
// 无应答
i2c_Stop();
printf("FDC2214无应答\n");
return 1;
}
i2c_SendByte(reg_addr);
if(i2c_WaitAck() == 1)
{
// 无应答
i2c_Stop();
printf("向FDC2214%x写入失败\n",reg_addr);
return 1;
}
i2c_Start();
i2c_SendByte((FDC2214_ADDR << 1)|1);
if(i2c_WaitAck() == 1)
{
// 无应答
i2c_Stop();
printf("FDC2214读取失败\n",reg_addr);
return 1;
}
recData = i2c_ReadByte() << 8;
i2c_Ack();
recData |= i2c_ReadByte();
i2c_NAck;
i2c_Stop();
return recData;
}
/**
* 得到通道x转换后的数据
* CHx: 0 -> CH0
* 1 -> CH1
* 2 -> CH2
* 3 -> CH3
* 返回值:读到的数据
*/
uint32_t FDC_ReadCH(uint8_t CHx)
{
uint32_t result;
switch(CHx)
{
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;
}
uint8_t FDC2214_Init(void)
{
uint16_t id;
i2c_CfgGpio();
id = FDC_Read(MANUFACTURER_ID);
if(id == 0x5449)
{
/* 设置转换时间 (RCOUNT_CH0 * 16)/40M 约为3.3ms */
Set_FDC2214(RCOUNT_CH0,0x18,0x6A);// 0x2089(三个通道使用)
Set_FDC2214(RCOUNT_CH1,0x18,0x6A);
Set_FDC2214(RCOUNT_CH2,0x18,0x6A);
Set_FDC2214(RCOUNT_CH3,0x18,0x6A);
/* 设置转换之前的稳定时间 T=(SETTLECOUNT_CHx*16)/40M, 大约4us */
Set_FDC2214(SETTLECOUNT_CH0,0x00,0x0A);
Set_FDC2214(SETTLECOUNT_CH1,0x00,0x0A);
Set_FDC2214(SETTLECOUNT_CH2,0x00,0x0A);
Set_FDC2214(SETTLECOUNT_CH3,0x00,0x0A);
/*通道频率选择 测试时板载采用的是单端输入 故设置为0x2001 差分输入为0x1001,不分频*/
Set_FDC2214(CLOCK_DIVIDERS_C_CH0,0x20,0x01);
Set_FDC2214(CLOCK_DIVIDERS_C_CH1,0x10,0x01);
Set_FDC2214(CLOCK_DIVIDERS_C_CH2,0x10,0x01);
Set_FDC2214(CLOCK_DIVIDERS_C_CH3,0x10,0x01);
Set_FDC2214(ERROR_CONFIG,0x00,0x00);
Set_FDC2214(MUX_CONFIG,0xC2,0x0D);
Set_FDC2214(DRIVE_CURRENT_CH0,0x78,0x00);
Set_FDC2214(DRIVE_CURRENT_CH1,0x78,0x00);
Set_FDC2214(DRIVE_CURRENT_CH2,0x78,0x00);
Set_FDC2214(DRIVE_CURRENT_CH3,0x78,0x00);
/* 使能FDC2214取外部时钟为参考 */
Set_FDC2214(CONFIG,0x16,0x01);
}
else
return 1;
return 0;
}
接下来是头文件
#include "stm32f10x.h"
#include "bsp_usart1.h"
#define FDC2214_I2C_WR 0 /* 写控制bit */
#define FDC2214_I2C_RD 1 /* 读控制bit */
#define FDC2214_I2C_SCL_1() GPIO_SetBits(FDC2214_I2C_SCL_GPIO_PORT, FDC2214_I2C_SCL_GPIO_PIN) /* SCL = 1 */
#define FDC2214_I2C_SCL_0() GPIO_ResetBits(FDC2214_I2C_SCL_GPIO_PORT, FDC2214_I2C_SCL_GPIO_PIN) /* SCL = 0 */
#define FDC2214_I2C_SDA_1() GPIO_SetBits(FDC2214_I2C_SDA_GPIO_PORT, FDC2214_I2C_SDA_GPIO_PIN) /* SDA = 1 */
#define FDC2214_I2C_SDA_0() GPIO_ResetBits(FDC2214_I2C_SDA_GPIO_PORT, FDC2214_I2C_SDA_GPIO_PIN) /* SDA = 0 */
#define FDC2214_I2C_SDA_READ() GPIO_ReadInputDataBit(FDC2214_I2C_SDA_GPIO_PORT, FDC2214_I2C_SDA_GPIO_PIN) /* 读SDA口线状态 */
/* FDC2214设备地址 */
#define FDC2214_ADDR 0x2A
/* I2C地址 */
#define I2C_OWN_ADDR 0x0A
/* I2C定义 */
#define FDC2214_I2C I2C1
#define FDC2214_I2C_CLK RCC_APB1Periph_I2C1
#define FDC2214_I2C_BAUDRATE 400000
/* I2C引脚定义 */
#define FDC2214_I2C_SCL_GPIO_PORT GPIOB
#define FDC2214_I2C_SCL_GPIO_PIN GPIO_Pin_6
#define FDC2214_I2C_SCL_GPIO_CLK RCC_APB2Periph_GPIOB
#define FDC2214_I2C_SDA_GPIO_PORT GPIOB
#define FDC2214_I2C_SDA_GPIO_PIN GPIO_Pin_7
#define FDC2214_I2C_SDA_GPIO_CLK RCC_APB2Periph_GPIOB
/*等待超时时间*/
#define I2C_FLAG_TIMEOUT ((uint32_t)0x1000)
/* 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 //转换偏移
#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 //通道0-3的频率选择
#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 //设备状态报告配置。使用默认值0x0000来配置0x19,默认情况下,不允许中断启用。
#define CONFIG 0x1A //转换配置
#define MUX_CONFIG 0x1B //信道复用配置(开启多通道自动扫描)
#define RESET_DEV 0x1C //复位
#define DRIVE_CURRENT_CH0 0x1E //电流驱动 0x7800(0.146mA)此值更合适。
#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