十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

MAX6675热电偶测温实战:SPI读取与C51/STM32例程详解

MAX6675热电偶测温实战:SPI读取与C51/STM32例程详解 简介面向单片机与嵌入式开发者这份资源以MAX6675热电偶数字转换器为核心提供C51与STM32两套可直接运行的例程源码覆盖K型热电偶信号调理、冷端自动补偿、温度数字转换及串口显示等完整环节。压缩包共177个文件大小约6.6MB工程文件、C源码、烧录文件与文档互为配套。其中12个PDF和5个DOC文档汇集了MAX6675芯片手册、原理及应用、多路温度采集、高精度测温、热电偶温度表测量电路以及基于单片机实现的发动机尾气参数采集、注塑机温控、电阻炉温控等设计报告既适合课程设计与毕业设计选题参考也便于从原理图到代码逐层对照。目前已有1955人学习下载开发者可直接将C51或STM32驱动移植到自己的温度采集项目中并结合文档中的系统框架、误差分析与调试过程快速上手。1. 拿到MAX6675热电偶C51和STM32例程先弄清楚它在解决哪个问题第一次用K型热电偶做测温最容易踩的坑不是量程不够而是冷端补偿。热电偶输出的是微伏级热电动势冷端一变化读数就跟着漂。MAX6675把前端放大、冷端补偿和ADC整个集成在芯片里通过SPI直接输出0.25°C分辨率的数字温度值C51例程和STM32例程同时出现说明这类项目最常见的落地方式就是软件模拟SPI或外设SPI读取。C51例程解决51系单片机的低速GPIO模拟时序STM32例程解决SPI外设配置、16位数据帧和字节序问题而相关应用技术文档资料则把冷端补偿电路、滤波和PCB上的坑补齐。三块内容合起来正好覆盖从单片机最小系统到稳定读温的完整链路适合正在做加热台、温控烤箱、热风枪、实验室水温采集的人也适合刚开始接触热电偶采样的工程师照着代码过一遍时序。2. MAX6675的SPI数据格式与K型热电偶冷端补偿原理2.1 为什么MAX6675能省掉热电偶电桥补偿电路热电偶测温的本质是把温差变成电压。以K型热电偶为例测量端放在高温区接线端放在电路板上测到的热电动势是这个温差对应的值。接线端温度一变同样的被测温度会得到不同电压所以必须做冷端补偿。传统做法是外接热电偶电桥补偿电路用桥路里的铜电阻抵消冷端温度影响再送进仪表放大器。MAX6675把这一整套做进芯片。MAX6675封装内部有一个温度传感器代表冷端温度ADC转换前会把补偿电压和热电偶电压相加输出的数字量直接就是补偿后的温度。硬件上要做的事情是保证芯片附近的温度尽量等于热电偶接线端的温度。很多人以为芯片集成了冷端补偿就可以随便摆放元件实际恰恰相反冷端补偿做得越智能芯片周围的热场越要稳定这部分在第5章展开。2.2 16位SPI帧怎么拆温度值、0.25°C分辨率与开路标志MAX6675通过SO口串行输出16位数据高位在前。CS低有效SCK空闲为低例程里常见读法是在SCK高电平期间读取SO。把16位原值称为raw先判断开路raw 0x0004为真时热电偶回路开路常见原因是热电偶断线、接线端子松动或者热电偶线没插到底。未开路时温度提取公式是固定的把raw右移3位再取低12位然后乘0.25。下面这段不依赖平台的C代码可以直接用来验证数据格式。#include stdint.h void max6675_cs(int level) { /* 由平台实现 */ } void max6675_sck(int level) { /* 由平台实现 */ } uint8_t max6675_so(void) { return 0; } uint16_t max6675_read_raw(void) { uint16_t raw 0; uint8_t i; max6675_cs(0); for (i 0; i 16; i) { max6675_sck(0); max6675_sck(1); raw 1; if (max6675_so()) raw | 1; } max6675_cs(1); return raw; } float max6675_celsius(uint16_t raw) { if (raw 0x0004) { return -1.0f; /* 热电偶开路调用方自行处理 */ } return (float)((raw 3) 0x0FFF) * 0.25f; }读取顺序是先把SCK拉低再拉高raw先左移一位再把SO状态放到最低位。这样第1个bit最终落在bit15符合高位在先的SPI习惯。如果先读再移位结果会变成低字节在前用示波器或串口打印一次就能看出来。raw右移3位后取低12位是因为MAX6675的温度数据在bit14到bit3bit2是开路标志bit1和bit0不用参与温度计算。2.3 不依赖平台的模拟SPI读取先验证数据格式上面代码的最大价值是隔离了平台差异C51和STM32的例程里核心循环几乎都是这个结构。MAX6675对SCK频率不敏感模拟时序只要不把CS提前拉高16位都能正确收到。很多例程会在CS拉低后加一个短延时等待SO输出第一位这是为了消除GPIO翻转后的建立时间在慢速51上问题不大在STM32上模拟时序时反而要注意别把延时压缩到0。连续两次读取之间必须等待一个转换周期MAX6675内部转换不是随时完成的常见量级在200ms左右所以主循环里读到一次温度后延时220ms再读下一次。如果连续读得太快显示值会在几个相近温度之间跳动那不是滤波问题是采样节奏问题。先确认这个节奏再谈滤波。3. 吃透C51例程Keil C51环境、引脚配置和时序实现3.1 Keil5同时装C51和STM32的坑以及C51工程从哪里下手C51例程在Keil C51下编译STM32例程在Keil MDK-ARM下编译两者可以共存于同一个Keil5安装目录。很多人先装了MDK双击C51工程文件时报错找不到C51编译器原因就是工具链没装全。常见做法是在Pack Installer里把C51编程器支持装好老版本C51 v9.59a或更新的版本都可以然后重启Keil5再打开工程。STM32例程这边需要单独装芯片支持包STM32F1用对应F1系列packG0、L4等系列各自装对应pack。如果工程本身是基于标准库的芯片包只解决器件选择和下载算法问题标准库文件通常已经放在工程目录的Libraries文件夹里不需要额外下载。还有一类工程是HAL库的CubeMX生成的代码会依赖STM32Cube FW包打开后第一件事是确认编译器的Include Path里库路径存在。3.2 C51核心读温函数与主循环写法C51例程常用sbit定义三个引脚好处是可以直接控制某一位而不用掩盖整组端口。下面这段可以直接放进Keil C51工程编译。#include REGX52.H #include intrins.h sbit TC_CS P1^0; sbit TC_SCK P1^1; sbit TC_SO P1^2; unsigned int max6675_read_raw(void) { unsigned char i; unsigned int value 0; TC_CS 0; _nop_(); for (i 0; i 16; i) { TC_SCK 0; _nop_(); TC_SCK 1; _nop_(); value 1; if (TC_SO) value | 0x0001; } TC_CS 1; return value; } unsigned int max6675_to_temperature(unsigned int raw) { if (raw 0x0004) { return 0xFFFF; /* 开路标志 */ } return (raw 3) 0x0FFF; /* 单位是0.25°C */ }void main(void) { unsigned int raw, code; while (1) { raw max6675_read_raw(); code max6675_to_temperature(raw); if (code 0xFFFF) { /* 断线处理例如点亮故障灯 */ } else { /* code 乘 25 后是百分之一摄氏度便于数码管显示 */ } /* 等待下一个转换周期 */ delay_ms(220); } }C51里int固定16位0xFFFF用作错误码不会和正常温度值冲突但注意返回前要把整个raw右移raw是unsigned int右移是逻辑右移不会引入符号位扩展。主循环里的delay_ms必须自己实现可以用定时器或者_nop_()拼但不能在MAX6675读取期间关中断太久否则读数时间拉长会导致CS低电平持续时间过长。3.2.1 引脚分配用sbit还是整组端口读sbit方式可读性好改引脚只要改三行定义适合多项目复用。整组读端口的方式在C51例程里也常见例如把整个P1读进一个变量再按位取SO这样做的好处是省一条端口操作指令坏处是引脚布局被端口捆死换板就要重写。MAX6675本身只需要三根信号线用sbit散接在任意IO上是更稳的选择。有的资料会提到C51的SPI通信库文件51系列里带硬件SPI的单片机确实可以用SPI外设但MAX6675模块往往不带标准引脚定义硬件SPI引脚固定反而限制布局。自己做模拟时序只要在读取期间不被打断效果完全一样而且可以直接套到STM32的GPIO模拟上。3.3 C51例程里最容易出现的两处偏差第一处是移位顺序。有人习惯先判断SO再左移写成if(TC_SO) value | 1; value 1;这样读进来的bit全部向左错了一位最终温度会变成实际值的两倍左右。统一用先移位后写最低位不管改到哪套代码都不会乱。第二处是开路判断。MAX6675的开路标志在bit2有的例程写成if(raw 0x04)有人图省事直接判断if(raw 0x0004)这在温度接近0时会把正常读数当成开路。先用raw 0x0004判开路再对raw右移这个顺序不能反过来右移之后bit2已经丢了。4. STM32例程里SPI外设配置与16位读取的实现4.1 用STM32的SPI外设读取MAX6675的标准配置STM32例程如果还用GPIO模拟SPI不是不能跑只是浪费了片内外设而且GPIO翻转受中断影响时序抖动比硬件SPI大。MAX6675读取频率不高用SPI1做主模式、16位数据帧、MSB先行一次就能把16位读回来。下面以STM32F1标准库为例。#include stm32f10x_spi.h #include stm32f10x_gpio.h #define TC_CS_LOW() GPIO_ResetBits(GPIOA, GPIO_Pin_4) #define TC_CS_HIGH() GPIO_SetBits(GPIOA, GPIO_Pin_4) void max6675_spi_init(void) { GPIO_InitTypeDef gpio; SPI_InitTypeDef spi; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE); gpio.GPIO_Pin GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; gpio.GPIO_Mode GPIO_Mode_AF_PP; gpio.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, gpio); gpio.GPIO_Pin GPIO_Pin_4; gpio.GPIO_Mode GPIO_Mode_Out_PP; GPIO_Init(GPIOA, gpio); TC_CS_HIGH(); spi.SPI_Direction SPI_Direction_2Lines_FullDuplex; spi.SPI_Mode SPI_Mode_Master; spi.SPI_DataSize SPI_DataSize_16b; spi.SPI_CPOL SPI_CPOL_Low; spi.SPI_CPHA SPI_CPHA_1Edge; spi.SPI_NSS SPI_NSS_Soft; spi.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_32; spi.SPI_FirstBit SPI_FirstBit_MSB; SPI_Init(SPI1, spi); SPI_Cmd(SPI1, ENABLE); } uint16_t max6675_read_raw(void) { uint16_t raw 0; TC_CS_LOW(); SPI_I2S_SendData(SPI1, 0x0000); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) RESET); raw SPI_I2S_ReceiveData(SPI1); TC_CS_HIGH(); return raw; }PA5接SCKPA6接SOPA7接MOSIPA4接CS。MAX6675没有真正的MOSI输入PA7只是用于产生时钟发送空数据可以不接线但SPI外设必须往数据寄存器写值才能启动时钟。预分频32在72MHz主频下得到2.25MHz落在MAX6675可接受的时钟范围内如果主频更高就加大预分频SCK超过4MHz容易读到错误数据。4.2 标准库和HAL库的读温细节差异HAL库的读取写法更短但要注意数据类型和字节序。uint16_t max6675_read_raw_hal(void) { uint16_t raw 0; HAL_GPIO_WritePin(TC_CS_GPIO_Port, TC_CS_Pin, GPIO_PIN_RESET); HAL_SPI_Receive(hspi1, (uint8_t *)raw, 1, 100); HAL_GPIO_WritePin(TC_CS_GPIO_Port, TC_CS_Pin, GPIO_PIN_SET); return raw; }如果CubeMX里SPI Data Size配置的是8位这里的HAL_SPI_Receive只收8位MAX6675需要16个SCK剩下的8个时钟不会被触发。常见做法是在CubeMX的Parameter Settings里把Data Size改为16Bit然后重新生成代码。另一条路是连续调用两次HAL_SPI_Receive每次8位再把两个字节拼起来。16位接收还有一个细节MAX6675输出是高字节先发而STM32是小端处理器读回来的raw可能要做一次字节交换。例程里如果发现温度值大得离谱比如读到上万先加一行无条件字节交换验证raw (raw 8) | (raw 8);交换后温度恢复合理区间就保留这行如果本身合理不要加否则会把温度高字节和低字节搞混。这个坑和SPI模式无关只和处理器字节序有关。4.3 读到的温度一直是0或固定值时的常规排错顺序先查GPIO配置CS必须是推挽输出SO引脚如果配成复用推挽会读不到数据SO应该配成输入F1上用GPIO_Mode_IN_FLOATING或上拉输入都行。再查SPI时钟PA5上没有脉冲就回头查GPIO和SPI的AFIO重映射PA5默认就是SPI1_SCK不需要重映射但如果用的是SPI2和PB13等引脚要先查复用功能。接着查CS时序CS必须在SPI收发期间保持低电平读完再拉高。很多人把CS拉低和HAL_SPI_Receive写在同一个函数里但如果CubeMX生成的初始化代码里NSS是硬件模式SPI外设会自动管理NSS和软件CS打架表现为温度偶尔为0。解决方法是把NSS配成软管理或者干脆只用软件CS方式。5. MAX6675应用技术文档里最值得抄的参数与电路注意5.1 关键参数在例程里怎么对应应用技术文档资料不会只有例程源码真正值钱的是参数和波形。拿到文档后先看这几项温度分辨率、转换周期、热电偶开路标志的位定义、SPI时序图。把这些和例程代码对上才算读懂了程序。参数常见量级在例程中的对应位置温度分辨率0.25°C(raw 3) * 0.25温度量程约0°C到1023°Cunsigned int计算即可转换周期200ms左右主循环delay 220ms热电偶开路高有效raw 0x0004判断线SPI空闲电平CS高、SCK低CPOL0、CPHA0这些参数里最容易忽略的是转换周期。STM32硬件SPI速度高主循环跑得飞快如果不延时一秒能读上百次但MAX6675内部转换速度跟不上读出来的全是上一次转换结果。文档里写的采样周期是设计依据例程里的delay不是随便加的。5.2 冷端与电桥补偿相关的PCB细节MAX6675内部已经集成了冷端补偿电路外部要注意的是让芯片温度能真实反映冷端温度。PCB上MAX6675尽量靠近热电偶接线端子远离MOS管、变压器、功率电阻和散热器。不要在MAX6675背面铺大面积铜箔散热也不要在芯片旁边放发热量大的电源芯片否则内部温度传感器测到的是板温而不是接线端温度。T和T-是微伏级信号输入走线要短两侧包地。常见应用文档会建议在T和T-之间就近并联一个0.1μF小电容用来滤除空间耦合进来的高频噪声但电容太大会拖慢温度响应反馈回路里不要并大电容。老方案里的热电偶电桥补偿电路需要自己选铜电阻和调零MAX6675把这些做进芯片后外部要做的是减少温差而不是调电桥。5.3 滤波和断线检测放在哪一层更合适显示通道上可以加简单滤波控制通道上要谨慎。MAX6675的0.25°C分辨率已经够细滑动平均会引入滞后对温控系统来说是坏事。显示场景可以用中位数滤波连续读三次取中间值能滤掉接触不良造成的毛刺。uint16_t max6675_filter_raw(void) { uint16_t a, b, c, t; a max6675_read_raw_hal(); b max6675_read_raw_hal(); c max6675_read_raw_hal(); if (a b) { t a; a b; b t; } if (b c) { t b; b c; c t; } if (a b) { t a; a b; b t; } return b; }中位数滤波的代价是读取时间乘以三所以只适合显示刷新不适合温控闭环。断线检测也要做容错热电偶偶尔接触不良时单次读到开路标志不必立即报警连续两次读到raw 0x0004再置故障位能避免误报。判断完开路再滤温度顺序不能反否则开路时会把垃圾数据当成温度值。6. 从例程到项目串口输出、示波器验证和跨平台移植技巧6.1 先用串口把温度打出来拿到C51或STM32例程后第一件事不是接热电偶而是把读到的raw和换算后的温度同时打印到串口。只打印温度看不到原始位没办法判断字节序和开路标志。打印格式可以写成raw0x1234 temp45.25这样温度和原始值能对上。STM32上重定向printf到USART1后主循环里调一次读取打印一次延时220ms。如果室温下读到的温度和实际温度差十几度先检查是不是冷端附近有发热元件如果raw一直在0x0004附近跳说明开路检测被反复触发接线问题比软件问题可能性大。6.2 用示波器验证SPI时序示波器三通道分别接CS、SCK和SO触发设成CS下降沿。正常情况下CS低电平期间应该有16个SCK脉冲SO的数据在SCK高电平期间保持稳定。如果SO只在SCK上升沿附近抖动说明采样相位偏了把软件模拟里的“SCK高电平读”改成“SCK低电平读”再试。如果CS拉低后SCK没有动说明主循环卡在延时或中断里检查读取函数是不是被放在不可重入的中断服务程序中。6.3 把C51逻辑平移到STM32时只改三处第一处是引脚定义sbit换成HAL_GPIO_WritePin或标准库的GPIO_SetBits。第二处是引脚操作函数CS、SCK、SO的读写直接替换不需要改算法。第三处是延时_nop_()换成微秒级延时但读取循环里SCK翻转之间的延时可以去掉硬件SPI本来就不需要软件延时。剩下的移位、右移3位、乘0.25、判断raw 0x0004完全一致。移植完成后用一个简单方法验证把热电偶测量端放进冰水混合物读数值应该在0°C附近再把测量端用手捏住读数缓慢升到体温附近。如果反应方向反了多半是T和T-接反了。资料里的波形图对比重点放在CS下降沿到第一个SCK上升沿这段时间这个参数在模拟时序例程里经常被忽略也是示波器验证时唯一需要测量时间间隔的位置。本文还有配套的精品资源点击获取
返回列表