小王同学基于MSP430的恒温自动控制系统
2023寒假一起练平台(4)- 小王同学基于MSP430的恒温自动控制系统,使用TI微控制器,通过IIC总线读取温度传感器精确测量温度,使用PID算法控制电阻加热,实现自动控制恒温。
标签
嵌入式系统
Arduino
数字逻辑
自动控制
MSP430
six
更新2023-03-28
河南科技大学
551

2023寒假一起练平台(4)- 小王同学基于MSP430的恒温自动控制系统

本次选择的是项目4 - 实现一个恒温自动控制系统

IO扩展板上有一处加温电阻,将加热区域用物体(纸巾等)包裹起来,通过电流给电阻加热,并通过温度传感器感知板上温度的变化,测温以及在LCD屏上的温度显示。

 

一、开发版介绍

1、MSP430F5529:

MSP-EXP430F5529LP是一款针对MSP430F5529 USB微控制器的廉价而简单的开发套件。它为MSP430 MCU提供了一种简单的方法,具有用于编程和调试的板载仿真,以及用于简单用户界面的按钮和LED。

FqDKZS7tSDk02vvfs6-ruEvOL7GQ

2、主要参数特性:

  • 支持 USB 2.0 的 MSP430F5529 16 位微控制器
  • 高达 25 MHz
  • 128KB 闪存和 8KB 内存
  • 12 位 SAR 模数转换器
  • 提供各种 USB 设备类示例和嵌入式软件库(CDC、HID、MSC)
  • eZ-FET lite:带应用程序 UART 的开源板载调试器
  • 通过使用板载 USB 集线器,为仿真器和目标提供 USB 连接
  • USB 作为电源:5V 和 3.3V 通过高效 DC/DC 转换器
  • 40 针 LaunchPad 标准,利用 BoosterPack 生态系统

4、输入、输出扩展板介绍:

本扩展板包含如下功能:

  • 按键、旋转编码器输入 - 以模拟信号的方式
  • 双电位计控制输入 - 以数字信号的方式
  • RGB三色LED显示
  • 1.44寸128*128 LCD,SPI总线访问
  • MMA7660三轴姿态传感器
  • 电阻加热
  • 温度传感器
  • 与MSP430 Launch Pad开发板的接口

Fnc4RDCc6PM5ohGntlk3Y2jpVxwE

 

 

 

二、设计思路

    正如扩展板的原理图所示的加热-->控温:首先控制MOS管导通,使得电阻流过电流产生热量,进而使电路板升温,再使用温度传感器将温度值读出,根据当前的温度值不断地进行反馈控制MOS管通断,使电路板温度保持在设定值范围。

     在MSP430的扩展板上,电阻最大加热功率P=U²/R = 5*5/(68//68//68//68) ≈ 1.47W

    Fghmwfqqs5hGrIDpeHxs1TLd2dR6

根据如上所示的设计思路,结合msp430芯片特性,绘制出大体上的软件流程:

          FpteaZRG8JJ8O665ovOU-s_zXcD6

三、开发环境准备

1.Platform IO with Visual Studio Code,用于编写代码及下载程序。

2.ComAssistant,用于串口调试

 

四、项目任务的代码实现

      本次项目使用Arduino框架开发,Arduino上手容易,简单快捷。

     综合项目的要求,首先确定项目的具体实现需求:

      1.读取温度传感器

      2.驱动屏幕进行显示

      3.产生PWM波形驱动MOS管导通,通过电流给电阻加热

      4.使用编码器/按键进行温度的设置

 

      首先是温度传感器,型号为NST112-DSTR,这是一款超小封装的高精度低功耗温度传感器,通过I2C接口数字进行访问以获取温度。该传感器的用法也较为简单,只需配置两个寄存器即可。

#include "Arduino.h"
#include "Wire.h"//IIC库

#define NST112_ADDRESS 0x48     //温度传感器地址
#define GET_ADDR 0X00//读寄存器
#define SET_ADDR 0X01//写寄存器
#define VAL_BASE 0.0625//基准读数
#define NUM_BIT 12//数据位数
#define CFG_CMD_0 0b01100000//数据高
#define CFG_CMD_1 0b11100000//数据低//8HZ


void NST112_Init()
{
    Wire.begin();//默认引脚加入

    Wire.beginTransmission(NST112_ADDRESS);
    Wire.write(SET_ADDR);//进入设置寄存器
    Wire.write(CFG_CMD_0);//写寄存器1
    Wire.write(CFG_CMD_1 | (NUM_BIT==13?(1<<4):0));//写寄存器2
    Wire.endTransmission();//结束
}

float NST112_Read()
{
    byte MSB;
    byte LSB;
    uint16_t value;
    float temp;

        Wire.beginTransmission(NST112_ADDRESS);
    Wire.write(GET_ADDR);//进入温度寄存器
    Wire.endTransmission();//结束
    Wire.requestFrom(NST112_ADDRESS,2);
    while(Wire.available())
    {
        MSB = Wire.read();
        LSB = Wire.read();

        value = ((MSB<<8)|LSB) >>(NUM_BIT==13?3:4);
        temp = ((float)value)*VAL_BASE;
    }
    return temp ;
}

      屏幕的驱动IC是ST7735,1.44寸128*128分辨率 ,使用SPI接口驱动屏幕进行显示,为了方便,选用了Ucglib驱动库,在屏幕上显示出设定温度、当前温度、PWM占空比及加热状态。这样可以更为直观的看到温度的变化。

    ucg.setColor(0, 0, 0);
    ucg.drawBox(50, 32, 60, 80);
    ucg.setColor(255, 0, 0);

    ucg.setPrintPos(50, 105);
    ucg.print("Off");

    ucg.setPrintPos(50, 105);
    ucg.print("Ok");

    ucg.setPrintPos(50, 105);
    ucg.print("On");

    ucg.setPrintPos(50, 45);
    ucg.print(Temp_target);

    ucg.setPrintPos(50, 65);
    ucg.print(Temp_now);

    ucg.setPrintPos(50, 85);
    ucg.print(Temp_OUT_PWM);

      产生PWM波形则使用的是MSP430微控制器芯片的定时器功能,产生 PWM 信号用于其他设备的控制。在Arduino使用起来也是非常方便的。只需要确定输出引脚及占空比数值,使用analogWrite()函数即可。

pinMode(PWM_Pin, OUTPUT);
analogWrite(PWM_Pin, (uint16_t)Temp_OUT_PWM);

      当PWM波形为高电平时,驱动MOS管开启,此时电阻流过电流开始发热,当PWM波形为低电平时,驱动MOS管关闭,此时电阻没有电流不发热。这样,通过控制PWM波形的占空比,就可以控制电阻的平均功率,进而控制发热温度。

      在这个过程中,使用经久不衰的PID算法,计算出在设定温度下,相应的PWM波形占空比,并且随着实际温度自动进行调整,最终使其达到设定的温度。

      在PID算法中,还采用了积分分离的方式,防止温度超调,但是在最终的测试中,由于温控系统惯性环节较大,仅使用PD两项的系数就能达到较好的目标。

//单闭环位置PID//pid结构—目标-实际
float SinglePID_Position(Position_PID *pid, float Target, float Measure)
{
    float Err; //误差
    if (pid == NULL)
    {
        // SerialBT.println("pid is NULL");
        return 0.0;
    }
    Err = Target - Measure; //目标值减实际值
    /* 积分分离 */
    if (abs(Err) > (pid->Integraldead_zone)) //积分绝对值大于 积分盲区
    {
        pid->index = 0; //积分指数赋值0
    }
    else
    {
        pid->index = 1; //积分指数赋值1
    }
    pid->Output = pid->Kp * Err + pid->Kd * (Err - pid->Last_Err); //pd输出
    pid->Integral += pid->Ki * Err * pid->index; //积分累加
    constrain(pid->Output, pid->OutputMin, pid->OutputMax);
    pid->Integral = constrain(pid->Integral, pid->I_outputMin, pid->I_outputMax); ////积分限幅
    pid->Output += pid->Integral;                                         //pid完整的输出
    pid->Output = constrain(pid->Output, pid->OutputMin, pid->OutputMax); ////输出限幅
    pid->Last_Err = Err; //保存上一次控制量
    return pid->Output;  //返回pid控制量
}

       在该套控制系统中,由于温度传感器自动更新的频率为8Hz,为保证系统的一致性,所以PID程序的执行频率也应为8Hz,因此编写一个简易的定时任务,每隔125ms运行一次即可,包含温度读取,PID计算,更新占空比,刷新屏幕显示。

  if ((millis() > Time_timeout) || (Time_timeout < 125))
  {
    Temp_now = NST112_Read();

    Temp_OUT_PWM += SinglePID_Position(&Position_PID_Temp, Temp_target, Temp_now);
    Temp_OUT_PWM = constrain(Temp_OUT_PWM, 0, 255);
    ucg.setColor(0, 0, 0);
    ucg.drawBox(50, 32, 60, 80);
    ucg.setColor(255, 0, 0);

    // ucg.setColor(1, 255, 0, 0);

    if (Heating == 0)
    {
      Temp_OUT_PWM = 0;
      digitalWrite(LED_R_Pin, 1);
      ucg.setPrintPos(50, 105);
      ucg.print("Off");
    }
    else
    {
      // ledcWrite(ledChannel, (uint32_t)Temp_OUT_PWM);


      if (abs(Temp_now - Temp_target) < 1.0F)
      {
        digitalWrite(LED_R_Pin, 0);
        ucg.setPrintPos(50, 105);
        ucg.print("Ok");
      }
      else
      {
        digitalWrite(LED_R_Pin, 1);
        ucg.setPrintPos(50, 105);
        ucg.print("On");
      }
    }
      analogWrite(PWM_Pin, (uint16_t)Temp_OUT_PWM);

    ucg.setPrintPos(50, 45);
    ucg.print(Temp_target);

    ucg.setPrintPos(50, 65);
    ucg.print(Temp_now);
    ucg.setPrintPos(50, 85);
    ucg.print(Temp_OUT_PWM);

    Time_timeout = millis() + Temp_Timeout; //加上下一次的刷新毫秒输

    Serial.print("{T}"); //当前温度
    Serial.println(Temp_now);
    Serial.print("{Tt}"); //目标温度
    Serial.println(Temp_target);
    Serial.print("{PWM}"); //当前PWM值
    Serial.println(Temp_OUT_PWM);
  }

      最后是编码器、按键的部分,由于扩展板电路设计比较奇特,按键和编码器使用的是电阻网络分压的方案,因此使用ADC读取相应引脚的数值,即可判断出是某个按键按下,但是编码器比较特殊,硬件电路将编码器的AB相及中央按键均接入了该电阻网络,单次的读取并不能体现是否旋转,需要高频率地进行ADC采样,根据结果进行判断正/反转

      下图为采样频率200Hz的ADC采集数据。

      图1为顺时针旋转,图2为逆时针旋转,图三分别为编码器中央按键、普通按键上、普通按键下

FvfOOjftnHtOknZHgkbDSs-kyYCz

FhHGprONVZnGkw0UqoM_L7DvfdPw

FiQ_lgjvC01HWNfTDzBG8GqedUcI

      可以看出来编码器在进行不同方向的旋转时,ADC检测到的电压台阶顺序是不一样的,因此根据这个顺序,在符合电压最低处时判断上一台阶的电压值,即可分析出是哪个方向的旋转。

      事先使用手动方式测量出各个操作ADC的测量值,再根据此值判断即可

//ADC的几个测量值,对应无操作、编码器旋转(3个)、编码器按键按下、普通按键上、普通按键下按下。
uint16_t ADC_Val[7] = {3965, 3833, 3580, 3710, 3450, 2942, 1910};
#define ADC_error 50 //ADC的上下范围
//得到按键的值
void key_scan()
{
  ADC0 = analogRead(ADC_Pin);
  // Serial.print(millis());
  Serial.print("{A}");
  Serial.println(ADC0);
  //     Serial.print("{K}");
  // Serial.println(key_msg.id);
  if ((ADC0 > (ADC_Val[0] - ADC_error)) && (ADC0 < (ADC_Val[0] + ADC_error))) //未按下
  {
    for (uint8_t i = 0; i < (sizeof(key) / sizeof(KEY)); ++i)
    {
      key[i].val = 1;
    }
  }
  else if ((ADC0 > (ADC_Val[1] - ADC_error)) && (ADC0 < (ADC_Val[1] + ADC_error))) //旋转1
  {
    key[0].val = 0;
  }
  else if ((ADC0 > (ADC_Val[2] - ADC_error)) && (ADC0 < (ADC_Val[2] + ADC_error))) //旋转2
  {
    key[1].val = 0;
  }
  else if ((ADC0 > (ADC_Val[3] - ADC_error)) && (ADC0 < (ADC_Val[3] + ADC_error))) //旋转3
  {
    key[2].val = 0;
  }
  else if ((ADC0 > (ADC_Val[4] - ADC_error)) && (ADC0 < (ADC_Val[4] + ADC_error))) //编码器按键
  {
    key[3].val = 0;
  }
  else if ((ADC0 > (ADC_Val[5] - ADC_error)) && (ADC0 < (ADC_Val[5] + ADC_error))) //普通按键1
  {
    key[4].val = 0;
  }
  else if ((ADC0 > (ADC_Val[6] - ADC_error)) && (ADC0 < (ADC_Val[6] + ADC_error))) //普通按键1
  {
    key[5].val = 0;
  }
  else
  {
  }
  for (uint8_t i = 0; i < (sizeof(key) / sizeof(KEY)); ++i) //变更状态
  {
    if (key[i].last_val != key[i].val) //发生改变
    {
      key[i].last_val = key[i].val; //更新状态
      if (key[i].val == LOW)
      {
        key_msg.id = i;
        // key_msg.pressed = true;
      }
    }
  }
  if ((key_msg.id == 0) || (key_msg.id == 1) || (key_msg.id == 2))
  {
    if ((key_msg.id == 1)) //转到中间1的时候,判断上一次的值
    {
      switch (Encoder_Val_last)
      {
      case 0: //右转
        Encoder_Direction = 1;
        Temp_target += Add_Val;
        break;
      case 2: //左转
        Encoder_Direction = 0;
        Temp_target -= Add_Val;

        break;
      default:
        break;
      }
    }
    Encoder_Val_last = key_msg.id;
    key_msg.id = (-1);
  }
}

void KEY_update()
{
    if (key_msg.id != (-1))
    {
        if ((key_msg.id == 0) || (key_msg.id == 1) || (key_msg.id == 2))
        {
            if ((key_msg.id == 1)) //转到中间1的时候,判断上一次的值
            {
                switch (Encoder_Val_last)
                {
                case 0: //右转
                    Encoder_Direction = 1;
                    // Temp_target += Add_Val;
                    break;
                case 2: //左转
                    Encoder_Direction = -1;
                    // Temp_target -= Add_Val;

                    break;
                default:
                    break;
                }
            }
        }
        else if (key_msg.id == 3) //编码器按键
        {
            KEY_Encoder = false;
        }
        else if (key_msg.id == 4) //上按键
        {
            KEY_Key1 = false; //被按下为0
        }
        else if (key_msg.id == 5) //下按键
        {
            KEY_Key2 = false; //被按下为0
        }
        Encoder_Val_last = key_msg.id;
        key_msg.id = (-1);
    }
}

 

五、功能展示

      按下编码器中央按键时,切换开启/关闭加热,按下普通按键上时,切换需要设置的温度位数(10、1、0.1),顺时针旋转编码器增加设定值,逆时针旋转编码器减少设定值。

      当温度达到设定温度的±1℃时(原先要求的是±3℃,这里稍微提高了一下控制精度),亮起红灯,并在屏幕上显示“OK”字样。

 

Fo2KsEkRz0Rgwpak7UBuKywAajE1

FicJUm2Zw2Q2YpUfxKbmzRy6P7Uw

FjLlt3yFRi8uYqi-BjG1f9Pwaz-o

FntoktHqcMxOefbaevqyDGhTSYVL

 

六、遇到的主要难题及解决方法

      感觉该项目的主要难点就在编码器/按键方面,通过ADC读值的方式确定按键的状态,这样的好处也很明显,就是只用一个GPIO的端口即可完成多个按键的读取再有就是关于PID的部分,但是缺点就是需要高频率地使用ADC去读取模拟电压值,消耗时间,再加上MSP430微控制器本身频率就不高,还要进行彩屏的驱动。在寻找屏幕可用的驱动库时发现,由于MSP430的Arduino程序,与其他的互联网上大部分库均不能兼容,可能是由于编译器的版本问题,而且找到的这个库也不是很完善,屏幕右侧会出现两列花屏,在翻看了库中的代码后,也没有找到设置偏移是在哪里。最后由于温控系统的大惯性环节,当PWM占空比升高后,温度上升并没有那么迅速,因此PID中的微分系数需要比较大,才能使得系统稳定,且使用analogWrite()函数,占空比只能为8位(0-255),对温控的精确度方面有着不小的阻碍。

 

七、后续完善计划

      作为一个基本能够使用的恒温自动控制系统,已基本满足要求,但是还是有许多不足之处可以完善,比如在界面不够精美,按键交互不太完善,抗干扰性能不强等,由于时间关系暂未解决,后续的话有时间再抽空完善一下,这样总体的感觉也会提升。

 

八、参考资料

1.Arduino https://www.arduino.cc/

2.Energia https://energia.nu/

2.PlatformIO https://platformio.org/

3.MSP430F5529 https://www.ti.com.cn/product/cn/MSP430F5529?

4.高精度、低功耗数字温度传感器NST112 https://www.novosns.com/news-center-57

5.积分分离PID控制算法https://blog.csdn.net/songyulong8888/article/details/117389191

附件下载
MSP430_Project.rar
包含所有文件,解压使用PlatformIo打开即可
团队介绍
普通一枚在校大学生
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号