xunniqian 2026-02-24 15:07 采纳率: 0%
浏览 5

STM32+温度控制PID,达到设定值时,采样值剧烈波动

STM32F405作为主控,ADS1110作为温度采样,通过DAC输出模拟量,无论怎么进行PID整定,当达到目标值的时候,采样值会出现剧烈波动。结果如图:

img

代码如下:

#ifndef __ADS1110_I2C_H__
#define __ADS1110_I2C_H__

#include "main.h"

#define ADS1110_ADDR_A0    0x48  
#define ADS1110_ADDR_A1    0x49
#define ADS1110_ADDR_A2    0x4A
#define ADS1110_ADDR_A3    0x4B
#define ADS1110_ADDR_A4    0x4C
#define ADS1110_ADDR_A5    0x4D
#define ADS1110_ADDR_A6    0x4E
#define ADS1110_ADDR_A7    0x4F

// 设备类型
typedef enum {
    ADC_TYPE_ADS1110 = 0,
    ADC_TYPE_ADS1115 = 1,
    ADC_TYPE_UNKNOWN = 2
} ADC_Type;

// 模式
typedef enum {
    ADS1110_MODE_CONTINUOUS = 0x00,
    ADS1110_MODE_SINGLE     = 0x10
} ADS1110_Mode;

// 数据速率
typedef enum {
    ADS1110_DR_240SPS = 0x00,
    ADS1110_DR_60SPS  = 0x01,
    ADS1110_DR_30SPS  = 0x02,
    ADS1110_DR_15SPS  = 0x03
} ADS1110_DataRate;

// 增益
typedef enum {
    ADS1110_PGA_1 = 0x00,
    ADS1110_PGA_2 = 0x01,
    ADS1110_PGA_4 = 0x02,
    ADS1110_PGA_8 = 0x03
} ADS1110_Gain;

// ADS1110状态枚举
typedef enum {
    ADS1110_STATE_IDLE = 0,
    ADS1110_STATE_WAITING_DATA,
    ADS1110_STATE_DATA_READY,
    ADS1110_STATE_ERROR
} ADS1110_StateTypeDef;

// 数据结构用于存储读取结果
typedef struct {
    uint8_t rx_buf[3];
    int16_t raw_buf[5];     
    uint8_t raw_index;       
    int16_t raw_data;        
    float voltage;
    uint8_t gain_setting;
    uint8_t data_ready;
} ADS1110_Data;

extern ADS1110_Data ads1110_data;
extern ADC_Type adc_type;
extern ADS1110_StateTypeDef state;

ADC_Type Detect_ADC_Type(I2C_HandleTypeDef *hi2c, uint8_t dev_addr);

void ADS1110_Init(I2C_HandleTypeDef *hi2c, uint8_t dev_addr,
                    ADS1110_Mode mode, ADS1110_DataRate dr, ADS1110_Gain gain);
uint8_t ADS1110_IsDataReady(I2C_HandleTypeDef *hi2c, uint8_t dev_addr);

// DMA读取函数
HAL_StatusTypeDef ADS1110_StartRead_DMA(I2C_HandleTypeDef *hi2c, uint8_t dev_addr);
void ADS1110_ProcessData(void);

void TestDevice(void);
void USE_ADS1110(void);
void USE_ADS1110_DMA(void);

#endif


#include "ads1110_i2c.h"
#include "i2c.h"
#include "stdio.h"
#include "tempctrl.h"

ADS1110_StateTypeDef state = ADS1110_STATE_IDLE;
ADC_Type adc_type = ADC_TYPE_UNKNOWN;
ADS1110_Data ads1110_data;

ADC_Type Detect_ADC_Type(I2C_HandleTypeDef *hi2c, uint8_t dev_addr)
{
    uint8_t rx_data[3];
    HAL_StatusTypeDef status;
    uint8_t success_cnt = 0;
    
    printf("[ADS1110] 检测设备: 0x%02X\r\n", dev_addr);
    
    // 先检查设备是否响应
    if(HAL_I2C_IsDeviceReady(hi2c, dev_addr << 1, 3, 100) != HAL_OK) {
        printf("[ADS1110] 设备未响应\r\n");
        return ADC_TYPE_UNKNOWN;
    }

    for(int i = 0; i < 3; i++) {
        status = HAL_I2C_Master_Receive(hi2c, (dev_addr << 1) | 0x01, rx_data, 3, 100);
        if(status == HAL_OK) {
            success_cnt++;
        }
    }
    
    if(success_cnt == 3) {
        printf("[ADS1110] 连续3次读取成功,判定为ADS1110\r\n");
        return ADC_TYPE_ADS1110;
    }
    
    printf("[ADS1110] 读取失败或设备不匹配\r\n");
    return ADC_TYPE_UNKNOWN;
}

void ADS1110_Init(I2C_HandleTypeDef *hi2c, uint8_t dev_addr,
                    ADS1110_Mode mode, ADS1110_DataRate dr, ADS1110_Gain gain)
{
    printf("[ADS1110] 初始化...\r\n");
    
    uint8_t config = 0x00;
    
    // 构建配置字节
    config |= (mode & 0x10);           // 模式位(位4)
    config |= ((dr & 0x03) << 2);      // 数据速率位(位2-3)
    config |= (gain & 0x03);           // 增益位(位0-1)
    
    printf("[ADS1110] 发送配置: 0x%02X\r\n", config);
    
    if(HAL_I2C_Master_Transmit(hi2c, dev_addr << 1, &config, 1, 100) != HAL_OK)
    {
        printf("[ADS1110] 配置发送失败\r\n");
    }
    else
    {
        printf("[ADS1110] 配置发送成功\r\n");
    } 

    HAL_Delay(10);

    printf("[ADS1110] 初始化完成\r\n");
    
    ads1110_data.data_ready = 0;
    state = ADS1110_STATE_IDLE;
}


uint8_t ADS1110_GetConfig(I2C_HandleTypeDef *hi2c, uint8_t dev_addr)
{
    uint8_t rx_buf[3];
    
    if(HAL_I2C_Master_Receive(hi2c, (dev_addr << 1) | 0x01, rx_buf, 3, 100) != HAL_OK)
    {
        return 0xFF; 
    }
    
    return rx_buf[2]; 
}

uint8_t ADS1110_IsDataReady(I2C_HandleTypeDef *hi2c, uint8_t dev_addr)
{
    uint8_t rx_buf[3];

    if(HAL_I2C_Master_Receive(hi2c, (dev_addr << 1) | 0x01, rx_buf, 3, 100) != HAL_OK)
    {
        return 0;
    }

    // 第7位为0表示数据就绪
    return (rx_buf[2] & 0x80) ? 0 : 1;
}

// DMA读取
HAL_StatusTypeDef ADS1110_StartRead_DMA(I2C_HandleTypeDef *hi2c, uint8_t dev_addr)
{
    if(state == ADS1110_STATE_WAITING_DATA)
    {
        printf("[ADS1110] 已经在等待数据\r\n");
        return HAL_BUSY;
    }
    
    state = ADS1110_STATE_WAITING_DATA;
    ads1110_data.data_ready = 0;
    
    return HAL_I2C_Master_Receive_DMA(hi2c, (dev_addr << 1) | 0x01, ads1110_data.rx_buf, 3);
}

static void ADS1110_UpdateBuffer(ADS1110_Data *p_data) {
    p_data->raw_buf[p_data->raw_index] = p_data->raw_data;
    p_data->raw_index = (p_data->raw_index + 1) % 5;   // 循环索引
}

static float ADS1110_CalculateAverage(const ADS1110_Data *p_data) {
    int32_t sum = 0;
    for (int i = 0; i < 5; i++) {
        sum += p_data->raw_buf[i];
    }
    return (float)sum / 5.0f;
}

// 处理DMA读取完成的数据
void ADS1110_ProcessData(void)
{
    if(state != ADS1110_STATE_WAITING_DATA)
    {
        return;
    }

    if(ads1110_data.rx_buf[0] == 0xFF && ads1110_data.rx_buf[1] == 0xFF)
    {
        printf("[ADS1110] 接收到无效数据\r\n");
        state = ADS1110_STATE_ERROR;
        return;
    }

    ads1110_data.raw_data = (int16_t)((ads1110_data.rx_buf[0] << 8) | ads1110_data.rx_buf[1]);
    ads1110_data.gain_setting = ads1110_data.rx_buf[2] & 0x03;

    float gain_value;
    switch(ads1110_data.gain_setting)
    {
        case ADS1110_PGA_1: gain_value = 1.0f; break;
        case ADS1110_PGA_2: gain_value = 2.0f; break;
        case ADS1110_PGA_4: gain_value = 4.0f; break;
        case ADS1110_PGA_8: gain_value = 8.0f; break;
        default: gain_value = 1.0f;
    }
    
        if (ads1110_data.raw_data < 0) ads1110_data.raw_data = 0;
        
        ADS1110_UpdateBuffer(&ads1110_data);
        float raw_avg = ADS1110_CalculateAverage(&ads1110_data);
        
//    ads1110_data.voltage = (float)ads1110_data.raw_data / 32768.0f * 2.048f / gain_value;
        float voltage_avg = raw_avg / 32768.0f * 2.048f / gain_value;
    ads1110_data.data_ready = 1;
    state = ADS1110_STATE_DATA_READY;
        if (raw_avg < 0) raw_avg = 0;
        else if (raw_avg > 65535) raw_avg = 65535;
        adc_value = (uint16_t)(raw_avg + 0.5f);
        adc_conv_complete = 1;
}

void TestDevice(void)
{
    adc_type = Detect_ADC_Type(&hi2c1, ADS1110_ADDR_A0);
    printf("[ADS1110] 检测到设备类型: %d\r\n", adc_type);

    if(adc_type == ADC_TYPE_ADS1110) 
    {
        ADS1110_Init(&hi2c1, ADS1110_ADDR_A0, 
                      ADS1110_MODE_CONTINUOUS, 
                      ADS1110_DR_240SPS, 
                      ADS1110_PGA_1);
        
        uint8_t config = ADS1110_GetConfig(&hi2c1, ADS1110_ADDR_A0);
        printf("[ADS1110] 当前配置: 0x%02X\r\n", config);
        printf("[ADS1110] 初始化完成\r\n");
    }
    else
    {
        printf("[ADS1110] 不支持的设备类型\r\n");
    }
}

void USE_ADS1110_DMA(void)
{
    switch(state)
    {
        case ADS1110_STATE_IDLE:

            if(ADS1110_StartRead_DMA(&hi2c1, ADS1110_ADDR_A0) == HAL_OK)
            {
//                printf("[ADS1110] DMA读取已启动\r\n");
            }
            break;
            
        case ADS1110_STATE_DATA_READY:
            // 处理已读取的数据
            if(ads1110_data.data_ready)
            {
                            
//                            adc_conv_complete = 1;
//                printf("%d,%d\n", 
//                       ads1110_data.raw_data, adc_value);

                ads1110_data.data_ready = 0;
                state = ADS1110_STATE_IDLE;
            }
            break;
            
        case ADS1110_STATE_WAITING_DATA:
//            printf("[ADS1110] 等待数据中...\r\n");
            break;
            
        case ADS1110_STATE_ERROR:
            printf("[ADS1110] 错误状态,重置\r\n");
            state = ADS1110_STATE_IDLE;
            break;
    }
}

// I2C传输完成回调函数
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
    if(hi2c == &hi2c1)
    {
        ADS1110_ProcessData();
//        printf("[ADS1110] DMA读取完成\r\n");
//            HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_4);
    }
}

// I2C错误回调函数
void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c)
{
    if(hi2c == &hi2c1)
    {
        printf("[ADS1110] I2C通信错误\r\n");
        state = ADS1110_STATE_ERROR;
    }
}


#ifndef TEMPCTRL_H
#define TEMPCTRL_H

#include "main.h"
#include "dac.h"
#include "usart.h"
#include <stdio.h>

/* PID参数配置 */
#define KP 15.0f
#define KI 0.01f
#define KD 0.05f

/* 积分限幅 */
#define INTEGRAL_LIMIT 500.0f

/* DAC参数 */
#define DAC_RESOLUTION 4095  
#define DAC_MAX_VOLTAGE 3.0f
#define OUTPUT_MIN_VOLTAGE 0.0f
#define OUTPUT_MAX_VOLTAGE 3.0f

/* 函数声明 */
void TemCtrl_Init(void);
void TempCtrl(void);
float PID_Calculate(float setPoint, float actualInput);
void Update_DAC_Output(float output);
void Update_DAC_Voltage(float voltage_output);

/* 外部变量声明 */
extern uint16_t adc_value;
extern uint8_t rx_buffer[2];
extern uint8_t uart3_rec_flag;
extern uint8_t adc_conv_complete;
extern uint16_t setPoint;

#endif /* TEMPCTRL_H */


#include "tempctrl.h"
#include "ads1110_i2c.h"
#include "tim.h"
#include "usart.h"
#include "i2c.h"

uint16_t adc_value = 0;
uint8_t rx_buffer[2];
uint8_t uart3_rec_flag = 0;
uint8_t adc_conv_complete = 0;
uint16_t setPoint = 0x0498;

/* PID控制变量 */
float actualInput, lastInput, error, integral, derivative;
float pidOutput;
float lastError = 0;

float PID_Calculate(float setPoint, float actualInput) 
{
    error = setPoint - actualInput;

    integral += error;
    if (integral > INTEGRAL_LIMIT) integral = INTEGRAL_LIMIT;
    if (integral < -INTEGRAL_LIMIT) integral = -INTEGRAL_LIMIT;

    derivative = error - lastError;
    lastError = error;

    pidOutput = KP * error + KI * integral + KD * derivative;

    // 输出限幅及积分抗饱和
    if (pidOutput > 100.0f) {
        pidOutput = 100.0f;
//        if (error > 0) integral -= error;   
    } else if (pidOutput < 0.0f) {
        pidOutput = 0.0f;
//        if (error < 0) integral -= error;   
    }

    lastInput = actualInput;
    
    return pidOutput;
}

void Update_DAC_Output(float output)
{
    float voltage_output;
    uint16_t dac_value;

    voltage_output = (output / 100.0f) * OUTPUT_MAX_VOLTAGE;
    if (voltage_output < OUTPUT_MIN_VOLTAGE) voltage_output = OUTPUT_MIN_VOLTAGE;
    if (voltage_output > OUTPUT_MAX_VOLTAGE) voltage_output = OUTPUT_MAX_VOLTAGE;

    dac_value = (uint16_t)((voltage_output / DAC_MAX_VOLTAGE) * DAC_RESOLUTION);
    HAL_DAC_SetValue(&hdac, DAC_CHANNEL_2, DAC_ALIGN_12B_R, dac_value);
}

void TemCtrl_Init(void) 
{
    /* 启动DAC通道 */
    HAL_DAC_Start(&hdac, DAC_CHANNEL_2);
    HAL_DAC_SetValue(&hdac, DAC_CHANNEL_2, DAC_ALIGN_12B_R, 2048);

    /* 初始化PID变量 */
    error = 0;
    integral = 0;
    derivative = 0;
    lastInput = 0;
    lastError = 0;

    HAL_UART_Receive_IT(&huart3, rx_buffer, sizeof(rx_buffer));
    
    printf("Temperature Control Initialized with DAC Output\r\n");
    printf("Default Setpoint: 0x%04X\r\n", setPoint);
}

void TempCtrl(void) 
{
    static uint32_t last_pid_time = 0;
    uint32_t current_time = HAL_GetTick();

    if(adc_conv_complete == 1) 
    {
        adc_conv_complete = 0;

//        if(current_time - last_pid_time >= 100) 
//        {
//            last_pid_time = current_time;
//                        HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_4);
//            if (adc_value > 0x0490) 
//            {

                pidOutput = PID_Calculate((float)setPoint, (float)adc_value);

                if (pidOutput < 0.0f) pidOutput = 0.0f;
                if (pidOutput > 100.0f) pidOutput = 100.0f;

                Update_DAC_Output(pidOutput);

                printf("%d,%d,%.2f\n", 
                       adc_value, setPoint, pidOutput);
//            } 
//            else 
//            {

////                Update_DAC_Output(0.0);
//                printf("0x%04X\n", adc_value);
//            }
//        }
    }

    if (uart3_rec_flag) 
    {
        uart3_rec_flag = 0;

        uint16_t new_setpoint = (rx_buffer[0] << 8) | rx_buffer[1];
        setPoint = (float)new_setpoint;

        integral = 0;

        printf("New setpoint received: 0x%04X\r\n", new_setpoint);

        HAL_UART_Receive_IT(&huart3, rx_buffer, sizeof(rx_buffer));
    }
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) 
{
    if (huart->Instance == USART3) 
    {
        uart3_rec_flag = 1;
    }
}


int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_DAC_Init();
  MX_USART3_UART_Init();
  MX_TIM6_Init();
  MX_USART6_UART_Init();
  MX_TIM8_Init();
  MX_I2C1_Init();
  /* USER CODE BEGIN 2 */
    TestDevice();
    TemCtrl_Init();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
    
        while (1)
        {
            USE_ADS1110_DMA();
            TempCtrl();
        // 短暂延时
      HAL_Delay(1);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    }
  /* USER CODE END 3 */
}

有没有佬支支招啊(大哭),已经调了快两个星期了

  • 写回答

2条回答 默认 最新

  • 檀越@新空间 2026-02-24 15:08
    关注

    下午好🌅🌅🌅
    本答案参考通义千问

    您描述的问题是:在STM32F405上使用ADS1110进行温度采样,通过DAC输出模拟量控制温度,无论怎么进行PID整定,当达到目标值时,采样值会出现剧烈波动。

    这个问题通常与系统稳定性、传感器噪声、PID参数设置不当或硬件设计问题有关。下面我将从多个方面分析原因,并给出解决方案。


    🔍 一、可能的原因分析

    1. ADC采集不稳定

    • ADS1110的采样率较低(比如60SPS),如果采样频率过低,可能会导致数据不准确。
    • 如果没有正确配置ADS1110的增益(PGA)和数据速率(DR),可能导致采样值误差较大。

    2. PID参数设置不合理

    • PID中的**积分项(I)**容易引起超调和震荡,尤其是在设定值附近时,积分项累积过大,导致输出波动。
    • **微分项(D)**对噪声敏感,如果输入信号有噪声,会导致输出剧烈变化。

    3. 传感器噪声干扰

    • 温度传感器(如热电偶、热敏电阻)本身存在一定的噪声,尤其是使用ADS1110时,如果没有进行滤波处理,可能导致采样值波动。

    4. DAC输出不稳定

    • DAC的输出精度不够或存在毛刺,可能导致控制信号不稳定,从而引发反馈系统的震荡。

    5. 系统响应时间过长

    • 如果系统响应时间较长,PID控制器无法及时调整,也会造成振荡。

    ✅ 二、解决方案

    1. 优化ADC配置与采样方式

    • 确保ADS1110的增益(PGA)和数据速率(DR)设置合理。
    • 使用DMA方式进行连续采样,避免CPU中断影响采样精度。

    ✅ 修改后的代码示例:

    // 在ads1110_i2c.c中添加DMA接收回调函数
    void HAL_I2C_RxCpltCallback(I2C_HandleTypeDef *hi2c)
    {
        if (hi2c == &hi2c1) {
            // 处理ADS1110数据
            ADS1110_ProcessData();
        }
    }
    
    // 启动DMA读取
    HAL_StatusTypeDef ADS1110_StartRead_DMA(I2C_HandleTypeDef *hi2c, uint8_t dev_addr)
    {
        uint8_t tx_data = 0x00; // 指向配置寄存器
        return HAL_I2C_Master_Transmit_DMA(hi2c, (dev_addr << 1), &tx_data, 1);
    }
    

    重点: 使用DMA进行连续采样可以提高采样精度,减少CPU负担。


    2. 优化PID参数

    • 降低积分增益(Ki),防止积分项积累过大。
    • 增加微分增益(Kd),但需注意避免噪声放大。
    • 可以尝试使用自整定PID算法,或者手动逐步调试。

    ✅ 示例PID结构体定义:

    typedef struct {
        float Kp;
        float Ki;
        float Kd;
        float setpoint;
        float integral;
        float last_error;
        float output;
    } PID_Controller;
    

    ✅ PID计算函数:

    float PID_Calculate(PID_Controller *pid, float input)
    {
        float error = pid->setpoint - input;
        pid->integral += error;
        float derivative = error - pid->last_error;
    
        pid->output = pid->Kp * error + pid->Ki * pid->integral + pid->Kd * derivative;
        pid->last_error = error;
    
        return pid->output;
    }
    

    重点: 适当降低Ki,增加Kd,可有效抑制震荡。


    3. 增加信号滤波

    • 对ADC采集的原始数据进行软件滤波,如滑动平均滤波、中位值滤波等。

    ✅ 示例滤波函数:

    #define FILTER_SIZE 10
    int16_t filter_buffer[FILTER_SIZE];
    uint8_t buffer_index = 0;
    
    float Filter_Average(int16_t raw_value)
    {
        filter_buffer[buffer_index] = raw_value;
        buffer_index = (buffer_index + 1) % FILTER_SIZE;
    
        int32_t sum = 0;
        for (int i = 0; i < FILTER_SIZE; i++) {
            sum += filter_buffer[i];
        }
    
        return (float)sum / FILTER_SIZE;
    }
    

    重点: 采用滑动平均滤波可有效平滑ADC噪声。


    4. 检查DAC输出稳定性

    • 确保DAC的输出电压稳定,无毛刺。
    • 如果使用PWM生成模拟电压,需确保PWM频率足够高,避免抖动。

    5. 降低系统响应延迟

    • 如果系统响应较慢,可考虑降低采样频率,或增加PID的预估功能(如前馈控制)。

    🧠 三、建议操作步骤

    1. 确认ADS1110配置是否正确

      • 检查增益(PGA)、数据速率(DR)是否符合需求。
      • 确认I2C通信是否稳定,无丢包。
    2. 测试ADC采样数据

      • 在未接入PID控制的情况下,单独测试ADC采样值是否稳定。
      • 如果ADC数据波动大,可能是硬件或配置问题。
    3. 简化PID控制逻辑

      • 先只使用P控制,观察系统是否稳定。
      • 再逐步引入I和D项,调节参数。
    4. 加入滤波机制

      • 在ADC采集后加入软件滤波,提升数据质量。
    5. 优化PID参数

      • 通过实验逐步调整Kp、Ki、Kd,直到系统稳定。

    📌 四、总结

    | 问题 | 解决方案 | |------|----------| | ADC采集不稳定 | 优化ADS1110配置,使用DMA采样 | | PID参数不当 | 调整Kp、Ki、Kd,降低积分增益 | | 传感器噪声大 | 加入软件滤波(如滑动平均) | | DAC输出不稳定 | 检查DAC配置,确保输出平稳 | | 系统响应延迟 | 降低采样频率,或加入前馈控制 |


    如果您能提供具体的PID参数或ADC采样数据,我可以进一步帮助您分析具体问题所在。希望以上内容对您有所帮助!

    评论

报告相同问题?

问题事件

  • 创建了问题 2月24日