尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

FDC2214+STM32纸张计数方案:原理、寄存器配置与代码实现

FDC2214+STM32纸张计数方案:原理、寄存器配置与代码实现 简介这是一套基于STM32F103ZET6主控与FDC2214电容式传感模块实现的纸张计数装置完整嵌入式工程面向电子设计竞赛学生、传感器应用开发者及嵌入式初学者解决非接触式纸张厚度检测与数量估算的实际问题。资源包共132个文件含34个头文件.h定义寄存器与接口32个C源文件.c实现I²C驱动、FDC2214初始化、查表法厚度-张数映射、自校准逻辑及串口屏通信协议另有调试配置.dbgconf、编译输出.hex/.axf/.map和Keil工程文件.uvprojx/.uvoptx整体压缩包大小为2.76MB。已有772人学习下载工程结构规范包含完整的HAL或标准外设库驱动如stm32f10x_i2c.c、stm32f10x_tim.c等支持一键编译烧录附带自校准功能与无纸状态智能提示可直接用于课程设计、毕设原型或工业轻量级计数场景。 做纸张计数那会儿我一开始拿的是普通电容表想着把纸往两块极板中间一夹读个电容值就能换算张数。结果空极板和夹一张纸的读数差小到我看不出区别波形还一直跳。后来换了FDC2214这颗片子事情才慢慢变得可控。这篇文章把整套方案的选型、IIC通信、寄存器配置、数据换算和标定流程整理出来用的是STM32F103ZET6做后端控制通过IIC接口连接FDC2214电容传感模块目标是采集极板间纸张厚度变化并换算成张数代码结构和关键函数都会展开讲。适合正在调FDC2214驱动、想抄一套纸张计数方案的同行也适合比赛前临时抱佛脚的电子类学生参考。1. 为什么是FDC2214一张纸的电容变化到底有多小1.1 物理原理LC谐振测电容的本质FDC2214的核心是一个LC谐振测量电路。外接一个电感和被测电容并联组成LC振荡回路芯片内部通过测量这个回路的谐振频率来反向推算电容值。电容一变谐振频率就跟着变FDC2214把频率转换成28位数字量输出。它测电容不直接测容值而是测频率这是它分辨率高的关键原因。拿纸张计数场景来说极板间塞进纸后等效介电常数变了电容变大谐振频率下降。FDC2214在一个参考时钟窗口内统计转换结果28位分辨率理论上能分辨到0.1fF级别的电容变化。0.1fF是什么概念普通万用表电容档能做到1pF分辨率就不错了差距是四五个数量级。这就是为什么FDC2214适合干这种微小电容变化识别的活而普通方案做不了。不过要注意FDC2214虽然输出28位但实际有效分辨率和传感器设计、PCB走线、电源噪声都强相关。如果极板引线太长、屏蔽没做好高位数据会乱跳分辨率优势直接被浪费掉。后面第4章会细说极板设计的注意事项。1.2 和常见电容测量方案的对比做这个项目前我还对比过几个方案这里直接给结论。方案分辨率/精度成本接口适合场景万用表电容档1pF左右已拥有手动静态测量完全不适合实时计数PCap01分辨率很高中高SPI/IIC本来也是好选择但外围电路复杂配置繁琐FDC221428位中IIC/SPI外围简单LC谐振直接接传感器最适合这种小电容变化识别实际对比下来FDC2214最大优势在于外围电路极简。只需要一个电感和两块极板就能构成传感器不需要额外的高精度运放或激励源这对做项目、做比赛都是很友好的。我最后选了FDC2214还有一个原因是它的典型应用手册里明确提到了接近检测、液位检测这类微小电容变化场景和纸张计数的物理模型非常接近。1.3 纸张计数场景的等效模型极板可以看成一个平行板电容器电容公式是 CεS/d。空气的介电常数约1纸张的介电常数大概是2到3。如果纸完全填满极板间隙电容会增大到原来的两三倍。但实际装置里纸张只占间隙的一部分极板间距通常有几毫米单张纸厚0.1mm量级所以总的等效介电常数变化很小。我粗略估算过一张普通A4打印纸引起的电容变化在0.1pF到0.5pF之间具体取决于极板面积和间距。这个量级对FDC2214来说刚好落在最佳测量区间。如果极板面积太小比如硬币大小单张纸的变化可能低于0.05pF测量难度会明显增加如果极板面积太大虽然电容变化更大但容易受到人手靠近、环境位移的干扰。后面第4章的极板设计小节再展开。2. 硬件连接和IIC通信为什么我强烈建议软件模拟IIC2.1 硬件最小系统FDC2214的外围电路很简单核心连接就是IIC总线和LC传感器。我用的接线方式整理成表格FDC2214引脚连接说明VDD3.3V必须干净推荐LC滤波GNDGND单点接地避免和电机共地SDASTM32 PB7需要上拉电阻SCLSTM32 PB6需要上拉电阻ADDRGNDIIC地址选择接地后7位地址0x2AINTB不接或接GPIO转换完成中断引脚用轮询可以省掉IN0A/IN0B接LC谐振回路电感并联极板电容两端分别接这两个引脚上拉电阻这块很多人忽略我特意说一下。FDC2214的SDA和SCL是开漏输出必须外部上拉否则IIC通信时好时坏。电阻取值4.7kΩ到10kΩ都可以。我实测下来3.3V供电、IIC速率100kHz时用4.7kΩ比较稳波形边沿干脆用10kΩ也能工作但上升沿稍慢如果线材较长容易出偶发通信失败。IIC总线上并联的小电容不要超过几十pF线越短越稳。关于IIC地址ADDR接GND时7位从机地址是0x2A接VDD时是0x2B。换算成8位读写地址写地址分别是0x54/0x56读地址是0x55/0x57。很多新手在这个地方搞混导致通信不通。我建议初始化时先读一下设备ID寄存器确认地址正确。2.2 STM32F103ZET6硬件IIC的两个坑STM32F103系列自带硬件IIC外设理论上可以直接用。但实际做项目时我强烈建议用软件模拟IIC原因有两个。第一个坑是标准库下的硬件IIC容易卡死。F1系列的硬件IIC在事件处理不当时会进入忙状态总线释放不掉现象是程序跑着跑着就死等在一个事件标志上。这个问题本质上和芯片的IIC模块设计、中断优先级、事件标志清除顺序都有关系排查起来非常费时间。第二个坑是HAL库虽然把硬件IIC封装得好了不少但底层用起来依然有些边界情况需要处理。比如主机接收数据时最后两字节的NACK和STOP条件时序处理不好就会出现多读一个字节或者通信超时。如果你就想用硬件IIC也不是不行但要做好心理准备中断优先级要合理配置错误处理要完善最好加超时机制。我比较务实的做法是直接软件模拟IIC一个GPIO位操作就搞定了逻辑完全可控跑100kHz完全没问题对于FDC2214这种慢速设备来说绰绰有余。整个项目调下来软件IIC一次都没出过问题。2.3 软件模拟IIC的核心时序实现软件模拟IIC的关键是SDA和SCL的电平切换时序。这里给出我自己用的核心函数STM32F103ZET6标准库写法GPIO配置为开漏输出。#define SDA_H() GPIO_SetBits(GPIOB, GPIO_Pin_7) #define SDA_L() GPIO_ResetBits(GPIOB, GPIO_Pin_7) #define SCL_H() GPIO_SetBits(GPIOB, GPIO_Pin_6) #define SCL_L() GPIO_ResetBits(GPIOB, GPIO_Pin_6) #define SDA_READ() GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_7) static void IIC_Delay(void) { // 100kHz下大约5us延时可用定时器或空循环微调 delay_us(5); } void IIC_Start(void) { SDA_H(); SCL_H(); IIC_Delay(); SDA_L(); // SCL高电平时SDA拉低产生起始信号 IIC_Delay(); SCL_L(); } void IIC_Stop(void) { SDA_L(); SCL_H(); IIC_Delay(); SDA_H(); // SCL高电平时SDA拉高产生停止信号 IIC_Delay(); } void IIC_SendByte(uint8_t data) { for (uint8_t i 0; i 8; i) { if (data 0x80) SDA_H(); else SDA_L(); data 1; IIC_Delay(); SCL_H(); IIC_Delay(); SCL_L(); } } uint8_t IIC_ReadByte(void) { uint8_t data 0; for (uint8_t i 0; i 8; i) { SCL_H(); IIC_Delay(); data (data 1) | SDA_READ(); SCL_L(); IIC_Delay(); } return data; } uint8_t IIC_WaitAck(void) { SDA_H(); // 释放SDA让从机拉低应答 IIC_Delay(); SCL_H(); IIC_Delay(); uint8_t ack SDA_READ(); // 低电平表示ACK SCL_L(); IIC_Delay(); return ack ? 1 : 0; }这里有几个操作细节要注意。起始信号和停止信号必须保证SCL为高时SDA发生跳变发送字节时SDA只能在SCL低电平期间变化等待应答前主机必须释放SDA否则从机拉不低总线。这些细节我踩过坑严格按照时序来写后面基本一次性通过。软件IIC还有个好处是方便调速率。如果设备不稳定调大延时就行硬件IIC要改波特率配置折腾得多。3. FDC2214寄存器初始化RCOUNT、SETTLECOUNT、CLOCK_DIVIDERS、MUX_CONFIG怎么配3.1 先读设备ID验证通信系统上电后不要急着配置先让电源稳定。我习惯上电后延时100ms然后复位芯片再读设备ID。FDC2214的设备ID寄存器地址是0x7E正常读取返回0x3055。这一段代码能确认IIC通不通、地址对不对、芯片有没有正常启动。// 复位FDC2214 FDC2214_WriteReg(0x1C, 0x8000); delay_ms(20); // 读设备ID验证IIC通信 uint16_t dev_id FDC2214_ReadReg(0x7E); if (dev_id 0x3055) { // 通信正常 } else { // 检查接线、地址、上电时序 }如果读不到0x3055优先检查三点ADDR引脚的地址选择是否正确、SDA/SCL上拉电阻是否漏焊、供电是否稳定。我遇到过一次是芯片供电引脚接触不良导致读ID时好时坏补焊后就好了。3.2 RCOUNT和SETTLECOUNT的取舍RCOUNT寄存器决定了单次转换的采样窗口长度也就是内部参考时钟周期的计数个数。这个值越大转换时间越长分辨率越高值越小转换越快但数据抖动会加大。FDC2214的RCOUNT_CH0寄存器地址是0x0816位有效范围1到0xFFFF。SETTLECOUNT寄存器是沉降计数主要给LC振荡器一个稳定时间。传感器上电或切换通道后振荡幅度不会瞬间稳定需要等一段时间。SETTLECOUNT越大等待越久适合高Q值传感器太小可能导致转换结果还没稳定就被读出来数据偏大或跳变。我自己用的推荐配置组合是寄存器地址我用的值对应效果RCOUNT_CH00x080x1FFF8191个参考周期单次转换约10ms级别SETTLECOUNT_CH00x100x0064100个周期沉降兼顾速度和稳定如果你的极板传感器和我的差异大可以先按这个组合跑然后把RCOUNT提高试试。如果数据还是很跳优先检查电源和传感器连接而不是无限加大RCOUNT因为转换时间太长会造成响应迟钝纸张插进去半天才出结果。3.3 CLOCK_DIVIDERS和DRIVE_CURRENTCLOCK_DIVIDERS寄存器配置的是参考时钟分频和传感器频率分频。FDC2214使用外部参考时钟或晶振我用的模块上有晶振参考频率固定。CLOCK_DIVIDERS_CH0地址是0x14位13到12配置传感器频率分频位9到8配置参考频率分频。每个分频器有1、2、4、8四档。这里不能随便抄一个值就完事。要先估算LC传感器的谐振频率。比如我用的电感是47μH空极板电容约15pF谐振频率大概是 1 / (2π√(LC)) ≈ 6MHz。这个频率下传感器分频选1参考分频选1或2都行。如果传感器频率太高超出芯片测量范围就要提高分频。我配置成0x1001也就是传感器分频2、参考分频2实测数据落在合理区间。DRIVE_CURRENT寄存器控制LC振荡器的激励电流。激励电流太小传感器可能不起振太大则振荡幅度过高影响稳定性和精度。DRIVE_CURRENT_CH0地址是0x1E寄存器值越高输出电流越小也就是反比关系。这个值一般根据传感器谐振频率查手册表格来粗选再通过实际波形微调。调试DRIVE_CURRENT时有个简单方法写一个值读DATA看数据是否稳定。如果数据连续几十次跳变超过一定范围就换个档位试试。我最终用的值是0x7800对应大约几毫安的激励电流具体以你自己的传感器实测为准。3.4 用MUX_CONFIG选通道并启动转换FDC2214有四个测量通道MUX_CONFIG寄存器用来选择使能哪个通道以及是否自动切换序列。如果只用一个极板传感器配置CH0就够了。MUX_CONFIG地址是0x1B我用到的值是0x0201对应使能CH0、关闭其他通道、不做自动序列切换。// FDC2214初始化函数 void FDC2214_Init(void) { // 1. 复位 FDC2214_WriteReg(0x1C, 0x8000); delay_ms(50); // 2. 配置RCOUNT_CH0 0x1FFF FDC2214_WriteReg(0x08, 0x1FFF); // 3. 配置SETTLECOUNT_CH0 0x0064 FDC2214_WriteReg(0x10, 0x0064); // 4. 配置CLOCK_DIVIDERS_CH0 0x1001 FDC2214_WriteReg(0x14, 0x1001); // 5. 配置DRIVE_CURRENT_CH0 0x7800 FDC2214_WriteReg(0x1E, 0x7800); // 6. 配置MUX_CONFIG使能CH0关闭其他通道 FDC2214_WriteReg(0x1B, 0x0201); }配置完MUX_CONFIG后芯片会按照配置自动开始连续转换。这时可以通过轮询状态寄存器或者直接读数据寄存器来判断转换是否完成。我习惯用轮询方式简单可靠。4. 28位数据读取与纸张厚度换算别再拼错寄存器了4.1 读取时序与数据拼接FDC2214的转换结果是28位分在两个寄存器里。DATA_CH0寄存器地址是0x00存的是28位结果的高16位DATA_LSB_CH0寄存器地址是0x01存的是低12位但这低12位放在16位寄存器的bit15到bit4上也就是左对齐的bit3到bit0固定为0。所以拼接方式很关键正确写法是uint32_t FDC2214_ReadDataCH0(void) { uint16_t msb FDC2214_ReadReg(0x00); // 高16位 uint16_t lsb FDC2214_ReadReg(0x01); // 低12位在bit15:bit4 // 正确拼接28位结果 (msb 12) | (lsb 4) uint32_t result ((uint32_t)msb 12) | (lsb 4); return result; }我见过有人直接写成(msb 16) | lsb这样低12位没有右移数据整体偏大而且还有可能把状态位混进去。DATA_LSB_CH0寄存器的高4位里有DRDY、DONE等状态标志不右移的话这些标志会混入数据低位导致结果完全不对。这是一个特别容易踩的坑。读取时机上我建议每次读取前先判断转换完成标志。最简单的方式是读DATA_LSB_CH0寄存器时检查bit14DONE标志如果为1说明本次转换已完成可以安全读取。uint32_t FDC2214_ReadDataWithCheck(void) { uint16_t msb, lsb; uint32_t result; // 轮询DONE标志 do { lsb FDC2214_ReadReg(0x01); } while ((lsb 0x4000) 0); // bit14 DONE msb FDC2214_ReadReg(0x00); lsb FDC2214_ReadReg(0x01); result ((uint32_t)msb 12) | (lsb 4); return result; }加上这个DONE判断后数据稳定性提升明显。如果轮询超时还是等不到DONE大概率是传感器没起振查一下DRIVE_CURRENT和LC回路。4.2 从DATA到频率再到电容拿到28位数据后如果想精确换算成电容值可以先算传感器谐振频率。TI手册给出的关系约为DATA (2^28 × f_sensor) / (FIN_DIVIDER × f_ref)反推就是f_sensor (DATA × FIN_DIVIDER × f_ref) / 2^28有了频率再反推电容C 1 / (4π² × f_sensor² × L)其中L是你外接的电感值。这套换算在理论上是完整的但我实际做项目时并不直接算电容原因有两个。第一L本身的精度并不高贴片电感的感值误差可能5%到10%算出来的电容绝对值误差很大第二纸张计数真正关心的是相对变化量而不是绝对容值。所以我更建议把28位DATA值直接作为原始特征量来标定和判断省去换算环节也少一个误差来源。不过理解频率和电容的关系还是有用的。我要记住一个核心方向电容变大时谐振频率下降DATA值变小纸张越多电容越大DATA越小。基于这个方向做标定逻辑就清晰了。4.3 极板设计对厚度分辨能力的影响极板设计是决定整个装置成败的关键一环。我在调试中总结了几条实践经验。极板面积推荐在100mm×100mm左右太大容易受环境电容干扰太小则信噪比不足。间距方面极板固定间距建议控制在5mm以内太宽了纸张引起的电容变化占比会变小。材料直接用PCB覆铜板或者铜箔胶带贴在亚克力板上就行关键是表面平整、间距均匀。引线是另一个大坑。极板到FDC2214传感器的引线越长寄生电容越大而且引线本身受人体靠近影响严重数据会出现无规律的漂移。我的做法是极板引线不超过10cm并且使用屏蔽线屏蔽层单端接地。如果你发现手一靠近极板数据就疯跳大概率就是引线屏蔽没做好。FDC2214的传感器输入引脚IN0A和IN0B之间直接接LC并联回路这个回路要尽量远离数字信号线和大电流走线尤其是IIC的SCL/SDA不要贴着传感器走线走。PCB布板时最好在传感器区域周围铺一圈地起到隔离作用。这些设计细节直接影响最终的分辨率比代码优化更值得花时间。5. 从原始读数到计数结果标定与判据5.1 标定流程空极板、单张、批量拿到稳定的DATA值之后下一步是建立DATA和张数的关系。标准做法是先做标定。我的标定流程是这样的极板空着连续采样100次取平均记为DATA_0。放1张纸等数据稳定后采样100次取平均记为DATA_1。依次放5张、10张、20张、50张分别记录平均值建立一张标定表。每次标定时纸要放得平整尽量贴住极板不要悬空。我这组实测数据用100mm×100mm铜箔极板间距3mm大致长这样纸张数量DATA平均值与空极板的差值09200000019188000-1200059140000-60000109080000-120000208960000-240000508600000-600000注意这组数据只是我那个装置的绝对值不同板子、不同极板、不同纸张都会有差异不要直接拿来当你的阈值。但从趋势能看出DATA值和纸张数量近似线性单张纸的差分信号在10000以上远大于数据波动分辨没问题。5.2 计数策略区间判断和线性拟合标定完成之后计数策略有两个常用方案。方案一区间判断。把标定表里每个张数对应的DATA值上下扩展一个容差形成区间。比如1张纸的DATA在9180000到9196000之间那实测数据落在这个区间就判断为1张。这个方案简单直接代码量小适合张数离散、数量不多的场景。方案二线性拟合。用最小二乘法把标定的数据点拟合成一条直线张数 k × ΔDATA b。我标定的数据拟合出来大概是每张纸对应12000点左右的变化所以判定代码可以写成// 假设标定得到的差值是线性的 // delta_per_sheet (DATA_50 - DATA_0) / 50 int CountSheets(uint32_t data) { int32_t delta (int32_t)data - (int32_t)data_base; // data_base是空极板基准 if (delta 0) return 0; // 比基准还大视为空 int count (int32_t)(-delta * 50 / (data_base - data_50)); return count; }线性拟合方案适合张数连续变化的场景但要注意纸张较多时关系可能偏离线性尤其超过50张以后误差会变大。如果目标就是1到100张范围我建议分段拟合比如0到20张用一段21到50张用另一段精度会好很多。关于基准值data_base我会在每次开机后先空极板采样几十次求平均作为本次运行的基准而不是把上一次保存的固定值拿来用。因为环境温度、湿度甚至极板上残留的灰尘都会让基准漂移开机现场校准最保险。5.3 防抖与实时性实际使用中纸张插进极板和抽出的过程中DATA值会剧烈波动不能读到一次数据就直接判定张数。我的处理方式是加滑动平均滤波和延时确认。滑动平均窗口用10到20次采样每采一个新值就替换掉队列里最旧的值取平均后再判断。单次采样耗时约10ms20次也就200ms左右响应速度完全够用。额外再加一个确认机制连续3次判断的结果相同才认为张数稳定了。这样可以过滤掉插纸瞬间的抖动。如果用户快速连续插纸芯片也能跟上因为有滤波和确认机制不会在动作进行中误判。我遇到过的情况是纸张抽出一半时DATA值落在某个中间位置如果此时判断张数会偏大。加了延时确认后这种误判基本消除。6. 实测中容易踩的坑供电、温漂、纸张差异6.1 供电纹波直接体现在DATA上这是我最头疼的坑。FDC2214虽然抗干扰能力不错但供电若是纹波大28位数据的低位会一直跳表现为读数像心电图一样抖动。排查方法很简单用示波器看FDC2214芯片VDD引脚的纹波。正常应该在几十毫伏以内。如果超过这个量级就得处理。处理办法有三个芯片电源引脚附近加一个10μF钽电容和0.1μF陶瓷电容并联做去耦如果系统里有电机、继电器这类大电流负载FDC2214的供电要从主电源单独走线或加磁珠隔离每次读取数据后多采几次取平均本文还有配套的精品资源点击获取
返回列表