2024年寒假练 - 基于i.MX RT1021开发板用micropython实现的温度控制
该项目使用了i.MX RT1021、MicroPython语言、PID算法,实现了温度控制的设计,它的主要功能为:使用Micropython实现了在RT1021上用PID自动控制温度的算法。。
标签
嵌入式系统
测试
显示
2024年“寒假在家一起练”
aramy
更新2024-03-29
432

硬件介绍:
这次寒假一起练,一眼就喜欢上了这个带调试器的i.MX RT1021开发板。板子是基于恩智浦i.MX RT1020系列MIMXRT1021CAG4A芯片设计的综合开发、学习平台,支持触摸屏、编码器以及电位计输入,搭配下载器。适合高性能嵌入式系统学习以及GUI编程控制。

板子设计的很漂亮,总体分两部分。核心板:i.MX RT1020跨界MCU基于Arm?Cortex?-M7内核,运行频率高达500MHz,内置256KB片上RAM,该MCU系列提供各种存储器接口和丰富的连接接口,包括UART、SPI、I2C、USB、10/100M以太网和CAN,具有实时性能和高集成功能,该适用于工业和物联网应用。扩展板:搭配了多种类型的传感器和输入输出外设,包括温湿度传感器、三轴磁场传感器、加速度角速度传感器、环境光传感器,一个RGB彩灯、一块128*160分辨率的电阻式触摸屏,一个旋钮,一个双轴电位计摇杆。将调试和一组UART接口引出,可使用搭配的DAPlink进行调试和通信。并且搭配了一个12指神探作为烧写器。

任务选择:
因为这个板子的颜值,冲动了一把。入手后发现自己完全搞不懂恩智浦提供的例程,跟着老师视频课,也是仅仅能点亮LED灯而已。高估了自己能力,小看了这个板子的难度。

经过不断地努力学习,最终放弃了官方IDE的编程。在参考论坛帖子后,改道使用micropython,最终完成了任务。这里我选择的任务是:温湿度测量系统&自动恒温系统。要求:1)实现温湿度自动采集,并显示在屏幕上;2)实现恒温控制功能,进入恒温菜单后,可以实现温度设定,进入工作状态后,能达到设定温度,并显示当前温度曲线。

任务实现:

  1. 烧写固件:使用micropython第一步就是要烧写适合的固件。在NXP社区转悠了很久,有介绍如何制作MPY固件的帖子,但是没能尝试成功,有几个帖子有下载mpy固件的,但是不能用。最后终于找到一个能用的固件
    2.png
    image.png
  2. 使用micropython存在着几个问题。
    问题1:扩展板的IIC,SPI都不是硬件对应的输出管脚,这就意味着无法使用硬件的SPI、IIC来驱动屏幕和传感器了。这里使用了软件驱动SPI和IIC。能用,但是屏幕刷新比较慢。
    问题2:加热用的MOS管,在扩展板上对应的是“GPIO2_29(GPIO_EMC_29)”这个管脚在MPY中没有做映射,尝试了一下,可以控制这个GPIO口,但是PWM输出就不行了,无法做到PWM输出。SPI、IIC能模拟输出,但是PWM就没法模拟了,这里我采用跳线方式来解决。将三色led灯中的红色控制管脚“GPIO1_10”与MOS控制管脚短接。这样就可以输出PWM信号到“GPIO1_10”从而控制MOS管温度了。但是导致的后果就是,板子上电后,代码尚未运行,MOS管会导通,导致升温。
    Honeyview_3.jpg
    问题3:micropython固件中部分管脚没有做映射,但是经过实际测量,没有经过映射的管脚,直接写管脚名称也是能控制的。
>>> help(Pin.board)
object <class 'board'> is of type type
J2_39 -- Pin(GPIO_AD_B0_00)
J2_40 -- Pin(GPIO_AD_B0_01)
J2_37 -- Pin(GPIO_AD_B0_02)
J2_38 -- Pin(GPIO_AD_B0_03)
J2_35 -- Pin(GPIO_AD_B0_04)
J2_36 -- Pin(GPIO_AD_B0_05)
J2_33 -- Pin(GPIO_AD_B0_06)
J2_34 -- Pin(GPIO_AD_B0_07)
J2_31 -- Pin(GPIO_AD_B0_08)
J2_32 -- Pin(GPIO_AD_B0_09)
J2_29 -- Pin(GPIO_AD_B0_10)
J2_30 -- Pin(GPIO_AD_B0_11)
J2_27 -- Pin(GPIO_AD_B0_12)
J2_28 -- Pin(GPIO_AD_B0_13)
J2_25 -- Pin(GPIO_AD_B0_14)
J2_26 -- Pin(GPIO_AD_B0_15)
J2_23 -- Pin(GPIO_AD_B1_10)
J2_24 -- Pin(GPIO_AD_B1_11)
J2_21 -- Pin(GPIO_AD_B1_12)
J2_22 -- Pin(GPIO_AD_B1_13)
J2_19 -- Pin(GPIO_AD_B1_14)
J2_20 -- Pin(GPIO_AD_B1_15)
J3_08 -- Pin(GPIO_SD_B1_00)
J3_10 -- Pin(GPIO_SD_B1_01)
J3_12 -- Pin(GPIO_SD_B1_02)
J3_14 -- Pin(GPIO_SD_B1_03)
J3_16 -- Pin(GPIO_SD_B1_04)
J3_18 -- Pin(GPIO_SD_B1_05)
J3_20 -- Pin(GPIO_SD_B1_06)
J3_22 -- Pin(GPIO_SD_B1_07)
J3_24 -- Pin(GPIO_SD_B1_08)
J3_26 -- Pin(GPIO_SD_B1_09)
J3_28 -- Pin(GPIO_SD_B1_10)
J3_30 -- Pin(GPIO_SD_B1_11)
J3_07 -- Pin(GPIO_EMC_04)
J3_09 -- Pin(GPIO_EMC_05)
J3_11 -- Pin(GPIO_EMC_06)
J3_13 -- Pin(GPIO_EMC_07)
J3_15 -- Pin(GPIO_EMC_08)
J3_17 -- Pin(GPIO_EMC_09)
J3_19 -- Pin(GPIO_EMC_16)
J3_21 -- Pin(GPIO_EMC_17)
J3_23 -- Pin(GPIO_EMC_18)
J3_25 -- Pin(GPIO_EMC_19)
J3_27 -- Pin(GPIO_EMC_20)
J3_29 -- Pin(GPIO_EMC_21)
J3_31 -- Pin(GPIO_EMC_22)
J3_32 -- Pin(GPIO_EMC_23)
J3_33 -- Pin(GPIO_EMC_24)
J3_34 -- Pin(GPIO_EMC_25)
J3_35 -- Pin(GPIO_EMC_26)
J3_36 -- Pin(GPIO_EMC_27)
J3_37 -- Pin(GPIO_EMC_32)
J3_38 -- Pin(GPIO_EMC_33)
J3_39 -- Pin(GPIO_EMC_34)
J3_40 -- Pin(GPIO_EMC_35)
>>>

解决了以上问题就可以做程序设计啦!

4.png

驱动编码器:这里编码器使用两个中断进行驱动控制。编码器垂直按下:切换系统运行状态。设置或运行。设置状态下旋转编码器可以修改设定温度,顺时针旋转为增加设定温度,逆时针旋转为降低设定温度。每次变化步进值为0.5摄氏度。设定最高95摄氏度和最低30摄氏度的限制。当切换到运行状态时,编码器旋转不起作用了。此时MOS管开始工作,通过MOS管导通,来加热;当温度超过设定值时,MOS管关闭,通过环境来自然冷却板子。

class RT1021():
def __init__(self):
self.runstat=0 #运行状态 0:设置态 1:运行态
self.ispuh=True
self.setTempture=40.0 #设定温度
self.curTempture=float() #实际温度
self.queue=Queue(tempturevalnum,self.setTempture) #温度的队列
#初始化 PWM
Pin(Pin.cpu.GPIO_EMC_29,Pin.IN)
Pin(Pin.board.J2_25,Pin.OUT).on()
Pin(Pin.board.J2_26,Pin.OUT).on()
#Pin(Pin.board.J2_23,Pin.OUT).on()
self.pwm = PWM(Pin(Pin.board.J2_29),freq=1000,duty_u16=1, sync=True)
#屏幕初始化
spi = SoftSPI(baudrate=20000000, polarity=1, phase=0, sck=Pin(Pin.board.J3_37), mosi=Pin(Pin.board.J3_39), miso=Pin(Pin.cpu.GPIO_EMC_35))
Pin(Pin.cpu.GPIO_EMC_31,Pin.OUT).on() #打开背光
self.disp=TFT(spi,Pin.cpu.GPIO_EMC_36,Pin.cpu.GPIO_EMC_39,Pin.cpu.GPIO_EMC_33)
self.disp.rotation(2) #旋转屏幕
self.disp.initr()
self.disp.rgb(True)
self.disp.fill(TFT.BLACK) # 初始化屏幕

#sht30初始化
i2c = SoftI2C(scl=Pin(Pin.cpu.GPIO_EMC_02,Pin.OUT), sda=Pin(Pin.cpu.GPIO_EMC_03), freq=40000)
self.sht=SHT30(i2c,0,0,0x44) # 0x44是模块的I2C地址

# 编码器初始化
self.key = Pin(Pin.board.J3_09, Pin.IN, Pin.PULL_UP) # 中间按键
self.keyright = Pin(Pin.board.J3_13, Pin.IN, Pin.PULL_UP) # 左旋
self.keyleft = Pin(Pin.board.J3_17, Pin.IN, Pin.PULL_UP) # 右旋
self.key.irq(trigger=Pin.IRQ_FALLING, handler=self.int_keyhandler) # 编码器按键绑定 下降沿触发
self.keyleft.irq(handler=self.int_valhandler) # 编码器旋钮绑定 双向触发

# 编码器终端函数
def int_keyhandler(self, pin):
if self.ispush: # 处理被重复按下的问题
self.ispush = False
self.key.irq(handler=None) # 关闭中断
self.keyleft.irq(handler=None) # 关闭中断
self.runstat=(self.runstat+1)%2 #修改运行状态
print(self.runstat)
#修改屏幕显示
if self.runstat==0: #设置态
self.keyleft.irq(handler=self.int_valhandler)
self.key.irq(trigger=Pin.IRQ_FALLING, handler=self.int_keyhandler)

# 编码器 按键 处理函数
def int_valhandler(self, pin):
self.keyleft.irq(handler=None) # 关闭中断
#pinstr = str(pin)
#print(pinstr)
if self.keyleft.value() == 0: # 旋转操作
if self.keyright.value() == 1:
#print("顺时针")
self.setTempture=self.setTempture+0.5
if self.keyright.value() == 0:
#print("逆时针")
self.setTempture=self.setTempture-0.5

if self.keyleft.value() == 1:
if self.keyright.value() == 0:
#print("顺时针")
self.setTempture=self.setTempture+0.5
if self.keyright.value() == 1:
#print("逆时针")
self.setTempture=self.setTempture-0.5
if self.setTempture<limittemp[0]:
self.setTempture=limittemp[0]
if self.setTempture>limittemp[1]:
self.setTempture=limittemp[1]
#print(self.setTempture)
self.keyleft.irq(handler=self.int_valhandler)

屏幕部分,没有使用触摸部分,仅仅是用来做显示。当设定状态时,顶部会显示“Setting”,中间为设定温度值(红色),下方为实际温度值(绿色)。此时可以通过编码器旋钮上下调节设定温度。

5.jpg

7.jpg

当温度设定完成后,垂直按下编码器按钮,即可切换到运行模。此时顶部显示“Running”,中间位设定的温度值(黄色),下方为实际温度值(绿色)。此时编码器旋钮不起作用。在下方,显示当前温度变化曲线。

Honeyview_6.jpg

Honeyview_8.jpg

PWM值使用PID算法,直接移植了C语言下的算法,不过时间紧迫,相关参数调的不好。分析了一下几个原因吧。1、使用空气自然冷却,导致在到达设定温度值时,不能很好地继续加热。2、因为使用的模拟SPI,屏幕刷新很慢,相邻两次控制时间,目前控制为500毫秒,控制间隙过大。这些因素都会导致自动控制温度的波动。

import time

kp=5290.5
ki=0.72
kd=0.14
MAXOUTVAL=65535.0

class PID():
def __init__(self):
self.Sv=float() #用户设定值
self.Pv = float() #实际值
self.Ek = float() #本次偏差
self.Ek_1 = float() #上次偏差
self.SEK = float() #历史偏差之和
self.Iout = float()
self.Pout = float()
self.Dout = float()
self.OUT=float()

def PID_Calc(self):
self.Ek=self.Sv-self.Pv #得到当前的偏差值
self.Pout=kp*self.Ek_1
self.SEK=self.SEK+self.Ek

DelEk=self.Ek-self.Ek_1 #最近两次偏差之差
self.Iout=ki*self.SEK
self.Dout=kd*DelEk
out=self.Pout+self.Iout+self.Dout
if out>MAXOUTVAL :
self.OUT=MAXOUTVAL
elif out<1.0:
self.OUT=1
else:
self.OUT=out
self.Ek_1=self.Ek

心得体会
感谢funpack带来的这期活动。这次是这个板子颜值吸引了自己。忽视了板子的难度。希望以后的直播课能多讲点内容,面对新板子,我这样的小白还是无处下手。

附件下载
firmware.hex
micropython固件
rt1021.zip
mpy代码
团队介绍
折腾小能手,差点翻车君。
团队成员
aramy
单片机业余爱好者,瞎捣鼓小能手。
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号