ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

MAX31855热电偶采集实战:硬件SPI与GPIO模拟时序全解析

MAX31855热电偶采集实战:硬件SPI与GPIO模拟时序全解析 做多通道温度采集那阵子K型热电偶的方案把我折腾得够呛。一开始想用仪表放大器加ADC自己搭调理电路结果冷端补偿和标定弄得我头大最后老老实实换成了MAX31855这颗专用芯片。板子上同时跑了硬件SPI和软件模拟时序两种读取方式——不是闲得慌是因为其中一路的SPI外设被别的器件占了只能拿普通GPIO硬读。这篇就把电路怎么画、两种程序怎么写、数据怎么解析、实际调试踩过哪些坑一次性说清楚。如果你正准备用MAX31855做热电偶测温或者遇到了硬件SPI读不出来、想用GPIO模拟时序的情况这篇文章应该能帮你少走不少弯路。1. 为什么温度采集偏偏选了MAX31855从热电偶的脾气说起1.1 热电偶信号为什么难伺候热电偶的原理是塞贝克效应两种不同金属材料焊接成测量端当测量端和参考端存在温差时回路里会产生一个微弱的直流电压。K型热电偶的灵敏度大约41μV/℃也就是说100℃温差对应的电压也只有4.1mV。这种微伏级信号直接送ADC根本不现实必须先经过高增益、低失调的放大器。更麻烦的是冷端补偿。热电偶测出来的本质是测量端和参考端的温差而我们关心的是测量端的绝对温度。参考端通常在电路板的接线端子处它的温度不是恒定的必须用一个温度传感器测出参考端的绝对温度再和热电偶读到的温差相加才能得到测量端的真实温度。传统方案要在板子上额外放DS18B20或者NTC做冷端补偿再加上精密仪表放大器、独立ADC、软件标定整个模拟链路又长又容易漂。1.2 MAX31855把这堆麻烦集成到了一颗芯片里MAX31855是Maxim现在叫Analog Devices专门为热电偶设计的数字转换芯片。它内部集成了斩波稳定放大器、冷端温度传感器、ADC和SPI接口外部只需要接一颗去耦电容就能工作。MCU通过SPI直接读出测量端的温度和芯片内部的冷端温度省掉了整个模拟调理链路。要注意区分两个容易搞混的型号MAX6675是老一辈的热电偶转换芯片只能读热电偶温度12位分辨率没有冷端补偿输出MAX31855是升级版14位分辨率热电偶温度分辨率0.25℃冷端温度分辨率0.0625℃还带了开路、短路检测。MAX31855根据热电偶类型分为K、J、N、R、S、T、E、B等多种版本比如MAX31855K对应K型热电偶。做一般的工业测温场景K型版本最常见。1.3 为什么同一个芯片我还要写两种读取方式按理说MAX31855就是SPI接口直接接MCU的硬件SPI外设最省事。但在实际项目中我遇到的情况是板子上SPI1挂了Flash存储器SPI2挂了显示屏第三路MAX31855已经没有空闲的硬件SPI可用了。这时候有两种选择换一颗更多SPI外设的主控或者用普通GPIO软件模拟SPI时序。软件模拟时序业内也叫bit-banging本质就是用GPIO手动拉高拉低SCLK、控制CS片选、逐位读取MISO数据。它不依赖芯片的SPI外设理论上只要GPIO够用任何单片机都能实现。除了引脚冲突的场景有些平台根本没有硬件SPI可用——典型的例子是51单片机或者ESP8266这类SPI接口被WiFi协议栈和Flash占用的模块。另外软件模拟时序在调试阶段也特别好用因为每个bit的翻转都清清楚楚用逻辑分析仪一看就知道时序对不对。所以我在这块板子上把两种方式都做了一路用硬件SPI一路用GPIO软件模拟。下文分别讲电路和代码。2. MAX31855外围电路我画的最简电路和三个PCB布局雷区2.1 原理图元件少到不能再少MAX31855的外围电路简单到什么程度去掉电源滤波芯片本身就能正常工作。以K型版本为例标准接法是VCC接3.3VGND接地VCC和GND之间放一颗0.1μF去耦电容电容尽量靠近VCC引脚T接热电偶正极T-接热电偶负极中间不串电阻、不分压直接进芯片SO接MCU的MISO引脚CS接MCU的任意GPIOSCLK接MCU的SCLK引脚或任意GPIO不需要在SO上加外部上拉电阻MAX31855的SO是推挽输出不是开漏注意一个关键点MAX31855的工作电压是3.0V到3.6V推荐用3.3V供电绝不能接到5V上。如果你的主控是5V的51单片机或者Arduino UNOSCLK和CS这两个输入引脚从5V单片机直接接到MAX31855上超出了芯片的绝对最大额定值必须加电平转换或者用电阻分压降到3.3V左右。SO引脚是3.3V输出的5V单片机读它一般没问题因为TTL高电平阈值是2.0V3.3V足够被识别为高电平。2.2 电源和地线的三个雷区雷区一去耦电容放得太远。MAX31855内部有斩波放大器对电源噪声比较敏感。去耦电容如果离VCC引脚超过几毫米走线电感会让高频噪声滤不干净实测表现是温度读数最低位跳来跳去。我习惯在VCC引脚旁边放一颗0.1μF陶瓷电容如果板子空间允许再并联一颗1μF10μF的钽电容或陶瓷电容稳定性会更好。雷区二把芯片放在发热元件旁边。MAX31855的冷端补偿依赖芯片内部的温度传感器也就是说芯片自己感受到的温度就是冷端温度。如果芯片旁边躺着一颗LDO或者功率MOS管热量传导到芯片上冷端温度读数就会偏高最终导致热电偶温度整体偏移。我踩过一次很深的坑把MAX31855放在一个低压差稳压器旁边开机半小时后温度读数漂了将近5℃。后来重新布局把芯片挪远了读数才稳下来。雷区三地线处理不当。热电偶输出的是微伏级信号虽然MAX31855内部已经做了很多努力但外部地线噪声还是会耦合进去。我的做法是MAX31855的地和MCU的数字地走同一个地平面但热电偶输入走线要尽量短远离继电器、电机驱动这类大电流开关信号。T和T-两条线最好双绞如果PCB上没法双绞就平行走线并保持间距一致减少环路面积。2.3 热电偶输入滤波电容的取舍很多人习惯在ADC输入端加一个RC低通滤波滤掉高频干扰但MAX31855这里不能照搬。芯片内部有一个开路检测机制会在热电偶输入端施加偏置电流来检测热电偶是否断线。如果T和T-之间并了一个比较大的电容这个电容会给偏置电流充电影响开路检测的判断而且还会和芯片内部输入阻抗组成RC低通导致转换结果建立时间变长、读数不准。实际测试下来T和T-之间尽量不要加电容最多加100pF到1nF的小电容用于EMC防护。如果现场电磁干扰确实很严重更推荐在热电偶线路上套磁环或者在接线端子处做滤波而不是直接在芯片引脚上并大电容。我见过有人在T/T-之间并了100nF结果读数一直跳、偶尔还报开路错误把电容拆掉就正常了。3. 硬件SPI读取CubeMX配置参数与HAL库的落地代码3.1 CubeMX参数这样填MAX31855才认用STM32的HAL库开发第一步是在CubeMX里把SPI外设配好。MAX31855对SPI时序的要求是空闲时钟为低电平MSB先出数据在SCLK的第一个边沿被主机采样。换算成CubeMX的参数就是参数推荐值原因ModeFull-Duplex Master或Receive OnlyMAX31855不需要MCU发送有效数据但Master模式才能产生SCLK时钟Data Size8 bit一次读4字节拼接成32位First BitMSB FirstMAX31855输出格式固定为MSB在前Clock Polarity (CPOL)LowSCLK空闲时为低电平Clock Phase (CPHA)1 Edge主机在第一个边沿上升沿采样数据Prescaler分频后约1MHz满足时序要求的同时兼顾稳定抗干扰这里最容易搞错的是CPOL和CPHA的组合。MAX31855对应的是SPI Mode 0CPOL0、CPHA0在CubeMX里翻译过来就是Clock Polarity选Low、Clock Phase选1 Edge。如果你配置成了Mode 1或者Mode 2读出来的数据往往错位或者全是0xFF。还有一个容易踩的坑是硬件NSS和软件片选的选择——MAX31855读取时必须把CS拉低完整读完32位再拉高用GPIO软件控制最灵活尤其是多个MAX31855共用一个SPI总线时每个芯片一根CS线硬件NSS根本管不过来。这个话题在嵌入式圈子里经常被提起所谓硬件片选vs软件片选之争在MAX31855这类需要精确控制读取窗口的芯片上答案很明确软件片选。3.2 HAL库读取与解析代码配置好CubeMX生成工程后核心读取代码其实没几行。MAX31855的SO输出本身是32位数据MCU只要产生32个SCLK时钟同时从MISO上把数据收进来就行。HAL_SPI_Receive会一边发送0xFF一边接收数据正好满足产生时钟的需求// max31855_hw_spi.c —— 硬件SPI方式读取原始32位数据 #include main.h #include spi.h #define MAX31855_CS_LOW() HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET) #define MAX31855_CS_HIGH() HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET) uint32_t MAX31855_ReadRaw(void) { uint8_t buf[4] {0}; uint32_t raw 0; MAX31855_CS_LOW(); // 加一小段延时确保CS拉低后SO输出有效 for (volatile int i 0; i 10; i); // 产生4字节时钟读回32位数据 HAL_SPI_Receive(hspi1, buf, 4, HAL_MAX_DELAY); MAX31855_CS_HIGH(); raw ((uint32_t)buf[0] 24) | ((uint32_t)buf[1] 16) | ((uint32_t)buf[2] 8) | ((uint32_t)buf[3]); return raw; }拿到原始32位数据之后不要急着直接转温度先看一下解析函数。MAX31855的32位数据里包含热电偶温度、冷端温度、故障标志三部分格式比较特殊必须按位拆解// max31855_parse.c —— 解析MAX31855原始数据 typedef struct { float tc_temp; // 热电偶温度单位℃ float ref_temp; // 冷端温度单位℃ uint8_t fault; // 综合故障标志1表示有故障 uint8_t scv; // 热电偶短路到VCC uint8_t scg; // 热电偶短路到GND uint8_t oc; // 热电偶开路 } max31855_data_t; int MAX31855_Parse(uint32_t raw, max31855_data_t *d) { d-oc (raw 0) 1; d-scg (raw 1) 1; d-scv (raw 2) 1; d-fault (raw 16) 1; if (d-fault || d-oc || d-scg || d-scv) { d-tc_temp 0.0f; d-ref_temp 0.0f; return -1; // 返回-1表示数据无效 } // 热电偶温度bit31~bit1814位二进制补码LSB0.25℃ int16_t tc (int16_t)((raw 18) 0x3FFF); if (tc 0x2000) { tc | 0xC000; // 14位符号扩展到16位 } d-tc_temp tc * 0.25f; // 冷端温度bit16~bit413位二进制补码LSB0.0625℃ int16_t ref (int16_t)((raw 4) 0x1FFF); if (ref 0x1000) { ref | 0xE000; // 13位符号扩展到16位 } d-ref_temp ref * 0.0625f; return 0; }在主循环里读取间隔要大于MAX31855的转换时间。手册给出的典型转换时间是100ms也就是说你每次发起SPI读取之前必须确保上一次读完之后至少过了100ms否则读到的还是旧的转换结果。很多初学者发现温度值半天不变其实就是读取频率太快读到的都是上一次的数据。3.3 硬件SPI的几个隐藏坑第一个坑是CS拉低到第一个SCLK之间的建立时间。MAX31855在CS下降沿之后SO上会立即输出32位数据的最高位但如果你SPI速度很快CS刚拉低就立刻产生时钟SO可能还没稳定导致最高位读错。解决办法就是在CS拉低之后加一个微秒级别的延时或者把SPI时钟降到1MHz左右。实测1MHz下不加延时通常也能正常工作但稳妥起见我还是会加。第二个坑是CS要等4字节全部读完才能拉高。HAL_SPI_Receive是一次性读4个字节中途不会释放CS这没问题。但如果你用的是自己写循环单字节读取每读一个字节就把CS拉高那数据就彻底乱了。MAX31855要求一次CS低电平期间连续读满32位中途CS不能拉高。第三个坑是SPI总线挂了多个设备时的CS保护。MAX31855的SO是三态输出当CS为高电平时它不会驱动总线所以多个SPI设备共用MISO线是安全的。但要注意如果某个设备的CS没有被正确拉高它可能会持续驱动MISO导致总线上其他设备读数据时发生冲突。排查这类问题的时候用逻辑分析仪同时看CS线和MISO线一眼就能看出是哪个设备在捣乱。4. 软件模拟时序手写GPIO读取的完整流程与防错技巧4.1 软件模拟的本质和时序流程软件模拟SPI说白了就是不用芯片自带的SPI外设用普通GPIO按照SPI协议把时钟和数据一根一根地抠出来。对于MAX31855这种从机设备我们需要模拟的是主机侧的行为产生SCLK、控制CS、读取SO。对照MAX31855的手册时序一次完整的读取过程是这样的先把SCLK置于低电平空闲状态把CS拉低此时MAX31855会把32位数据的最高位bit31放到SO引脚上循环32次每次做两件事把SCLK拉高主机在上升沿读取SO引脚的电平得到当前位数把SCLK拉低产生下降沿MAX31855会把下一位数据放到SO上32位全部读完后把CS拉高结束通信为什么是先拉高读数据、再拉低让芯片更新因为MAX31855是在SCLK的下降沿更新SO输出所以主机必须等到SCLK为高电平时SO上的数据才是稳定有效的。这个顺序一旦搞反读到的数据就会整体错位。4.2 完整代码HAL库版和寄存器加速版以下代码以STM32为例用HAL库的GPIO操作做示范。如果你用的平台不是STM32逻辑完全一样只需要把GPIO操作部分替换成对应平台的API即可// max31855_soft_spi.c —— 软件模拟SPI读取MAX31855 #define SCLK_PIN GPIO_PIN_5 #define CS_PIN GPIO_PIN_4 #define MISO_PIN GPIO_PIN_6 #define GPIO_PORT GPIOA #define SCLK_H() HAL_GPIO_WritePin(GPIO_PORT, SCLK_PIN, GPIO_PIN_SET) #define SCLK_L() HAL_GPIO_WritePin(GPIO_PORT, SCLK_PIN, GPIO_PIN_RESET) #define CS_H() HAL_GPIO_WritePin(GPIO_PORT, CS_PIN, GPIO_PIN_SET) #define CS_L() HAL_GPIO_WritePin(GPIO_PORT, CS_PIN, GPIO_PIN_RESET) #define MISO_READ() HAL_GPIO_ReadPin(GPIO_PORT, MISO_PIN) // 微秒延时根据不同平台实现STM32可以用DWT或者定时器 extern void delay_us(uint32_t us); uint32_t MAX31855_ReadRawSoft(void) { uint32_t raw 0; CS_L(); delay_us(1); // CS拉低后等待SO输出有效 for (int i 31; i 0; i--) { SCLK_H(); // 上升沿主机采样 if (MISO_READ()) { raw | (1UL i); } SCLK_L(); // 下降沿芯片更新下一位 delay_us(1); // 保持时序稳定尤其是MCU主频较高时 } CS_H(); return raw; }如果你的MCU主频很高比如STM32F4跑168MHzHAL库的GPIO操作本身会消耗一定指令周期但还不够慢逻辑分析仪抓出来SCLK高电平时间可能只有几十纳秒。MX31855内部虽然能承受MHz级别的SCLK但SO数据的建立时间如果不够就会出现偶发错位。解决办法很简单在SCLK_H()之后、读取MISO之前加几个空操作或者像我上面那样在SCLK_L()之后加delay_us(1)。这个延时的上一步是拉低SCLK给芯片留出更新SO的时间。追求极致速度的话可以直接操作寄存器省去HAL库函数调用的开销// max31855_soft_spi_fast.c —— 寄存器操作版本速度更快 #define SCLK_PIN (1 5) #define CS_PIN (1 4) #define MISO_PIN (1 6) uint32_t MAX31855_ReadRawSoftFast(void) { uint32_t raw 0; GPIOA-BSRR CS_PIN; // CS高先确保 GPIOA-BRR CS_PIN; // CS低 __NOP(); for (int i 31; i 0; i--) { GPIOA-BSRR SCLK_PIN; // SCLK拉高 __NOP(); // 留出SO建立时间 if (GPIOA-IDR MISO_PIN) { raw | (1UL i); } GPIOA-BRR SCLK_PIN; // SCLK拉低 } GPIOA-BSRR CS_PIN; // CS高 return raw; }寄存器版本在F103这种72MHz主频下读一次32位数据大约几十微秒和硬件SPI1MHz下约32微秒差距不大。但要注意GPIOA-BSRR和BRR的位操作是原子的直接操作GPIO时别把输入输出方向搞错。MISO引脚必须配置为输入模式最好是浮空输入或者上拉输入千万不要配成推挽输出否则会和外部的SO输出打架。4.3 软件模拟最常翻车的三个点翻车点一读出来全是0xFFFFFFFF。最常见的原因是SCLK空闲电平反了。软件模拟时序中初始状态SCLK必须是低电平CS拉低后芯片才能正常工作。如果初始化时不小心把SCLK拉高了芯片会认为来了一堆多余的时钟边沿SO上的数据位就全乱了。检查方式很简单CS拉低后、循环开始前用逻辑分析仪看SCLK是不是处于低电平。翻车点二数据偶尔错位一个bit或者整体偏移。这通常是采样时刻太早或者太晚。上升沿之后、SO数据稳定之前就去读会读到上一bit的值下降沿之后立刻读也会读到芯片还没来得及更新的旧值。解决办法就是像上面代码里那样在SCLK_H()之后加几个指令周期再读MISO。我遇到过一种情况板子一上电就工作正常运行几分钟后偶发错位最后发现是定时器中断打断了读取过程导致某个bit的采样时间被拉长。处理方法是读取32位期间临时关闭中断或者用临界区保护。翻车点三MISO引脚配置错误。软件模拟SPI时MISO要配置成输入但很多人直接复制了别的GPIO输出代码把MISO配成了推挽输出。这样单片机引脚本身输出高电平和MAX31855的SO输出互相打架读回来的数据要么全是1要么不稳定。检查引脚模式是很基础但非常容易忽略的一步特别是用CubeMX这类工具自动生成代码时引脚复用配置很容易看走眼。5. 32位数据的准确解码故障位、补码扩展、冷端补偿缺一不可5.1 一张表看懂32位数据MAX31855的SPI输出是固定的32位MSB在前。数据格式如下表所示位号长度含义格式分辨率/说明Bit31 ~ Bit1814位热电偶温度二进制补码有符号1 LSB 0.25℃Bit171位保留恒为0可用于校验数据是否错位Bit16 ~ Bit413位冷端参考端温度二进制补码有符号1 LSB 0.0625℃Bit31位保留恒为0同上Bit21位SCV标志位1表示热电偶短路到VCCBit11位SCG标志位1表示热电偶短路到GNDBit01位OC标志位1表示热电偶开路另外还有一个综合故障标志F当SCV、SCG、OC任一位置1时F位会被置1。F位就是冷端温度数据段里的最高位Bit16。所以解析数据时一定要先判断故障位再解析冷端温度否则故障状态下读到的冷端温度会是一个没有意义的大负数。5.2 补码符号扩展的代码细节热电偶温度在32位数据里是14位二进制补码LSB对应0.25℃。问题是C语言里没有14位整数这种类型我们必须把它转成16位或32位有符号整数再做乘除运算。如果忽略符号扩展负温度会变成很大的正数比如-5℃对应的14位补码值是某个负数如果不扩展直接当作正数乘0.25读出来可能是三千多度显然不对。符号扩展的通用做法是先取出14位数值判断最高位bit13也就是0x2000是否为1如果为1就把高位置1。代码里我写的是int16_t tc (int16_t)((raw 18) 0x3FFF); if (tc 0x2000) { tc | 0xC000; }这样处理后tc就是一个标准的16位有符号数负温度也能正确表示。冷端温度同理13位补码取出来后要判断0x1000位如果为1就把高3位置1。很多初学者第一次处理MAX31855读数异常问题往往就出在这里正温度没问题一到负温度就开始胡来。5.3 故障处理温度显示开路而不是0实际现场应用中热电偶线断掉、接头松动、端子氧化都是很常见的事情。MAX31855自带开路检测功能当热电偶断开时OC位会置1F位同时置1。这时候热电偶温度数据段已经无效如果程序不判断故障位直接把无效数据当温度显示出来用户看到的就是一个离谱的数字。我习惯的做法是解析函数返回一个状态码0表示数据有效-1表示有故障。主程序里如果读到故障状态显示屏或上位机直接显示开路或SENSOR ERROR而不是显示0℃——0℃本身是一个合法温度用它表示故障会误导人。串口调试时把raw原始值、故障位、冷端温度一起打出来能很快定位问题。举个例子热电偶没接raw打印出来如果bit0为1说明OC触发如果bit1为1说明T或者T-对地短路如果bit2为1说明热电偶线碰上了电源正极。这些状态信息比单纯看温度值好排查得多。5.4 冷端补偿别把冷端温度直接显示出来MAX31855读出来的冷端温度是芯片内部传感器感受到的温度也就是参考端的温度。正常情况下这个值应该等于室温大概20℃到30℃。如果程序只读取热电偶温度而不做冷端补偿那读数会偏差几十度——但注意MAX31855内部已经做了冷端补偿所以它输出的热电偶温度本身就是经过补偿之后的绝对温度值不需要我们再手动加冷端温度。这一点和直接用ADC读热电偶的原始电压完全不同。MAX31855输出的热电偶温度已经是测量端真实温度冷端温度信息只是附带提供用于系统自检和调试。如果你的程序把热电偶温度和冷端温度相加再显示反而会把温度算错。我见过有人刚接触MAX31855看到官方例程读出了两个温度就在应用层把两个温度加起来显示结果室温环境下读出一百多度就是这个道理。冷端温度还有一个用途判断PCB环境是否异常。如果冷端温度明显高于室温比如50℃以上说明MAX31855附近有强热源或者芯片自身过热这时候即使热电偶温度看起来正常整个测量精度也会下降因为冷端补偿的线性范围有限。6. 两种读取方式的实测对比与选型建议6.1 同一块板子上两种方式的实测数据我在同一块板子上用同一个MAX31855芯片先后用硬件SPI和软件模拟各读取了200次温度对比结果如下对比项硬件SPI1MHz软件模拟GPIO每bit约1μs单次读取耗时约32μs约40μs ~ 60μs数据一致性200次全部一致200次偶发1次错位关中断后0次CPU占用极低可用DMA完全解放CPU全程占用CPU抗中断干扰强SPI外设硬件控制时序弱需在读取期间关中断引脚需求需SPI外设引脚通常固定任意3个GPIO即可适用平台STM32、ESP32、树莓派等51、ESP8266、任何有GPIO的平台单从速度上看软件模拟并不比硬件SPI慢多少因为MAX31855的转换时间长达100ms读取本身的几十微秒完全可以忽略。两者的本质差异在可靠性和资源占用上。硬件SPI有芯片外设保证时序精度即使中断频繁发生SCLK的翻转节奏也不会乱软件模拟则完全依赖软件控制一旦读取过程中被高优先级中断打断SCLK的高低电平持续时间就会变化MAX31855对这种变化有一定的容忍度但容不下太长的间隔数据可能错位。6.2 如何根据硬件处境选择我的建议很直接硬件SPI能用就用硬件SPI引脚冲突或者平台没有SPI外设的时候才考虑软件模拟。这不是说软件模拟不行而是硬件SPI在可靠性上天然占优尤其是项目进入量产阶段后中断处理、RTOS任务调度等问题会让软件模拟的时序风险成倍放大。但软件模拟的价值在于救急。比如你用的ESP8266硬件SPI虽然存在但大多数情况下被WiFi协议栈和Flash占了很难拿来接外部SPI设备。网上经常有人问ESP8266模块能连接SPI接口芯片吗答案是可以的大部分方案就是GPIO软件模拟虽然代码难看一点但能稳定跑。再比如51单片机很多型号根本没有SPI外设想接MAX31855就只能用GPIO模拟。这种情况下软模拟不是备选方案而是唯一方案。如果你在项目里同时需要两种方式最好抽象一层接口让上层代码不用关心底层是硬件还是软件模拟。用一个函数指针或者宏定义把读取函数和解析函数解耦// 在头文件中定义读取接口 uint32_t MAX31855_ReadRaw(void); // 硬件SPI版本 uint32_t MAX31855_ReadRawSoft(void); // 软件模拟版本 // 切换读取方式时只改这一行 #define MAX31855_READ_RAW MAX31855_ReadRaw这样硬件SPI和软件模拟共用同一套数据解析、故障处理、滤波逻辑新增一种平台时只需要提供对应的读取函数上层代码完全不用动。我在多通道温度采集板里就是这么干的三路MAX31855共用一套解析代码两路走硬件SPI一路走软件模拟代码清晰很多。6.3 多路MAX31855扩展的接线方案一个MCU要接多路MAX31855接线方式根据读取方式不同有两种思路。硬件SPI方式所有MAX31855共用一个SPI总线的SCLK和SO每个芯片单独接一根CS线到MCU的不同GPIO。芯片的SO在CS为高时是高阻态不会干扰总线所以MISO可以并联。同一时刻只拉低一个芯片的CS软件上逐个读取即可。软件模拟方式每个MAX31855使用独立的3个GPIOSCLK、CS、MISO互不干扰。理论上GPIO够多就能扩展很多路但每一路读取都要占用CPU时间路数多了要注意实时性。不管哪种方式多路读取时都要注意一个问题MAX31855的转换是并行进行的100ms转换时间对所有芯片同时生效所以轮询读取N个芯片的总耗时大约是N倍的单片读取时间只要这个时间远小于100ms就没问题。我实测过8路MAX31855硬件SPI轮询每路读取加解析不到0.5ms8路也就几毫秒完全来得及。7. 调试心得让温度读数抽风的几个隐蔽原因读数突然跳变、偶尔报错、数值整体偏移这类问题排查起来最费时间。分享几个我在实际调试中遇到的隐蔽原因如果你也碰到类似现象可以按顺序查一遍。第一个是CS引脚浮空。有些主控上电初始化过程中GPIO处于高阻态CS引脚悬空MAX31855可能被噪声误触发进入未知状态。解决办法在CS引脚上加一个10kΩ上拉电阻到3.3V或者在主控初始化代码里先把CS输出高电平再去初始化SPI外设。这个坑我用逻辑分析仪抓了一晚上才定位到。第二个是读取间隔卡在100ms边缘。MAX31855转换时间是典型值100ms但这个值不是精确的受温度和芯片个体差异影响会有浮动。如果你的主循环正好按100ms周期读取可能会出现读到的数据偶尔是上一次的这种现象表现就是温度值偶尔不变或者跳变一下。稳妥做法是把读取周期放到150ms以上或者在两次读取之间加一个明确的延时。第三个是电源纹波导致的最低位数跳动。热电偶温度分辨率0.25℃如果电源纹波大ADC结果会在最低位附近抖动显示出来就是温度在23.25℃和23.50℃之间反复横跳。这种情况先检查供电MAX31855和主控最好分开供电或者在软件里做一次简单的滑动平均滤波。我实测取4次平均值就足够压住纹波引起的抖动再多反而会拖慢响应速度。第四个是热电偶接线端子接触不良。工业现场用的K型热电偶一般通过标准插头连接插头用久了簧片松弛测温过程中偶发开路MAX31855就会报OC错误或者读出跳变的温度。排查方法很简单拿万用表量热电偶两端电阻K型热电偶常温下电阻一般在几欧到几十欧之间如果量出来几百千欧甚至无穷大那就是线断了或接触不良。第五个是SPI速率太高引起的偶发错位。硬件SPI虽然时序稳定但速率过高时如果PCB走线较长或者信号完整性不好MISO上的数据边沿会变缓主机采样点如果太靠近数据跳变沿就会偶发读错。我通常把MAX31855的SPI速率控制在1MHz到2MHz这个范围足够快也足够稳。软件模拟时序同理不需要追求极致速度稳定压倒一切。最后分享一个小技巧调试MAX31855时不要只盯着温度值看。把每次读到的原始32位数据和解析出来的故障位、冷端温度一起通过串口打出来用十六进制格式打印raw值你会看到很多有用信息。比如正常情况下raw的高位是稳定的温度数据保留位bit17和bit3应该恒为0——如果这两个位偶尔变成1说明时序有问题数据发生了错位这时候去查CS时序和SCLK波形比瞎猜快得多。这块板子项目结束后我把软件模拟的那个函数留在工程里没有删。后来换了一颗新主控恰好SPI引脚被其他外设占满这个当初只当备胎的软模拟函数直接派上了用场。热电偶采集这种事方案好不好用往往不是看芯片本身多强大而是看你在各种硬件受限的情况下有没有退路。硬件SPI和软件模拟两种方式都掌握遇到什么样的平台都不慌。
返回列表