
简介这份资源是面向嵌入式开发者的tpc116s8设备完整可用驱动程序适合从事工业控制、自动化或物联网项目、需要让操作系统识别并控制该硬件的工程师与学习者。压缩包共126个文件约2.36MB以C源码与头文件为主体包含33个.h、30个.c并附带12个.o、12个.d等编译中间文件以及11个.crf、8个.s汇编文件另有hex、map、axf、uvprojx、uvoptx等工程与烧录产物还包含bat批处理、lst列表、sct分散加载及说明文档覆盖从源码到可执行固件的完整链路。内容预览显示涉及STM32F10x系列外设驱动如定时器、Flash、RCC、ADC、I2C、CAN、USART等模块并包含UART通信相关代码便于理解串口数据传输的配置与管理。已有1261人学习下载可作为设备驱动调试、工程移植与固件维护的参考素材。1. 从一颗“哑巴”ADC说起tpc116s8驱动到底解决什么问题如果你手头有一块 tpc116s8 的板子上电后 SPI 读出来全是 0xFF 或者 0x00别急着怀疑芯片坏了——十有八九是驱动时序没对上。tpc116s8 是一颗 8 通道、12 位精度的逐次逼近型 ADC支持 SPI 接口常见于多路电压采集、工业传感器信号调理这类场景。它本身不复杂但 datasheet 里那些“看起来能跑”的时序参数实际写进 MCU 里经常翻车。这份“完整可用驱动程序”就是冲着这个痛点来的它不是那种只跑通一路、换个通道就飘的 demo而是把 8 个通道的采集、配置、校准流程都封好了拿过来改改引脚就能用。适合谁做嵌入式采集板、搞 STM32 或 Linux 平台 SPI 外设的兄弟尤其是被“读出来数据不对但示波器看波形又没错”折磨过的人。下面我按“先搞懂它怎么工作、再动手接进来、最后把坑填平”的顺序拆一遍。2. tpc116s8 的 SPI 时序与寄存器模型先把黑匣子拆开看2.1 为什么不能直接拿通用 SPI 驱动套上去tpc116s8 的 SPI 不是那种“发一个字节收一个字节”的傻瓜模式。它的帧结构是前 4 个 bit 是控制位通道选择 单端/差分 省电模式后面跟着 12 个 bit 的转换结果。也就是说一次完整的读写是 16 个时钟周期但数据不是字节对齐的。很多兄弟直接用 HAL_SPI_TransmitReceive 发两个字节结果读回来的数永远差几位就是因为没处理这个 412 的位偏移。更麻烦的是tpc116s8 在 CS 拉低之后需要至少 15 个时钟周期才能出有效数据而且转换期间不能随便切通道。如果你用 DMA 连续采通道之间会串扰。常见做法是每次采集前先发配置字等转换完成查 BUSY 引脚或者延时再发空字节把结果读出来。这份驱动里把“配置-等待-读取”做成了一个原子操作避免你在中断里手动拼时序。2.2 寄存器配置字怎么拼通道、模式与省电位tpc116s8 没有传统意义上的“寄存器地址”它的配置全靠那 4 个控制位。具体定义如下位序名称含义D15START1启动转换0忽略D14CH1通道选择高位D13CH0通道选择低位D12SGL/DIFF1单端0差分D11-D0DATA12 位转换结果只读通道选择逻辑是CH10, CH00 选通道 0CH10, CH01 选通道 1以此类推到通道 3。但注意tpc116s8 实际有 8 个通道高 4 通道需要通过二次配置或者外部多路复用器切换——这一点 datasheet 写得比较隐晦很多驱动只做了 4 通道就以为全了。我一般会在驱动里加一个 channel_map 数组把 0~7 映射到具体的配置字避免每次手动算。拼配置字的代码大概长这样// 构造 tpc116s8 配置字start1, ch0~7, single_ended1 uint16_t tpc116s8_build_cfg(uint8_t ch, uint8_t single_ended) { uint16_t cfg 0; cfg | (1 15); // START 位必须置 1 cfg | ((ch 0x03) 13); // 低两位通道选择 cfg | (single_ended ? (1 12) : 0); // 单端/差分 // 高 4 通道需要额外拉高一个 GPIO 做 bank 切换这里不展开 return cfg; }逻辑说明这个函数只处理了低 4 通道的位拼接高 4 通道的 bank 切换在驱动里是用一个独立 GPIO 控制的因为 tpc116s8 的 A2/A3 引脚在某些封装里是复用出来的。参数 ch 传 0~7函数内部只取低两位高通道靠外部引脚。如果你只采 4 路直接传 0~3 就行。2.3 时序参数延时到底给多少才不玄学tpc116s8 的转换时间典型值是 1.5us但 SPI 时钟最高只能到 10MHz。这意味着如果你用 10MHz 的 SCLK一个时钟周期 0.1us16 个周期才 1.6us刚好卡在转换时间边缘。实际调试时我建议把 SCLK 降到 4MHz 左右给转换留足余量。另外CS 拉低到第一个 SCLK 下降沿之间要有至少 20ns 的建立时间很多 MCU 的 SPI 外设默认是 0需要手动在 CS 拉低后加一个 __NOP() 或者 delay_ns(50)。还有一个血泪经验如果你用 Linux 的 spidev默认的 SPI 模式是 mode 0但 tpc116s8 要求数据在 SCLK 下降沿被采样也就是 CPOL0, CPHA1对应 mode 1。设错了模式读出来的数据会整体移一位看起来像“数值翻倍”或者“通道错乱”。在设备树里改 spi-cpha 和 spi-cpol 属性就行别在用户态硬调。3. 把驱动接进 STM32 HAL 库从 CubeMX 配置到第一个采样值3.1 CubeMX 里 SPI 参数怎么点打开 CubeMX选好你的 STM32F4 或 F1 型号在 Connectivity 里启用 SPI。关键参数只有几个Mode 选 Full-Duplex MasterData Size 选 16 Bits因为 tpc116s8 是 16 位帧Clock Polarity 选 LowClock Phase 选 2 Edge这就是 CPHA1。Prescaler 先设 16让 SCLK 落在 2~4MHz 之间。NSS 选 Software因为我们要手动控制 CS 引脚方便在通道切换时拉高拉低。生成代码后HAL 会给你一个 hspi1 句柄。但别急着用 HAL_SPI_TransmitReceive因为那个函数是按字节操作的16 位数据它会拆成两个字节发时序会乱。正确做法是用 HAL_SPI_TransmitReceive 的 16 位版本或者直接操作寄存器。我一般用 HAL_SPI_TransmitReceive(hspi1, (uint8_t*)tx, (uint8_t*)rx, 1, 100)注意最后一个参数是“数据个数”不是字节数。传 1 表示发一个 16 位数据。3.2 完整采集函数的写法与参数解释下面这个函数是我在驱动里封装好的单通道采集直接抄就能用// 单次采集ch0~7, 返回 0~4095 的 ADC 值失败返回 0xFFFF uint16_t tpc116s8_read_channel(SPI_HandleTypeDef *hspi, uint8_t ch) { uint16_t tx, rx; // 1. 拉低 CS HAL_GPIO_WritePin(TPC_CS_GPIO_Port, TPC_CS_Pin, GPIO_PIN_RESET); // 2. 如果需要高 4 通道先切 bank if (ch 4) { HAL_GPIO_WritePin(TPC_BANK_GPIO_Port, TPC_BANK_Pin, GPIO_PIN_SET); ch - 4; } else { HAL_GPIO_WritePin(TPC_BANK_GPIO_Port, TPC_BANK_Pin, GPIO_PIN_RESET); } // 3. 发配置字启动转换 tx tpc116s8_build_cfg(ch, 1); if (HAL_SPI_TransmitReceive(hspi, (uint8_t*)tx, (uint8_t*)rx, 1, 100) ! HAL_OK) { HAL_GPIO_WritePin(TPC_CS_GPIO_Port, TPC_CS_Pin, GPIO_PIN_SET); return 0xFFFF; } // 4. 等待转换完成tpc116s8 典型 1.5us这里给 5us 余量 delay_us(5); // 5. 发空帧读结果 tx 0x0000; HAL_SPI_TransmitReceive(hspi, (uint8_t*)tx, (uint8_t*)rx, 1, 100); // 6. 拉高 CS HAL_GPIO_WritePin(TPC_CS_GPIO_Port, TPC_CS_Pin, GPIO_PIN_SET); // 7. 取低 12 位 return rx 0x0FFF; }逻辑说明第 3 步发配置字时tpc116s8 会在第 15 个时钟沿启动转换但结果不会立刻出现在 MISO 上。所以第 4 步的延时是必须的不能省。第 5 步再发一个空帧这时候 MISO 上才是真正的转换结果。参数 ch 传 0~7函数内部处理 bank 切换。返回值是 12 位无符号整数对应 0~VREF 的电压。如果你发现读出来一直是 0 或者 4095先查 VREF 引脚有没有接对再查 CS 是不是被其他外设拉低了。3.3 多通道轮询与数据稳定性处理8 个通道轮询的时候通道之间会互相干扰尤其是高阻信号源。我一般会在驱动里加一个简单的滑动平均每个通道存最近 4 次采样取中间值。别用算术平均因为如果有一次被干扰拉飞平均值会整体偏移。取中值排序后取中间那个能滤掉大部分尖峰。另外通道切换后第一次采样建议丢弃。因为 tpc116s8 内部有个采样保持电容切通道后需要几个周期才能稳定。驱动里可以加一个 tpc116s8_read_channel_avg 函数连续采 3 次丢掉第一次后两次取平均。这样出来的数据基本不用再在应用层做滤波了。4. 避坑与排查那些 datasheet 没写但一定会遇到的问题4.1 现象读出来永远是 0xFFF换通道也没用原因SPI 模式设错了。tpc116s8 要求 CPHA1如果你用默认的 mode 0数据在第一个边沿就被采样了但此时 MISO 还是高阻态MCU 内部上拉把它拉成了高电平所以读出来全是 1。解决在 CubeMX 里把 Clock Phase 改成 2 Edge或者在 Linux 设备树里加 spi-cpha。4.2 现象低 4 通道正常高 4 通道读出来跟低通道一样原因bank 切换引脚没控制。tpc116s8 的 A2/A3 在某些封装里是复用引脚需要外部拉高才能选高通道。很多驱动只发了配置字没动这个 GPIO。解决在采集函数里加 bank 引脚控制并且注意 bank 切换后要延时 1us 再发配置字否则内部多路复用器还没稳定。4.3 现象数据偶尔跳变示波器看 SCLK 有毛刺原因CS 拉低和第一个 SCLK 之间没有建立时间。STM32 的 SPI 外设一旦使能SCLK 会立刻开始翻转如果 CS 是软件控制的中间会有几十纳秒的窗口。解决在 CS 拉低后加一个 __NOP() 或者 delay_ns(50)再调 HAL_SPI_TransmitReceive。别小看这 50ns能消掉大部分毛刺。4.4 现象用 DMA 连续采集时通道串扰严重原因DMA 是连续发帧的但 tpc116s8 每次转换需要 1.5usDMA 不等转换完成就发下一帧导致上一通道的电荷还没释放就切到了下一通道。解决别用 DMA 做多通道连续采集改用定时器触发单次采集每次采集之间留 10us 以上的间隔。如果非要高速加外部模拟开关做通道切换让 ADC 一直采同一个通道。4.5 现象Linux 下 spidev 读出来全是 0原因spidev 默认的 bits_per_word 是 8而 tpc116s8 需要 16 位帧。如果你用 spi_ioc_transfer 发两个 8 位时序会断成两截。解决在传输结构体里把 bits_per_word 设成 16len 设成 2字节数或者直接用 16 位模式。另外spidev 的 speed_hz 别设太高先降到 1MHz 调通再往上加。5. 进阶技巧用定时器触发 环形缓冲做无阻塞采集如果你要做 8 通道同步采集又不想让 CPU 在 SPI 上死等我一般会这么干用一个定时器每 100us 触发一次转换在定时器中断里只做一件事——拉低 CS启动 SPI 传输然后立刻退出。SPI 传输完成中断里再拉高 CS把结果写进环形缓冲。这样 CPU 占用率能降到 5% 以下。具体实现上STM32 的 HAL 库可以用 HAL_SPI_TransmitReceive_IT但要注意 16 位数据的指针转换。环形缓冲用两个指针head 和 tail主循环里读 tail 指针取数据中断里更新 head。缓冲大小设成 64 的倍数方便用位运算取模。验证方法很简单给通道 0 接一个已知电压比如 1.25V用万用表量一下然后看串口打印出来的值是不是在 1.25/3.3*4095 ≈ 1551 附近。如果偏差超过 ±5 个 LSB先查 VREF 的纹波再查 PCB 上模拟地和数字地是不是单点连接。我见过太多人把 VREF 直接接到 3.3V 数字电源上结果噪声大得没法看。还有一个技巧tpc116s8 的省电模式可以通过配置字的 D12 位控制但如果你在高速采集时频繁进出省电模式反而会增加功耗。我一般只在空闲超过 1ms 时才发省电命令平时就让它一直开着。从那以后我每次调新板子都强制先跑一遍单通道直流测试确认 VREF 和地没问题了再上多通道轮询。希望帮到你。本文还有配套的精品资源点击获取