
简介针对STM32F103嵌入式开发者的四路MAX6675温度采集工程以SPI方式读取K型热电偶数据实现多点温度监测并通过LCD1602实时显示、UART串口打印输出适合学习传感器采集、SPI通信和外设驱动开发的入门与进阶参考。压缩包约7.65MB共216个文件以C源文件、H头文件、Keil工程文件uvprojx、编译生成文件o/crf/axf/hex/map等为主既保留可读源码也包含可直接烧录的hex文件便于直接验证与二次修改。已有1143人学习使用。项目代码覆盖MAX6675四路采集配置、LCD1602显示驱动、串口打印逻辑、系统时钟与定时器初始化等模块目录结构清晰能帮助读者快速理解从传感器数据获取到人机交互显示和串口输出的完整流程。对于需要搭建多点测温装置或熟悉STM32标准外设库的开发者这套资料具有直接参考价值。 手里同时要测四路K型热电偶的温度并且需要实时显示到LCD、发到串口上位机这个需求在我之前的一个小项目里正好出现过。当时直接把STM32F103和一排MAX6675模块搭起来用SPI轮询四路通道结果一次就点亮了液晶屏数据也稳定地出现在串口调试助手里。今天就把这套方案从硬件到代码完整整理出来项目工程里包含了驱动源码、接线说明直接在标准外设库工程上改一改就能复用。这类项目的典型应用场景很明确多路热风枪温度监控、3D打印机热床与喷头温度采集、锂电池充放电温度记录、工业加热设备多点巡检等。凡是“需要同时看几个热电偶温度、又要留一份数据给电脑处理”的场合这套配置基本都能用。适合有一定STM32基础、想把传感器真正跑起来的人参考。1. 项目概述与方案选型1.1 为什么选择MAX6675做温度采集K型热电偶本身输出的是微伏级电压信号完全不能直接接单片机ADC。MAX6675这颗芯片内部集成了信号放大、冷端补偿和ADC转换把热电偶的电压信号变成数字量再通过SPI接口发给主控。这个“一站式”特性让它在单片机项目里非常流行省掉了运放调理电路和一大堆校准工作。MAX6675的实际测量范围是0到1024摄氏度分辨率为0.25摄氏度。对绝大多数加热设备监控场景来说这个精度够用。它内部还带了冷端补偿也就是说室温变化不会导致测量值漂移这在多路测量时特别重要——如果冷端补偿做不好四路读出来的温度会跟实际环境温度一起变动容易误判。1.2 为什么选择STM32F103作为主控STM32F103主频72MHzSPI、USART、GPIO这些资源非常充足处理四路MAX6675的轮询采集完全不在话下。这项目用的是大家比较熟悉的标准外设库StdPeriph不是HAL代码看起来直白网上资料也多新手对着改也能上手。选择F103而不是51单片机核心原因有两个。第一F103的GPIO翻转速度快软件模拟SPI读MAX6675的时候时序余量充足不像51那样要抠机器周期。第二F103的USART带硬件缓冲串口打印大量温度数据时不丢数据而且后续想加FreeRTOS、文件系统、PID控制这个平台都撑得住。1.3 四路采集的拓展思路四路MAX6675共享同一个SPI总线利用各自独立的CS片选脚区分设备。读取第一路时把CS1拉低其他三路CS保持高电平读完后拉高CS1再拉低CS2去读第二路。这是最典型的SPI多设备挂载方式不增加SPI外设只多占几个GPIO引脚。这样一来扩展路数非常方便。今天做四路明天想改成六路、八路只需要复制一份读取代码、多接一个模块、多占一个CS引脚就行主控逻辑完全不用动。2. 硬件连接与关键时序2.1 接线方法完整对照表MAX6675标准的模块有5个引脚VCC、GND、SCK、SO也叫MISO、CS。模块上的T和T-是接热电偶的T接K型热电偶的正极通常是红色线T-接负极另一色线。我实际使用的引脚分配如下表建议直接照抄MAX6675模块STM32F103引脚说明VCC所有模块3.3V统一用3.3V供电GND所有模块GND共地必须接牢SCK所有模块并联PA5SPI时钟线共用SO所有模块并联PA6数据输出线共用CS1PA4第一路片选CS2PA7第二路片选CS3PB0第三路片选CS4PB1第四路片选LCD1602和串口的接线也需要说明一下。LCD1602采用4线模式能省4个引脚LCD1602引脚STM32F103引脚RSPB10RWPB11EPB12D4PB13D5PB14D6PB15D7PB8VCC5V或者3.3V模块不同而异GNDGND串口这里用的是USART1PA9做TX、PA10做RX接一个USB转TTL模块就能在电脑上看到数据。2.2 MAX6675的SPI读写时序MAX6675的SPI时序很有特点。读数据的流程是先把CS拉低此时SO引脚输出第一位数其实是无效位然后给16个SCK脉冲在每个脉冲的上升沿读取SO引脚上的电平就得到了16位数据。时序上有个硬性要求CS拉低之后至少要等100ns才能开始第一个SCK脉冲。整个16位数据读完后CS拉高。这个拉低的持续时间也不能太短否则转换结果可能还没准备好。另外MAX6675完成一次温度转换大约需要220ms也就是说两次连续读取之间的间隔不能小于220ms否则读到的还是旧数据。16位数据里真正有用的部分看这张表就清楚了位含义D15恒为0无意义D14~D312位温度数据D2热电偶开路标志1表示断线D1~D0恒为0无意义温度值计算很简单把D14~D3这12位数据当成一个整数乘以0.25就是实际温度。比如读出16位数据是0x0190右移3位得到十进制5050乘以0.25等于12.5摄氏度。如果D2位是1说明热电偶没接好此时温度数据无效。2.3 硬件设计上容易踩的坑第一个坑是供电电压。很多MAX6675模块上有稳压电路5V也能用但SO引脚输出的高电平是跟随供电电压的。如果模块用5V供电SO引脚输出5V高电平直接接在STM32F103的PA6上长期运行有可能损伤引脚。所以稳妥的做法是整个系统统一3.3V给MAX6675供电避免电平不匹配。第二个坑是热电偶方向接反。K型热电偶接反不会烧芯片但读出来的温度是负值或者明显偏低。如果四路里有某一路读数异常优先检查热电偶是否接反。第三个坑是模块的SO引脚。MAX6675的SO引脚在CS为高电平时输出高阻态所以四路SO并联不会互相干扰不需要额外加上拉电阻。但模块上如果有板载指示灯的话不同模块之间可能会因为LED电流导致电源纹波严重时会让温度读数跳动这时在模块供电端加一个100uF电解电容和0.1uF瓷片电容去耦问题通常能解决。3. 软件实现STM32F103库函数版3.1 引脚初始化与串口配置整个工程的初始化顺序是先配置系统时钟默认72MHz然后配置GPIO、USART1、LCD1602最后进入主循环。GPIO配置这里有一个重要细节PA4、PA5、PA6、PA7这四个引脚默认是SPI1的NSS、SCK、MISO、MOSI功能但因为我们用软件模拟SPI不需要开启SPI外设只用普通推挽输出和上拉输入模式就行。PA5作为SCK配置成50MHz推挽输出PA6作为SO配置成上拉输入PA4、PA7、PB0、PB1作为CS配置成推挽输出。LCD引脚全部配置成推挽输出RS、RW、E、D4~D7都是输出模式。USART1配置成115200波特率、8位数据、1位停止位、无校验。这里提醒一句串口发数据的时候要注意换行符的问题。单独发\n有时候在部分串口助手里显示不换行最稳妥的做法是发\r\n组合这样无论在SSCOM还是其他调试工具里都能正常换行。3.2 MAX6675读取与数据解析读取MAX6675的核心代码用软件模拟SPI其实很直观。上代码// 读取一路MAX6675数据返回16位原始值 uint16_t MAX6675_Read(GPIO_TypeDef* CS_PORT, uint16_t CS_PIN) { uint16_t raw 0; uint8_t i; // CS拉低等待至少100ns GPIO_ResetBits(CS_PORT, CS_PIN); Delay_Us(1); for(i 0; i 16; i) { // SCK拉高在上升沿读取SO数据 GPIO_SetBits(GPIOA, GPIO_Pin_5); Delay_Us(1); raw 1; if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6) Bit_SET) { raw | 0x01; } // SCK拉低 GPIO_ResetBits(GPIOA, GPIO_Pin_5); Delay_Us(1); } // CS拉高结束读取 GPIO_SetBits(CS_PORT, CS_PIN); return raw; } // 把16位原始数据转换成浮点温度值 float MAX6675_ToCelsius(uint16_t raw) { if(raw 0x04) // 热电偶开路 { return -999.0f; // 用-999表示断线 } return (float)((raw 3) * 0.25); }这段代码里两个细节值得注意。第一是每次SCK拉高和拉低之间加1微秒延时这个时间远大于MAX6675的时序要求能保证在上升沿时数据已经稳定。第二是读取前先左移再判断MISO电平这样最高位D15先进来移位顺序正好对上MAX6675的MSB先行输出。这里有人会问为什么不直接用芯片自带的硬件SPI我实测下来软件模拟SPI在这个场景里反而更实用。MAX6675的SPI时序非常简单不需要高速传输软件模拟可以精确控制每个时序间隔而且四路CS轮询的逻辑本来就要操作GPIO软件模拟写起来更顺手。实际上在寄存器操作级别F103主频72MHzGPIO翻转速度足够快读一路16位数据耗时不到1毫秒四路全读也就几毫秒完全不影响系统实时性。3.3 LCD1602显示与串口上报LCD1602的驱动代码我使用的是4位模式关键点是初始化时序。LCD1602上电后必须等40ms以上然后按“03、03、03、02”的顺序发初始化命令再设置“显示开、光标关、不闪烁”等参数。4位模式下每发一个字节要先发高4位再发低4位中间通过E引脚的下降沿锁存数据。显示四路温度时我把它分成两行char line1[17]; char line2[17]; sprintf(line1, T1:%4.1f T2:%4.1f, temp[0], temp[1]); sprintf(line2, T3:%4.1f T4:%4.1f, temp[2], temp[3]); if(temp[0] -100 || temp[1] -100) { sprintf(line1, T1: OPEN T2: OPEN); } if(temp[2] -100 || temp[3] -100) { sprintf(line2, T3: OPEN T4: OPEN); } LCD_ShowString(0, 0, line1); LCD_ShowString(0, 1, line2);这样第一行显示T1和T2的温度第二行显示T3和T4的温度每路温度保留一位小数。一旦某一路上报了负的999也就是断线标志LCD对应位置直接显示OPEN比显示乱码或者0.0友好得多。串口打印这里我推荐一个上位机解析友好的格式。如果每次都打印“CH1:25.50℃ CH2:26.00℃”这种中文加单位的长文本上位机处理起来麻烦中文编码在串口助手之间也不一致。更实用的做法是打印纯数字用逗号分隔printf(%.2f,%.2f,%.2f,%.2f\r\n, temp[0], temp[1], temp[2], temp[3]);配合上位机用Python的split(,)或者EXCEL导入都能轻松解析成表格做趋势记录很方便。如果你用的串口助手没有CSV解析功能想调试时看个直观效果可以临时改成“CH1:25.50 CH2:26.00”这样的格式。3.4 主循环与转换周期调度主循环的逻辑非常直白while(1) { // 依次读取四路MAX6675 raw[0] MAX6675_Read(GPIOA, GPIO_Pin_4); raw[1] MAX6675_Read(GPIOA, GPIO_Pin_7); raw[2] MAX6675_Read(GPIOB, GPIO_Pin_0); raw[3] MAX6675_Read(GPIOB, GPIO_Pin_1); // 转换成温度 for(i 0; i 4; i) { temp[i] MAX6675_ToCelsius(raw[i]); } // 刷新LCD显示 LCD_UpdateDisplay(temp); // 串口打印 printf(%.2f,%.2f,%.2f,%.2f\r\n, temp[0], temp[1], temp[2], temp[3]); // 等待下一个转换周期 Delay_Ms(250); }每次循环间隔250ms比MAX6675的220ms转换周期略长保证每次读到的都是最新转换结果。四路全部读取只占几毫秒剩下的时间都让给LCD刷新和串口发送整体刷新率大概在4Hz左右看温度变化完全够用。这里有一个容易忽略的点不要在读取MAX6675之后马上再读第二次一定要留有至少220ms的时间间隔。有的新手发现数据不变以为是传感器坏了其实是读取频率太快读到的全是上一次的缓存结果。4. 常见问题与排查技巧4.1 温度数据异常怎么定位温度读出来一直是0.00这是最常见的故障。排查优先级是先看CS引脚对应的GPIO是否配置成输出模式再看SCK引脚和SO引脚有没有接反最后用万用表量一下MAX6675模块的VCC和GND是不是真的3.3V。我遇到过一例就是模块供电线太细压降太大模块实际工作电压只有2.8V导致SPI通信不稳定。温度读出来忽高忽低跳变超过1摄氏度通常是电源问题或热电偶接触不良。K型热电偶的探头和引线连接处是焊接的经常弯折容易虚焊。可以用万用表测热电偶两端电阻正常值在几欧姆到几十欧姆之间如果电阻无穷大说明内部断线。另外热电偶线材要尽量远离交流电线和继电器之类的强干扰源否则感应出来的噪声会被MAX6675完整放大。温度读出来是负值最常见的原因就是热电偶正负极接反。K型热电偶在高温下接反读数不是负温度而是在0℃附近徘徊这个特征比较迷惑人。判断方法很简单用手捏住热电偶探头温度读数如果升高说明接线正确如果下降说明接反了。4.2 LCD和串口不工作怎么办LCD1602显示方块或者没反应90%的情况是初始化时序不对。注意LCD1602上电后必须延时至少40ms才能发第一条命令如果上电后立即初始化LCD内部还没准备好命令就丢失了。另外检查一下VO引脚也就是对比度调节脚通常要接一个10K电位器到GND阻值调到中间否则液晶显示可能淡得看不见。串口收不到数据优先级最高的排查项是USB转TTL模块的TXD和RXD有没有接反。STM32的TX要接模块的RXSTM32的RX接模块的TX。我见过很多次两个板子的TX接TX一头一头发出去谁也收不到。第二个排查项是波特率确认串口助手里的波特率和代码里配置的115200一致。第三是看PA9和PA10有没有被其他功能占用比如有些开发板用PA9接了LED或者按键需要断开。4.3 如何让四路采集更稳定四路MAX6675一起工作时如果某一路偶尔读到错误数据大概率是片选切换太仓促。代码里每次CS拉低之后、开始读数据之前加一个微秒级的延时让MAX6675的SO引脚有时间从高阻态切换到数据输出状态这个问题就会消失。我还在代码里加了一组软件滤波每次连续读取三遍取中间值执行效果很好。MAX6675本身是12位分辨率噪声本身不大但在电磁环境复杂的工业现场偶尔跳变一个LSB也很正常用中值滤波能消除绝大多数偶发异常。5. 后续扩展与低功耗思路5.1 从轮询到DMA如果后续想提升系统的效率可以考虑把软件模拟SPI改成硬件SPI DMA。但前面也说了MAX6675本身转换周期就有220ms四路轮询也就几毫秒完成普通场景下DMA带不来实际体验提升。真正需要DMA的场景是主控同时还要跑屏显、通信、PID控制等高占用的任务不想让SPI读取阻塞主循环。改DMA的思路是把四路MAX6675的CS都用GPIO控制SPI的SCK和MISO用硬件SPI1每次读取时用SPI_I2S_DMACmd开启接收DMADMA收到16位数据后自动触发中断在中断里读结果。这样主循环完全不用等待靠中断驱动数据刷新。5.2 低功耗场景优化如果你的项目需要电池供电可以参考STM32F103的停机模式。思路是主循环读一次温度、刷一次屏、发一次串口然后立刻进入STOP模式用定时器唤醒再重复以上流程。MAX6675转换期间其实不需要主控参与所以可以在MCU睡着的220ms里等它转换完醒过来直接读数据。实际测试下来这种“读温度-睡觉-醒-读温度”的节奏待机电流能从正常的40mA降到5mA左右。当然LCD1602和MAX6675本身的耗电占了大头真正要低功耗还得给传感器单独做电源开关这里就不展开说了。最后分享一个我做这套项目时的小心得给每个MAX6675模块贴上通道编号标签尤其是在机箱里走线的时候四根热电偶混在一起不贴标签就只能一路一路拔了试非常费时间。工程代码里我在结构体里也存了通道号和片选脚后续加通道只需要往数组里追加就行这也是多路采集项目最该提前规划好的部分。本文还有配套的精品资源点击获取