
简介这是面向单片机开发者的MAX6675热电偶采集完整资料包围绕K型热电偶数字转换器提供C51与STM32两套例程源码并配套K型热电偶原理、数字转换器应用、温度采集系统设计等多篇技术文档可帮助理解冷端补偿、SPI读取、温度显示等关键环节。压缩包共177个文件、约6.6MB以C/H源码各28个、Keil工程文件、HEX烧录文件为主另有12份PDF与5份DOC文档兼顾可编译工程与原理性参考资料。其中C51例程适合入门8051温控设计STM32例程包含完整MDK演示工程便于在开发板上直接验证配套文献还覆盖基于MAX6675的温度采集系统设计、多路温度采集、分布式高精度测温、热电偶温度表测量电路、电阻炉温控、发动机尾气参数采集等典型场景提供从芯片手册到系统方案的排错与设计思路。目前已有1955人学习适合正在做温度测控项目、毕业设计或课程设计的开发者和学生。1. MAX6675热电偶采集K型热电偶为何绕不开冷端补偿与16位SPI读取做温度采样时K型热电偶输出的是几十毫伏的热电势直接接MCU的ADC没有任何分辨率更麻烦的是参考端温度一变读数就跟着漂。MAX6675把放大、冷端补偿和12位ADC集成在一颗芯片里最后通过SPI串行输出16位数据C51用三根IO线模拟时序就能读STM32接硬件SPI也就十几行代码。这个标题里的例程包覆盖C51和STM32两种平台做3D打印机热床、烤箱温度监控、工业温控箱都直接可用。拿到资料先别急着烧录MAX6675这16位里有4位不是温度本篇把芯片原理、C51和STM32两个例程的差异以及落地时的测试校准一次讲清。2. MAX6675与K型热电偶适配原理从热电势到16位SPI读数2.1 从塞贝克效应到片内ADCMAX6675在测量链路里替掉了哪些电路K型热电偶由镍铬-镍硅两种合金构成测量端与参考端存在温差时回路产生与温差成正比的微弱热电势比例约为41µV/°C。也就是说测量100°C温差时信号只有4.1mV左右。这能推导出两件事第一信号必须经过高增益放大第二参考端温度不稳定的话放大结果毫无意义。MAX6675芯片内部有低漂移运放、冷端补偿二极管和12位ADC输入范围覆盖K型热电偶0°C到1023.75°C对应的热电势分辨率0.25°C。外部只需要加简单的RC滤波传统仪表放大器和热电偶电桥补偿电路都不再需要这是中小批量产品在主控侧做温度采集的常见选型。MAX6675支持的测温上限是1023.75°C这是由它的ADC量程和内部增益决定的不是K型热电偶的上限。K型热电偶本身可以测到1300°C左右但用MAX6675采集时超过1023.75°C读数是饱和的选型时要先区分这两个上限。另一个限制是它不支持负温0°C以下输出读数为0不能直接用于冷冻冷库这类需要负温测量的场景。做发动机排气温度这类超过1000°C的测量时应换用MAX31855或专用调理芯片而不是继续沿用MAX6675例程。2.2 16位数据格式与温度换算raw右移3位再乘0.25MAX6675每次转换完成后可以通过SPI把16位结果移出来各位定义如下表位名称值含义D15热电偶开路标志1热电偶断线或未接D14-D3温度数据12位无符号LSB0.25°CD2设备ID固定为1读回0时说明SPI时序或接线有问题D1SCV1输入短路到VCCD0SCG1输入短路到GND温度换算公式是temperature (raw 3) * 0.25。这里raw是读到的完整16位值右移3位丢掉D2、D1、D0三个状态位剩下的D14-D3刚好是12位温度数据。举个例子读到0x0D24右移3位得到0x1A4即420再乘0.25就是105°C。读取数据时注意表格里的D15在半个字节的高位如果你用无符号16位变量保存raw 3会把这个标志位误带入温度区间所以先做raw 0x8000判断再移位。这个数据格式看起来简单有两个容易踩的误解一是读出来的float值不需要再做任何标定MAX6675出厂前已内部校准二是D2位不是错误标志而是自检位值为1表示芯片工作正常值为0多半是CS脚上的电容太大导致时序边沿钝化或电源纹波干扰了移位寄存器。例程包里的技术文档通常会附一张数据手册翻译表核对位定义时以手册时序图为准。2.3 冷端补偿与热电偶电桥补偿电路的替代关系K型热电偶测的是测量端和参考端的电势差。如果参考端也就是热电偶接入MAX6675那两个引脚的位置温度变了即使测量端温度不变输出也会变化。传统做法是在参考端布置一个由铜电阻或NTC构成的热电偶电桥补偿电路把热电势修正到0°C基准误差取决于电桥元件的匹配度和环境温度跟随速度。MAX6675把参考端传感器做到芯片内部直接测量芯片引脚附近的温度再从热电势中减去冷端贡献这一步不再需要外部电桥。接线时要注意让热电偶的引脚尽量贴近芯片引脚焊盘避免PCB铜箔在两者之间形成额外温差。K型热电偶有正负极之分通常红色为正、黄色为负接反后读数是下降而非上升开路标志不报错。MAX6675引脚不多典型接线如下表引脚名称连接目标1GND公共地2T-K型热电偶负极3TK型热电偶正极4SCKMCU的SPI时钟5CSMCU片选GPIO6SO数据输出接MCU的MISO或普通输入IO7VCC3.3V或5V电源完整的芯片还有NC脚等具体以数据手册为准另外电源脚旁边需要加100nF去耦电容到GND尽量靠近芯片。例程包里一般有接线图文档直接对照引脚名连接就行不要只看丝印排序。3. C51例程解读用普通IO模拟SPI读MAX6675的代码与时序3.1 Keil C51工程搭建芯片型号与编译器版本的选择C51例程通常在Keil C51环境下编译目标芯片以STC89C52、AT89C52这一代为主。打开例程前先确认Keil里装了C51编译器而不是只装了ARM编译器。Keil5兼容C51和STM32工程的做法是先安装C51编译器再在Pack Installer里安装ARM相关的芯片支持包比如c51 v9.59a与STM32F1系列包可以共存于同一IDE。如果你电脑上只装了STM32的MDK支持直接打开.C51工程会提示找不到C51编译器这不是工程坏了是工具链缺失。单片机上的引脚定义一般集中在工程头文件里常见的写法是三个sbitsbit MAX_SCK P1^0; // 时钟线输出 sbit MAX_CS P1^1; // 片选线输出 sbit MAX_SO P1^2; // 数据线输入改引脚时直接在头文件里替换这三个宏其他地方不用动。注意C51编译器里sbit只适用于可位寻址的IO口P0到P3都满足选任意引脚都可以不用强制接到硬件SPI引脚上这也是模拟SPI在51上最方便的地方。C51自带的SPI通信库文件用得少因为MAX6675只有单方向数据输出模拟时序比调用库函数更可控。3.2 核心读取代码16个时钟周期移出16位数据#include reg52.h sbit MAX_SCK P1^0; sbit MAX_CS P1^1; sbit MAX_SO P1^2; unsigned int max6675_read_raw(void) { unsigned char i; unsigned int value 0; MAX_CS 0; /* 片选拉低MAX6675准备输出数据 */ for (i 0; i 16; i) { MAX_SCK 1; /* 上升沿芯片把下一位放到SO线上 */ value 1; /* 寄存器左移一位 */ if (MAX_SO) { value | 0x0001; /* 当前位为1时置位最低位 */ } MAX_SCK 0; /* 时钟拉低准备下一个上升沿 */ } MAX_CS 1; /* 片选拉高结束本次读取 */ return value; } float max6675_read_temp_c(void) { unsigned int raw max6675_read_raw(); unsigned int temp12; float temp; if (raw 0x8000u) { /* D151热电偶开路 */ return -1.0f; /* 负值通知上层做异常处理 */ } if ((raw 0x0004u) 0u) { /* D2设备ID应为1 */ return -2.0f; /* 时序错误或CS干扰 */ } temp12 (raw 3) 0x0FFFu; /* 提取D14-D3这12位 */ temp (float)temp12 * 0.25f; /* LSB0.25°C */ return temp; }代码逻辑说明先拉低CS这是启动SPI通信的使能信号随后MCU主动产生16个时钟脉冲MAX6675在每个SCK上升沿把一位数据送到SO线上。value 1写在MAX_SCK 1之后是因为数据在上升沿后已经建立先读到当前位再移位新读到的位才能正确落在最低位。也可以把移位放到读取前只要保证每次循环恰好移入一位两种写法的效果等价。参数说明temp12 (raw 3) 0x0FFFu这行右移3位是去掉D2、D1、D0再用0x0FFF掩码防止高位的D15混入。如果直接用raw 3而不做掩码D15漂移到bit12时会引入4096×0.251024°C的偏移量排查这种问题非常费时间。C51里unsigned int就是16位移位运算的结果不会扩展到32位但raw在条件判断里参与运算时编译器会有隐式符号转换的风险所以字面量加u后缀判断统一用无符号比较。返回值的约定建议单独建一个错误码映射表方便上层逻辑判断返回值含义-1.0f热电偶开路-2.0f设备ID异常或SPI时序错误其他12位温度换算后的摄氏温度3.3 时序边界与刷新率170ms转换周期影响读取节奏MAX6675内部完成一次温度转换约需170ms。从CS拉低开始读出的16位结果可能是上一次转换的结果这不影响使用但要保证两次读取之间的距离大于一个转换周期否则连续读到的都是同一份旧数据。常见做法是每次读取前延时至少20ms读取后通过定时器或主循环调度控制在5Hz左右采样对温控场景已经够用。这里常见的错误是循环里不加延时串口打印出的温度数值一长串完全不变遂判定传感器坏了。这种问题用万用表量SO电平无效因为它有数据输出时本来就在跳变。定位方法很简单在读取函数后面加一个200ms延时如果数值开始变化说明时序正常是读取频率超过了芯片转换能力。模拟SPI的时钟速度不需要刻意减慢C51的IO翻转本身在几百kHz量级远低于MAX6675的4.3MHz上限。反而是SCK拉高后立刻读SO在部分STC型号上会因IO读取时序略慢而读到上一周期的旧位稳妥做法是读取语句放在拉高之后的第二条语句或者直接加一个_nop_()。例程包里有时会有两个版本的读取函数区别就是SO读数之前有没有空指令实际差异在低速时钟下几乎不可见。4. STM32例程移植硬件SPI的Mode选择与标准库替代写法4.1 HAL库硬件SPI配置Mode 1、软件CS、分频到1MHz左右STM32读MAX6675可以挂硬件SPI例程大多数是标准库或HAL库两个版本。先给一个HAL库的SPI配置片段这个配置直接决定了读取能否正常工作// 在MX_SPI1_Init()中设置参数 hspi1.Instance SPI1; hspi1.Init.Mode SPI_MODE_MASTER; hspi1.Init.Direction SPI_DIRECTION_2LINES; hspi1.Init.DataSize SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity SPI_POLARITY_LOW; // CPOL0空闲时钟为低 hspi1.Init.CLKPhase SPI_PHASE_2EDGE; // CPHA1下降沿采样对应Mode 1 hspi1.Init.NSS SPI_NSS_SOFT; // CS用普通GPIO控制 hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_64; // 72MHz/641.125MHz hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; HAL_SPI_Init(hspi1);读取函数float max6675_read_hal(void) { uint8_t buf[2]; uint16_t raw; float temp; HAL_GPIO_WritePin(MAX6675_CS_GPIO_Port, MAX6675_CS_Pin, GPIO_PIN_RESET); HAL_SPI_Receive(hspi1, buf, 2, 10); // 连续收2字节SCK由主机产生 HAL_GPIO_WritePin(MAX6675_CS_GPIO_Port, MAX6675_CS_Pin, GPIO_PIN_SET); raw ((uint16_t)buf[0] 8) | buf[1]; // 第1字节是D15-D8 if (raw 0x8000u) return -1.0f; // 热电偶开路 temp (raw 3) * 0.25f; return temp; }关键点是CLKPhase用了SPI_PHASE_2EDGE对应CPHA1。原因在于MAX6675在SCK上升沿更新SO线上的数据主机如果用Mode 0采样点正好落在数据翻转时刻第一个bit很容易被采错这是硬件SPI读MAX6675偶发一位错位的根源。使用Mode 1后主机在下降沿采样信号已建立丢位现象明显减少。提示如果例程里用的是Mode 0且也能读到温度别急着沿用到产品代码。Mode 0的采样点紧贴数据翻转沿长时间运行后偶发错位的概率不低统一改Mode 1更可靠。HAL_SPI_Receive只收不发因为MAX6675是单从机只输出、不需要MOSI数据。SPI分频到1.125MHz远低于4.3MHz上限给时序余量。CS始终由GPIO控制设定NSS_SOFT是为了避免硬件NSS自动拉低干扰多从机场景。整个配置里最容易被忽略的是FirstBit如果工程模板默认是LSB First读出来的字节顺序会整个颠倒温度值完全对不上。4.2 标准库版本用GPIO模拟SPI平移C51代码有些用户习惯用标准库可以直接把C51的模拟SPI代码平移过来ST的GPIO翻转速度更快时序余量更大。标准库初始化GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1; // SCK和CS GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin GPIO_Pin_2; // SO GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; // 上拉输入 GPIO_Init(GPIOB, GPIO_InitStructure);读取函数结构与C51相同把sbit操作改成GPIO的BSRR和IDR访问uint16_t max6675_std_read(void) { uint8_t i; uint16_t value 0; GPIOB-BRR GPIO_Pin_0 | GPIO_Pin_1; // CS拉低SCK拉低 for (i 0; i 16; i) { GPIOB-BSRR GPIO_Pin_1; // SCK拉高 value 1; if (GPIOB-IDR GPIO_Pin_2) { value | 0x0001; } GPIOB-BRR GPIO_Pin_1; // SCK拉低 } GPIOB-BSRR GPIO_Pin_0; // CS拉高 return value; }逻辑说明进入循环前CS和SCK都保持低电平每次循环先把SCK拉高MAX6675在上升沿更新SO数据IDR读取当前电平后写入value最低位。循环结束后CS拉高一次16位读取完成。BRR寄存器写1使引脚输出低BSRR写1使引脚输出高这两个寄存器只对写1的位生效写0不影响原状态。参数说明SO脚配成上拉输入是因为MAX6675的SO在CS高电平时为三态没有外部上拉的话MCU会读到不确定电平。标准库新建工程时别忘了开GPIOB时钟如果是硬件SPI复用还要单独开AFIO时钟。模拟SPI不需要AFIO只开GPIOB时钟就够了。若换成其它引脚注意所选的SO脚必须支持输入模式且不能是纯开漏输出的OD引脚。4.3 硬件SPI与模拟SPI的边界选型硬件SPI和模拟SPI本质上差别不在速度而在于对时钟极性和相位的要求。MAX6675本身转换周期170ms数据量极小模拟SPI对CPU的占用完全可以忽略所以大量产品里直接用GPIO模拟。硬件SPI的价值在于多从机共享总线或与其它SPI器件混用时可复用同一组引脚。对比项硬件SPIHAL/标准库模拟SPIGPIO接线引脚固定SCK/MISO任意IO方便PCB布线时钟极性必须设Mode 1无此概念天然适配速率最高4.3MHz例程多用1MHz几百kHz量级首个bit风险模式0容易错位只跟代码移位顺序有关多设备场景同总线可挂多个每个传感器多占一组IO从实际维护角度模拟SPI代码在C51和STM32之间几乎可以互相拷贝换平台成本低硬件SPI代码更紧凑但调试时需要用示波器看SCK/SO的对应关系排错门槛高一个台阶。我的习惯是单路测温用模拟SPI多路或用总线共享时上硬件SPI。若做STM32串口调试PID温控模拟SPI占用主循环的时间极短不会干扰PID计算节奏。5. 测试校准与数据平滑MAX6675热电偶采集的收尾工作5.1 用冰水和沸水验证绝对误差拿到例程后先用冰水做参考将K型热电偶探头放入冰水混合物中等待30秒读数不再漂移后数值应该在-1°C到2°C之间。MAX6675的冷端补偿误差本身就有±3°C的量级K型热电偶探头精度也是影响因素所以读数偏离1-2°C不是代码问题。沸水测试受大气压影响海拔每升高300m沸点大约降低1°C高海拔地区读数偏低属正常。若偏差是固定值可在换算后加一个系统偏移量修正例如temp 1.2f不要在12位原始数据上修正。5.2 滑动平均滤波与0.25°C量化噪声MAX6675分辨率0.25°C输出本身是阶梯状跳变。直接显示时温度变化1°C读数会依次跳过4档感官上就是跳字。滑动平均是代价最低的平滑手段#define FILTER_N 8 int sample_buf[FILTER_N]; unsigned char sample_idx 0; float max6675_filtered(float new_temp) { long sum 0; unsigned char i; sample_buf[sample_idx] (int)(new_temp * 4); // 按0.25°C的整数倍存放 sample_idx (sample_idx 1) % FILTER_N; for (i 0; i FILTER_N; i) { sum sample_buf[i]; } return (float)sum / 4.0f / FILTER_N; }这里用new_temp * 4把浮点温度转成整数再累加避免每帧都做浮点加法。long sum在C51里是32位8个样本的累加远不会溢出。8次平均对应8个转换周期约1.4s温度响应会略迟钝恒温控制场景可以接受。如果做3D打印机喷头温度监测想保留快速升温的响应把FILTER_N改成4即可过滤噪声同时保留趋势。5.3 断线检测与重读机制MAX6675的D15位是热电偶开路检测但热电偶接线松动时开路状态往往是瞬间的误报处理不好就会让温控逻辑误判。建议连续读到3次开路标志后再认定断线而不是第一次读到就紧急停机。重读机制的实现是同一帧读两次如果两次的raw值不一致以第二次为准CS的上升沿和下降沿之间的稳定度最影响这个结果。串口输出的原始raw值盯着看几个转换周期D15若频繁跳1就先查热电偶端子压接D2位跳变再查CS脚的去耦电容这类排查比反复读读数有效得多。本文还有配套的精品资源点击获取