1. 硬件连接和头文件
1.1 引脚定义 (max6675.h)
c
#ifndef __MAX6675_H
#define __MAX6675_H
#include "stm32f1xx_hal.h"
// MAX6675引脚定义
#define MAX6675_CS_PORT GPIOA
#define MAX6675_CS_PIN GPIO_PIN_4
#define MAX6675_SCK_PORT GPIOA
#define MAX6675_SCK_PIN GPIO_PIN_5
#define MAX6675_SO_PORT GPIOA
#define MAX6675_SO_PIN GPIO_PIN_6
// 函数声明
void MAX6675_Init(void);
float MAX6675_ReadTemp(void);
uint8_t MAX6675_CheckSensor(void);
float MAX6675_GetAverageTemp(uint8_t samples);
void MAX6675_StartConversion(void);
void MAX6675_Delay_us(uint32_t us);
#endif
2. SPI模拟驱动实现
2.1 基本驱动函数 (max6675.c)
c
#include "max6675.h"
#include "main.h"
#include <math.h>
// 全局变量
static uint8_t max6675_initialized = 0;
/**
* @brief 初始化MAX6675
*/
void MAX6675_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能GPIO时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置CS引脚为推挽输出
GPIO_InitStruct.Pin = MAX6675_CS_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(MAX6675_CS_PORT, &GPIO_InitStruct);
// 配置SCK引脚为推挽输出
GPIO_InitStruct.Pin = MAX6675_SCK_PIN;
HAL_GPIO_Init(MAX6675_SCK_PORT, &GPIO_InitStruct);
// 配置SO引脚为输入
GPIO_InitStruct.Pin = MAX6675_SO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(MAX6675_SO_PORT, &GPIO_InitStruct);
// 初始状态:CS高电平,SCK低电平
HAL_GPIO_WritePin(MAX6675_CS_PORT, MAX6675_CS_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(MAX6675_SCK_PORT, MAX6675_SCK_PIN, GPIO_PIN_RESET);
// 延时确保MAX6675准备好
HAL_Delay(100);
max6675_initialized = 1;
}
/**
* @brief 微秒延时函数
* @param us: 延时微秒数
*/
void MAX6675_Delay_us(uint32_t us)
{
uint32_t delay = us * (SystemCoreClock / 1000000) / 5;
while(delay--)
{
__NOP();
}
}
/**
* @brief 读取16位数据
* @retval 读取到的16位数据
*/
static uint16_t MAX6675_Read16Bits(void)
{
uint16_t data = 0;
// CS拉低,开始通信
HAL_GPIO_WritePin(MAX6675_CS_PORT, MAX6675_CS_PIN, GPIO_PIN_RESET);
// 等待至少100ns
MAX6675_Delay_us(1);
// 读取16位数据
for(uint8_t i = 0; i < 16; i++)
{
// SCK上升沿,MAX6675输出数据
HAL_GPIO_WritePin(MAX6675_SCK_PORT, MAX6675_SCK_PIN, GPIO_PIN_SET);
MAX6675_Delay_us(1);
// 读取数据位
if(HAL_GPIO_ReadPin(MAX6675_SO_PORT, MAX6675_SO_PIN) == GPIO_PIN_SET)
{
data |= (1 << (15 - i));
}
// SCK下降沿
HAL_GPIO_WritePin(MAX6675_SCK_PORT, MAX6675_SCK_PIN, GPIO_PIN_RESET);
MAX6675_Delay_us(1);
}
// CS拉高,结束通信
HAL_GPIO_WritePin(MAX6675_CS_PORT, MAX6675_CS_PIN, GPIO_PIN_SET);
return data;
}
/**
* @brief 读取温度值
* @retval 温度值(摄氏度),如果出错返回NAN
*/
float MAX6675_ReadTemp(void)
{
if(!max6675_initialized)
{
MAX6675_Init();
}
uint16_t raw_data = 0;
float temperature = 0.0f;
// 读取原始数据
raw_data = MAX6675_Read16Bits();
// 检查传感器是否连接正常
if(raw_data & 0x0004) // D2位为1表示热电偶开路
{
return NAN; // 返回NaN表示错误
}
// 提取温度数据位(D15-D3)
raw_data = raw_data >> 3;
// 转换为温度值(每个LSB对应0.25°C)
temperature = raw_data * 0.25f;
return temperature;
}
/**
* @brief 检查传感器状态
* @retval 0: 正常, 1: 热电偶开路
*/
uint8_t MAX6675_CheckSensor(void)
{
if(!max6675_initialized)
{
MAX6675_Init();
}
uint16_t raw_data = MAX6675_Read16Bits();
// 检查D2位(热电偶开路标志)
if(raw_data & 0x0004)
{
return 1; // 热电偶开路
}
return 0; // 正常
}
/**
* @brief 读取平均温度(多次采样取平均)
* @param samples: 采样次数
* @retval 平均温度值
*/
float MAX6675_GetAverageTemp(uint8_t samples)
{
if(samples == 0) samples = 1;
if(samples > 10) samples = 10; // 最多采样10次
float sum = 0.0f;
uint8_t valid_samples = 0;
for(uint8_t i = 0; i < samples; i++)
{
float temp = MAX6675_ReadTemp();
// 检查是否为有效温度(非NaN)
if(!isnan(temp))
{
sum += temp;
valid_samples++;
}
// 采样间隔
if(i < samples - 1)
{
HAL_Delay(10);
}
}
if(valid_samples > 0)
{
return sum / valid_samples;
}
else
{
return NAN; // 所有采样都无效
}
}
/**
* @brief 开始转换(可选,MAX6675自动转换)
*/
void MAX6675_StartConversion(void)
{
// MAX6675是自动转换的,但可以调用此函数触发新读取
// 实际只是模拟一下,确保在读取前有足够时间转换
HAL_GPIO_WritePin(MAX6675_CS_PORT, MAX6675_CS_PIN, GPIO_PIN_SET);
HAL_Delay(1);
}
3. 硬件SPI驱动实现(可选)
3.1 SPI硬件驱动版本 (max6675_spi.c)
c
#include "max6675.h"
#include "main.h"
// SPI句柄
extern SPI_HandleTypeDef hspi1;
/**
* @brief 硬件SPI方式读取温度
* @retval 温度值
*/
float MAX6675_SPI_ReadTemp(void)
{
uint16_t raw_data = 0;
float temperature = 0.0f;
// CS拉低
HAL_GPIO_WritePin(MAX6675_CS_PORT, MAX6675_CS_PIN, GPIO_PIN_RESET);
// 通过SPI读取16位数据
if(HAL_SPI_Receive(&hspi1, (uint8_t*)&raw_data, 1, 10) == HAL_OK)
{
// 交换字节顺序(如果需要)
raw_data = (raw_data << 8) | (raw_data >> 8);
}
else
{
HAL_GPIO_WritePin(MAX6675_CS_PORT, MAX6675_CS_PIN, GPIO_PIN_SET);
return NAN;
}
// CS拉高
HAL_GPIO_WritePin(MAX6675_CS_PORT, MAX6675_CS_PIN, GPIO_PIN_SET);
// 检查传感器状态
if(raw_data & 0x0004) // 热电偶开路
{
return NAN;
}
// 提取温度数据
raw_data = raw_data >> 3;
temperature = raw_data * 0.25f;
return temperature;
}
4. 温度处理模块
4.1 温度处理头文件 (temperature.h)
c
#ifndef __TEMPERATURE_H
#define __TEMPERATURE_H
#include <stdint.h>
#include <stdbool.h>
// 温度单位定义
typedef enum {
TEMP_UNIT_CELSIUS = 0,
TEMP_UNIT_FAHRENHEIT,
TEMP_UNIT_KELVIN
} TempUnit_t;
// 温度报警结构体
typedef struct {
float high_threshold; // 高温报警阈值
float low_threshold; // 低温报警阈值
float high_hysteresis; // 高温迟滞
float low_hysteresis; // 低温迟滞
bool high_alarm; // 高温报警状态
bool low_alarm; // 低温报警状态
} TempAlarm_t;
// 温度数据滤波结构体
typedef struct {
float buffer[10]; // 滤波缓冲区
uint8_t index; // 当前索引
uint8_t count; // 有效数据个数
uint8_t size; // 滤波器大小
} TempFilter_t;
// 函数声明
void Temp_Init(void);
float Temp_Read(void);
float Temp_GetAverage(uint8_t samples);
void Temp_ConvertUnit(float *temp, TempUnit_t from, TempUnit_t to);
void Temp_AlarmInit(TempAlarm_t *alarm, float high, float low, float hys);
void Temp_CheckAlarm(TempAlarm_t *alarm, float temp);
float Temp_FilterAdd(TempFilter_t *filter, float temp);
void Temp_FilterReset(TempFilter_t *filter);
bool Temp_IsValid(float temp);
#endif
4.2 温度处理实现 (temperature.c)
c
#include "temperature.h"
#include "max6675.h"
#include <math.h>
// 全局变量
static TempUnit_t current_unit = TEMP_UNIT_CELSIUS;
static TempAlarm_t temp_alarm = {0};
static TempFilter_t temp_filter = {0};
/**
* @brief 温度模块初始化
*/
void Temp_Init(void)
{
MAX6675_Init();
// 初始化温度滤波器
temp_filter.size = 5; // 5点滑动平均
Temp_FilterReset(&temp_filter);
// 初始化报警器
Temp_AlarmInit(&temp_alarm, 100.0f, 0.0f, 2.0f);
}
/**
* @brief 读取温度
* @retval 温度值
*/
float Temp_Read(void)
{
float temp = MAX6675_ReadTemp();
if(Temp_IsValid(temp))
{
// 滤波处理
temp = Temp_FilterAdd(&temp_filter, temp);
// 检查报警
Temp_CheckAlarm(&temp_alarm, temp);
}
return temp;
}
/**
* @brief 读取平均温度
* @param samples: 采样次数
* @retval 平均温度
*/
float Temp_GetAverage(uint8_t samples)
{
return MAX6675_GetAverageTemp(samples);
}
/**
* @brief 温度单位转换
* @param temp: 温度值
* @param from: 原始单位
* @param to: 目标单位
*/
void Temp_ConvertUnit(float *temp, TempUnit_t from, TempUnit_t to)
{
if(from == to) return;
// 先转换为摄氏度
switch(from)
{
case TEMP_UNIT_FAHRENHEIT:
*temp = (*temp - 32.0f) * 5.0f / 9.0f;
break;
case TEMP_UNIT_KELVIN:
*temp = *temp - 273.15f;
break;
default:
break;
}
// 从摄氏度转换到目标单位
switch(to)
{
case TEMP_UNIT_FAHRENHEIT:
*temp = *temp * 9.0f / 5.0f + 32.0f;
break;
case TEMP_UNIT_KELVIN:
*temp = *temp + 273.15f;
break;
default:
break;
}
}
/**
* @brief 温度报警器初始化
*/
void Temp_AlarmInit(TempAlarm_t *alarm, float high, float low, float hys)
{
alarm->high_threshold = high;
alarm->low_threshold = low;
alarm->high_hysteresis = hys;
alarm->low_hysteresis = hys;
alarm->high_alarm = false;
alarm->low_alarm = false;
}
/**
* @brief 检查温度报警
*/
void Temp_CheckAlarm(TempAlarm_t *alarm, float temp)
{
if(isnan(temp)) return;
// 高温报警检查
if(!alarm->high_alarm && temp >= alarm->high_threshold)
{
alarm->high_alarm = true;
}
else if(alarm->high_alarm && temp <= (alarm->high_threshold - alarm->high_hysteresis))
{
alarm->high_alarm = false;
}
// 低温报警检查
if(!alarm->low_alarm && temp <= alarm->low_threshold)
{
alarm->low_alarm = true;
}
else if(alarm->low_alarm && temp >= (alarm->low_threshold + alarm->low_hysteresis))
{
alarm->low_alarm = false;
}
}
/**
* @brief 温度滤波
* @param filter: 滤波器结构体
* @param temp: 新温度值
* @retval 滤波后的温度
*/
float Temp_FilterAdd(TempFilter_t *filter, float temp)
{
if(isnan(temp)) return 0.0f;
// 添加到缓冲区
filter->buffer[filter->index] = temp;
filter->index = (filter->index + 1) % filter->size;
if(filter->count < filter->size)
{
filter->count++;
}
// 计算平均值
float sum = 0.0f;
for(uint8_t i = 0; i < filter->count; i++)
{
sum += filter->buffer[i];
}
return sum / filter->count;
}
/**
* @brief 重置滤波器
*/
void Temp_FilterReset(TempFilter_t *filter)
{
for(uint8_t i = 0; i < filter->size; i++)
{
filter->buffer[i] = 0.0f;
}
filter->index = 0;
filter->count = 0;
}
/**
* @brief 检查温度是否有效
*/
bool Temp_IsValid(float temp)
{
if(isnan(temp)) return false;
if(temp < -270.0f || temp > 1370.0f) return false; // MAX6675范围
return true;
}
5. 主程序示例
5.1 主程序 (main.c)
c
#include "main.h"
#include "max6675.h"
#include "temperature.h"
#include "stdio.h"
#include "string.h"
// 全局变量
UART_HandleTypeDef huart1;
TIM_HandleTypeDef htim2;
// LCD显示相关(如果需要)
typedef struct {
float current_temp;
float max_temp;
float min_temp;
float avg_temp;
uint32_t sample_count;
} TempDisplay_t;
TempDisplay_t temp_display = {0};
TempAlarm_t temp_alarm = {0};
/**
* @brief 系统时钟配置
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 配置HSE
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
// 配置系统时钟
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
}
/**
* @brief GPIO初始化
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能GPIO时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
// 配置LED引脚
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// 配置报警输出引脚
GPIO_InitStruct.Pin = GPIO_PIN_12;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/**
* @brief USART1初始化
*/
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart1);
}
/**
* @brief TIM2初始化(用于定时采样)
*/
static void MX_TIM2_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 7200 - 1; // 10kHz时钟
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 10000 - 1; // 1秒定时
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
HAL_TIM_Base_Init(&htim2);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);
}
/**
* @brief 通过串口发送字符串
*/
void UART_SendString(char *str)
{
HAL_UART_Transmit(&huart1, (uint8_t*)str, strlen(str), HAL_MAX_DELAY);
}
/**
* @brief 通过串口发送温度数据
*/
void UART_SendTempData(float temp, uint8_t sensor_status)
{
char buffer[64];
if(sensor_status == 1)
{
sprintf(buffer, "ERROR: Thermocouple Open!\r\n");
}
else if(isnan(temp))
{
sprintf(buffer, "ERROR: Invalid Temperature!\r\n");
}
else
{
sprintf(buffer, "Temperature: %.2f°C\r\n", temp);
}
UART_SendString(buffer);
}
/**
* @brief 更新温度显示数据
*/
void UpdateTempDisplay(float temp)
{
if(isnan(temp)) return;
temp_display.current_temp = temp;
temp_display.sample_count++;
// 更新最大温度
if(temp > temp_display.max_temp || temp_display.sample_count == 1)
{
temp_display.max_temp = temp;
}
// 更新最小温度
if(temp < temp_display.min_temp || temp_display.sample_count == 1)
{
temp_display.min_temp = temp;
}
// 更新平均温度
temp_display.avg_temp = (temp_display.avg_temp * (temp_display.sample_count - 1) + temp) / temp_display.sample_count;
}
/**
* @brief 显示温度统计数据
*/
void DisplayTempStats(void)
{
char buffer[128];
sprintf(buffer, "\r\n=== Temperature Statistics ===\r\n");
UART_SendString(buffer);
sprintf(buffer, "Current: %.2f°C\r\n", temp_display.current_temp);
UART_SendString(buffer);
sprintf(buffer, "Maximum: %.2f°C\r\n", temp_display.max_temp);
UART_SendString(buffer);
sprintf(buffer, "Minimum: %.2f°C\r\n", temp_display.min_temp);
UART_SendString(buffer);
sprintf(buffer, "Average: %.2f°C\r\n", temp_display.avg_temp);
UART_SendString(buffer);
sprintf(buffer, "Samples: %lu\r\n", temp_display.sample_count);
UART_SendString(buffer);
sprintf(buffer, "=============================\r\n\r\n");
UART_SendString(buffer);
}
/**
* @brief 控制报警输出
*/
void ControlAlarmOutput(void)
{
static uint32_t blink_counter = 0;
if(temp_alarm.high_alarm)
{
// 高温报警:快速闪烁
if((blink_counter % 5) == 0)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_12);
}
}
else if(temp_alarm.low_alarm)
{
// 低温报警:慢速闪烁
if((blink_counter % 20) == 0)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_12);
}
}
else
{
// 正常状态:关闭报警
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
}
blink_counter++;
}
/**
* @brief TIM2定时器中断回调
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2)
{
static uint32_t counter = 0;
// 翻转LED指示运行状态
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_13);
// 每秒读取一次温度
if(counter % 1 == 0)
{
float temp = Temp_Read();
uint8_t status = MAX6675_CheckSensor();
UART_SendTempData(temp, status);
UpdateTempDisplay(temp);
}
// 每10秒显示一次统计信息
if(counter % 10 == 0)
{
DisplayTempStats();
}
// 控制报警输出
ControlAlarmOutput();
counter++;
}
}
/**
* @brief 主函数
*/
int main(void)
{
// HAL库初始化
HAL_Init();
// 系统时钟配置
SystemClock_Config();
// 外设初始化
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_TIM2_Init();
// 温度模块初始化
Temp_Init();
// 初始化报警器
Temp_AlarmInit(&temp_alarm, 100.0f, 10.0f, 2.0f);
// 启动定时器
HAL_TIM_Base_Start_IT(&htim2);
// 发送启动信息
UART_SendString("\r\n=== MAX6675 Temperature Monitor ===\r\n");
UART_SendString("Initialization Complete!\r\n\r\n");
// 主循环
while(1)
{
// 主循环中可以添加其他任务
// 例如:按键检测、屏幕刷新等
// 空闲时进入低功耗模式
__WFI();
}
}
6. Keil工程配置
6.1 工程配置要点
- Device: 选择对应的STM32型号(如STM32F103C8T6)
- Target: 设置正确的晶振频率
- C/C++: 添加包含路径和预定义符号
- Debug: 配置调试器
- Utilities: 配置Flash烧录算法
6.2 编译选项
makefile
# 在Keil的Options for Target -> C/C++中设置
# Define: STM32F103xB, USE_HAL_DRIVER
# Include Paths:
# ../Drivers/STM32F1xx_HAL_Driver/Inc
# ../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy
# ../Drivers/CMSIS/Device/ST/STM32F1xx/Include
# ../Drivers/CMSIS/Include
# ../Core/Inc
参考代码 max6675温度采集keil程序 www.youwenfan.com/contentcsv/71029.html
7. 高级功能扩展
7.1 PID温度控制 (pid_control.c)
c
#include "pid_control.h"
#include <math.h>
/**
* @brief PID控制器初始化
*/
void PID_Init(PID_Controller *pid, float kp, float ki, float kd, float out_min, float out_max)
{
pid->kp = kp;
pid->ki = ki;
pid->kd = kd;
pid->out_min = out_min;
pid->out_max = out_max;
pid->integral = 0.0f;
pid->prev_error = 0.0f;
pid->prev_time = 0;
}
/**
* @brief PID计算
*/
float PID_Calculate(PID_Controller *pid, float setpoint, float measured, uint32_t time_ms)
{
float error = setpoint - measured;
float dt = (time_ms - pid->prev_time) / 1000.0f; // 转换为秒
if(dt <= 0) dt = 0.1f; // 默认0.1秒
// 比例项
float proportional = pid->kp * error;
// 积分项
pid->integral += error * dt;
// 积分限幅
if(pid->integral > pid->out_max) pid->integral = pid->out_max;
if(pid->integral < pid->out_min) pid->integral = pid->out_min;
float integral = pid->ki * pid->integral;
// 微分项
float derivative = pid->kd * (error - pid->prev_error) / dt;
// 计算输出
float output = proportional + integral + derivative;
// 输出限幅
if(output > pid->out_max) output = pid->out_max;
if(output < pid->out_min) output = pid->out_min;
// 保存状态
pid->prev_error = error;
pid->prev_time = time_ms;
return output;
}
/**
* @brief PID重置
*/
void PID_Reset(PID_Controller *pid)
{
pid->integral = 0.0f;
pid->prev_error = 0.0f;
pid->prev_time = 0;
}
7.2 温度校准功能
c
// 温度校准结构体
typedef struct {
float offset; // 零点偏移
float gain; // 增益修正
float cal_temp1; // 校准点1温度
float read_temp1; // 校准点1读数
float cal_temp2; // 校准点2温度
float read_temp2; // 校准点2读数
} TempCalibration_t;
/**
* @brief 计算校准参数
*/
void TempCal_CalculateParams(TempCalibration_t *cal)
{
// 计算斜率和偏移
cal->gain = (cal->cal_temp2 - cal->cal_temp1) / (cal->read_temp2 - cal->read_temp1);
cal->offset = cal->cal_temp1 - (cal->read_temp1 * cal->gain);
}
/**
* @brief 应用温度校准
*/
float TempCal_Apply(float raw_temp, TempCalibration_t *cal)
{
return (raw_temp * cal->gain) + cal->offset;
}
8. 使用注意事项
-
硬件连接:
- VCC: 连接3.3V或5V电源
- GND: 连接电源地
- SCK: 连接时钟引脚
- CS: 连接片选引脚
- SO: 连接数据输出引脚
- T+ T-: 连接K型热电偶
-
注意事项:
- MAX6675最大测量温度:0-1024°C
- 精度:±2°C(0-700°C范围内)
- 分辨率:0.25°C
- 转换时间:170ms
- 需要冷端补偿
-
调试技巧:
- 使用逻辑分析仪检查SPI时序
- 添加CRC校验提高可靠性
- 实现软件看门狗防止程序卡死
- 添加掉电保护功能
-
优化建议:
- 使用DMA传输提高效率
- 实现环形缓冲区存储历史数据
- 添加温度变化率计算
- 实现温度曲线预测