
简介面向STM32F103的电容传感器PCAP01数据读取与发送程序包为嵌入式开发者提供了一套完整的SPI与RS-485通信参考。压缩包共154个文件大小约2.61MB以C源码、头文件、Keil工程文件为主附带HEX固件、AXF调试文件及批处理脚本可直接编译使用。程序实现了PCAP01内部寄存器读写支持按数据手册调整刷新频率与精度从而兼顾实时性与准确性数据通过UART转RS-485半双工方式远距离发送并集成SPI FlashW25Q16驱动用于保存配置或缓存测量数据。工程在引脚分配、SPI时序、中断处理与错误检测上有完整示例可帮助理解电容传感器采集链路。已有4213人学习下载适合工业传感、物联网节点开发等场景。 第一次拿到 PCAP01 这颗芯片时我差点被它骗了。表面上看它就是个 SPI 接口的电容采集前端写个寄存器、读个数据、串口丢出去完事。可真把电容传感器 PCAP01 数据读取及发送程序完整调通我才意识到这芯片的思路和普通 ADC 完全不一样——它给你的不是你接了多少 pF 的电容而是一个和放电时间强相关的比例码。不把这一点搞清楚后面每一步都会踩坑。这篇文章把我做这个项目的完整过程记录下来从测量原理、硬件连接、寄存器初始化到 SPI 读取、数据换算和串口发送全部走一遍。适合正在用 PCAP01 做液位检测、压力传感、微电容测量或者纯粹被这颗芯片的手册绕晕的工程师参考。我会把调试中真正卡住我的地方单独拉出来讲这些在数据手册里可不会写。1. PCAP01 的测量核心为什么说它测的是时间而不是电容1.1 CDC 电路的工作方式PCAP01 内部是一个完整的 CDCCapacitance-to-Digital Converter前端。它的测量路径不是把电容值直接送进 ADC而是让电容通过一个已知电阻放电用内部比较器捕捉放电过程的起始和结束时刻再用高分辨率 TDCTime-to-Digital Converter时间数字转换器把这个时间差量化成数字。这听起来有点绕但打个比方就好懂了你用一个固定容量的水杯参考电容和一个待测水杯传感器电容同时放水水通过同样的排水口排出。待测水杯的水放完需要多长时间和它的容量直接相关。PCAP01 做的事情就是精确测量这个放水的时间。TDC 的时间分辨率可以做到皮秒级所以等效出来的电容分辨率能做到几 fF 甚至更低。这也是为什么它特别适合微小电容变化的场景比如接近感应、薄膜厚度检测、或者高精度液位计。普通 ADC 加 RC 振荡器的方案在这些场景下分辨率和温漂都会被甩开一个量级。1.2 为什么参考电容模式是关键PCAP01 可以工作在单通道或双通道模式。单通道模式下它只测量一路传感器电容双通道模式则同时接传感器电容和参考电容。我在项目中用的是双通道、参考电容比值模式也就是让传感器电容 C_sensor 和参考电容 C_ref 一起参与测量最终输出的是两者的比例关系。这样做的直接好处是如果供电电压波动、环境温度漂移、或者芯片本身的增益发生变化传感器电容和参考电容会同时受到同样的影响比例关系基本不变。这相当于给测量加了免费的共模抑制。实测下来这个模式下的短时稳定性明显好于单通道模式尤其是连续工作半小时之后单通道方案的数据会缓慢漂移而比值模式则稳得多。1.3 输出到底是什么PCAP01 最终给你的是一个 24 位的原始码外加若干状态位。这个原始码不是直接的电容值也不是线性的 pF 数。它内部和放电时间、参考电容、传感器电容都存在非线性关系。所以项目里真正困难的地方不在 SPI 读回那几个字节而在你怎么把原始码换算成人能看懂的电容值。这个我放到后面第五部分专门讲。2. 硬件连接与上电复位布线、引脚和最容易翻车的细节2.1 SPI 引脚的连接方式PCAP01 的 SPI 接口不算复杂SCK、SDO、SDI、CSB 四根线加上电源和地。我用的主控是 STM32F1033.3V 电平可以直接连接。如果你用 5V 的单片机务必加电平转换PCAP01 的绝对最大额定电压扛不住 5V我见过有人直接把 5V 怼上去芯片当场冒烟。引脚连接建议如下PCAP01 引脚功能MCU 引脚SCKSPI 时钟SPI2_SCKSDI数据输入MOSISPI2_MOSISDO数据输出MISOSPI2_MISOCSB片选低有效任意 GPIORSTB复位低有效任意 GPIOVOCI输出驱动器类型配置看手册配置一个我踩过的坑是 SDO 和 SDI 接反。这两脚名称看起来对称但有的开发板丝印标注是MOSI/MISO而不是SDI/SDO一粗心就接反。接反的典型症状是写寄存器时好像写进去了读回来的全是 0xFF 或者 0x00非常迷惑。排查方法很简单短接 MCU 的 MOSI 和 MISO 自发自收一次先确认 SPI 外设本身没问题再怀疑 PCAP01。2.2 VOCI 引脚和 RSTB 复位时序VOCI 引脚决定 SDO 的输出驱动类型是推挽输出还是开漏输出。这个引脚接法不同直接决定你能不能读回数据。如果 VOCI 悬空或者接错SDO 可能根本拉不出有效电平SPI 读回来的数据就是乱的。我的经验是严格按照数据手册里推荐的接法来手册写着接高就接高写着接低就接低不要想当然。RSTB 复位引脚的时序也有讲究。上电后要给芯片一段稳定时间再把 RSTB 拉高解除复位。我最早用 GPIO 一下就拉高了结果读状态寄存器总是读出异常值。后来改成上电后延时 10ms拉低 RSTB 保持 10us 以上再拉高再等 1ms 让内部校准完成。按照这个时序一次就过。2.3 参考电容的选型和布线既然用了比值模式参考电容的稳定性就直接决定最终数据的稳定性。我试过 X7R 材质的电容做参考电容数据跳得没法看换成 C0G/NPO 材质之后噪声水平肉眼可见地降下来。如果你要求高可以用 0.1% 精度、温漂系数极小的薄膜电容。布线时参考电容尽量靠近芯片的 CREF 引脚避免走线过长引入寄生电容。另外电源退耦也很关键PCAP01 对 VDD 上的毛刺比较敏感我习惯在电源引脚旁边放一个 100nF 和 10uF 组合的退耦电容。做到这一条之后读数的跳动幅度大约能减少 30% 到 50%。3. 初始化寄存器把 PCAP01 从出厂状态调到我要的状态3.1 配置寄存器组的结构PCAP01 上电后不会自动进入你想要的测量模式必须先通过 SPI 写配置寄存器组。它内部的配置寄存器和普通芯片不一样不是一张简单的寄存器表而是一组按特定顺序写入的 24 位配置字包含主配置、测量序列配置、采样率配置、中断配置等。这里有一个重要认知不同固件版本的 PCAP01寄存器数量和位域定义可能有差别。我建议拿到芯片后先读一次 ID 寄存器或者状态寄存器确认固件版本再翻对应的数据手册章节。不要在网上随便找一段寄存器配置就抄我一开始就是抄了一个老版本示例结果芯片一直在错误配置下跑数据怎么读都不对。3.2 典型初始化流程我把初始化流程总结成四步复位芯片等待内部初始化完成。通过 SPI 写配置寄存器组配置测量模式、通道选择、采样率、增益等。回读配置寄存器逐位校验写入是否成功。配置 DREAD 相关寄存器设置数据输出速率启动测量。写寄存器的基础函数大致长这样示意代码具体命令字以你的芯片版本手册为准#define PCAP01_CS_LOW() HAL_GPIO_WritePin(PCAP01_CS_GPIO_Port, PCAP01_CS_Pin, GPIO_PIN_RESET) #define PCAP01_CS_HIGH() HAL_GPIO_WritePin(PCAP01_CS_GPIO_Port, PCAP01_CS_Pin, GPIO_PIN_SET) // 写一个24位配置字 uint8_t pcap01_write_cfg(uint16_t addr_cmd, uint32_t cfg24) { uint8_t buf[5]; buf[0] (uint8_t)(addr_cmd 8); buf[1] (uint8_t)(addr_cmd 0xFF); buf[2] (uint8_t)(cfg24 16); buf[3] (uint8_t)(cfg24 8); buf[4] (uint8_t)(cfg24); PCAP01_CS_LOW(); HAL_SPI_Transmit(hspi1, buf, 5, 100); PCAP01_CS_HIGH(); return 0; }注意每次操作 PCAP01 的 SPI 事务都必须以 CSB 拉低开始、拉高结束中间不能有其他 GPIO 操作干扰。只要 CSB 高电平持续时间不够或者时序上有毛刺芯片内部的状态机就可能错乱。3.3 采样率的设置思路DREAD 寄存器控制着数据的更新速率。我的项目里传感器是缓慢变化的液位不需要高速采样所以把数据率设在 1kSPS 左右然后在软件里再做一次 16 次滑动平均相当于降到 62.5SPS 输出。这样做的好处是既有足够的时间分辨率又能把随机噪声压下去。如果你的应用场景是快速变化的动态信号可以把数据率调高但要注意数据率越高单次测量的积分时间越短分辨率会相应下降。这是 PCAP01 这类时间数字转换器的固有权衡需要在分辨率和速度之间做取舍。4. 数据读取流程SPI 时序、DRDY 信号与连续读取实现4.1 一次完整的读取时序PCAP01 测量完成后数据会锁存在内部寄存器里等待主机来读。芯片提供了 DRDY数据就绪引脚也可以查询状态寄存器的标志位。我强烈建议不要盲目地定时去读而是用 DRDY 引脚做同步或者至少读状态寄存器确认数据有效再读数据。完整读取过程如下等待 DRDY 引脚拉低表示新数据已准备好。拉低 CSB。发送读数据命令和地址。连续读取 32 个时钟获取状态位和 24 位原始数据。拉高 CSB。处理数据进入下一轮等待。4.2 读取代码实现以下是基于 STM32 HAL 库的读取示例示意代码uint32_t pcap01_read_data(void) { uint8_t tx_cmd[2] {0xC0, 0x00}; // 示意读命令 地址 uint8_t rx_buf[5] {0}; // 等待DRDY有效超时保护 uint32_t timeout 10000; while (HAL_GPIO_ReadPin(DRDY_GPIO_Port, DRDY_Pin) ! GPIO_PIN_RESET) { if (--timeout 0) break; } PCAP01_CS_LOW(); HAL_SPI_TransmitReceive(hspi1, tx_cmd, rx_buf, 5, 100); PCAP01_CS_HIGH(); // 组合24位原始数据 uint32_t raw ((uint32_t)rx_buf[2] 16) | ((uint32_t)rx_buf[3] 8) | (uint32_t)rx_buf[4]; return raw; }一个容易忽略的细节很多 SPI 主设备发送命令和读取数据是分两个事务处理的但 PCAP01 要求 CSB 在整个命令数据读取期间保持低电平。如果中间 CSB 被拉高了一次芯片会认为事务结束后续读回来的数据就是错的。所以务必使用 TransmitReceive 这种半双工连续模式而不是先 Transmit 再 Receive。4.3 轮询和中断两种方式的选择我用过两种读取方式各有优劣。轮询方式代码简单在 MCU 任务不重的时候完全够用缺点是 CPU 会空转等待 DRDY中断方式用 DRDY 上升沿或下降沿触发CPU 可以去做别的事情代码复杂一点但实时性和资源占用都好很多。如果你的系统里还有显示、按键、通信等任务建议直接用中断。PCAP01 的数据率不高中断频率完全在 MCU 的处理能力之内。我最终使用的是 DRDY 下降沿中断在中断服务函数里只做一件事读取原始数据并放入环形缓冲区然后立即退出数据处理放到主循环里做。这样可以避免在中断里做浮点运算把中断响应时间压到最短。5. 从 24 位原始码到真实电容值转换与校准5.1 为什么不能直接查表PCAP01 的原始码和电容值之间存在非线性关系而且这个关系会受芯片制造偏差、参考电容实际容值、温度等多方面因素影响。同一个原始码放在两块不同的 PCAP01 上对应的电容值不一定完全相同。这就是为什么读原始码直接除以一个系数的做法不靠谱。5.2 两点标定法我项目里用的是最经典的两点线性标定。找两个已知容值的标准电容分别接到传感器通道上记录对应的原始码然后建立线性关系C (D - D1) * (C2 - C1) / (D2 - D1) C1其中 D1、D2 是两个标准电容对应的原始码C1、C2 是它们的已知容值。实际操作中我拿了一个 10pF 和一个 100pF 的 C0G 电容做标定效果良好。需要注意的是如果测量范围跨度很大比如从 1pF 到 1000pF两点线性标定可能不够需要增加标定点做分段线性或者多项式拟合。对于窄量程的传感器两点标定已经能到 0.1% 级别的精度了。5.3 用参考电容做比值校正在双通道比值模式下原始码本质上还包含 C_sensor 和 C_ref 的比例信息。这意味着即使参考电容本身容值有偏差只要它稳定标定后也能得到正确结果。我还会在每次测量时同时记录参考通道的原始码如果发现参考通道的读数发生明显漂移就说明硬件环境有问题比如温漂、湿气进入、参考电容老化这个数据可以作为系统自检的信号。5.4 软件滤波的必要性实测数据里总有随机噪声我做了两级处理滑动平均连续取 8 到 16 次原始码求平均压制高频噪声。中值滤波在滑动平均之前先做一次 3 点中值滤波剔除偶发的毛刺值。这两级滤波加在一起数据曲线平滑很多。但要注意滤波会引入延迟对于快速变化的场景需要折中滤波窗口大小。我的液位传感器变化速度很慢16 点平均完全无感但如果你做的是高速振动检测就要谨慎了。实际测试数据我记录了一组对比未滤波之前原始码跳动幅度约 ±20 LSB16 点滑动平均后跳动降到 ±3 LSB 以内换算成电容值稳定度大约 0.01pF对这个项目来说完全够用。6. 串口发送程序数据帧格式、校验与整机联调6.1 帧格式设计数据读出来、换算完成最后要交给上位机。我用的是 115200 波特率串口为了让上位机解析方便、不容易粘包设计了一个固定帧格式字节内容说明00xAA帧头10x55帧头验证2length负载长度3type0x01原始码0x02电容值4-7payload4 字节int32 或 float8status状态位9crc8从帧头到 status 的 CRC 校验帧头用 0xAA 0x55 双字节基本可以避免错位。CRC8 我用的是多项式 0x31和 Dallas/Maxim 的 1-Wire CRC 一致代码网上很多自己实现也不难。加上校验之后上位机可以放心地使用数据不用担心中间偶尔出现一帧坏数据导致曲线毛刺。发送代码很简单在 UART 已经初始化的前提下拼好缓冲区直接 HAL_UART_Transmitvoid pcap_send_frame(uint8_t type, int32_t value, uint8_t status) { uint8_t buf[10]; buf[0] 0xAA; buf[1] 0x55; buf[2] 5; buf[3] type; buf[4] (uint8_t)(value 24); buf[5] (uint8_t)(value 16); buf[6] (uint8_t)(value 8); buf[7] (uint8_t)(value); buf[8] status; buf[9] crc8_buf(buf, 9); HAL_UART_Transmit(huart1, buf, 10, 100); }6.2 上位机配合与调试验证发送频率我设置在 20Hz也就是每 50ms 发一帧上位机上用 Python 的 pySerial 读取解析成电容值后画实时曲线。这里有个小技巧调试时先发原始码不发换算后的电容值等确认原始码稳定了再做换算。因为换算公式如果写错你看到的数据曲线会非常离谱出现负电容、突变等而你很难判断到底是换算的问题还是读取的问题。分步验证能大大缩小排查范围。6.3 读不到数据时的排查三板斧如果按上面的流程做完串口还是没有有效数据我的排查顺序是量 PCAP01 的 VDD 电压是否稳定RSTB 是否已经解除复位DRDY 引脚是否在变化。DRDY 一点动静没有说明芯片根本没在跑测量先查配置和复位时序。用示波器看 SPI 波形重点看 CSB 时序是否正确、SCK 上有没有毛刺、SDO 上有没有正常返回数据。波形正常但数据不对查命令字和寄存器地址。回读配置寄存器逐位核对写入值。回读值和写入值不一致基本可以确定是 SPI 模式、极性、相位配置不对或者 SDO/SDI 接反了。我做过最蠢的一次SPI 极性相位配置反了但奇怪的是写寄存器偶尔能成功数据偶尔能读出来让我误以为是接触不良折腾了一下午才发现是 SPI 模式配置的问题。所以如果数据时好时坏先检查 CPOL/CPHA别急着怀疑芯片坏了。6.4 最后的一点个人体会整个项目做下来我的最大感受是PCAP01 这类芯片硬件连接和 SPI 读写只占 20% 的工作量剩下 80% 的时间都在跟数据语义较劲。它输出的原始码不是直观的物理量你需要理解内部测量原理设计校准流程处理噪声和漂移最后才能让数据真正可用。如果你也在做类似的项目我建议按这个顺序推进先打通 SPI 读写确认能稳定读回原始码再做硬件稳定性测试确保数据不跳然后做两点标定把电容值算准最后才是串口发送和上位机联调。每一步都验证通过再进入下一步看似慢实际是最快的路径。本文还有配套的精品资源点击获取