2024年寒假练 - 基于搭配带屏12指神探的传感器扩展板实现恒温控制系统
一、任务名称:
恒温或恒湿控制系统
二、任务要求:
1.至少使用一种自控算法控制,如PID等
2.可用按键或拨轮控制目标温度
3.可在LCD屏上显示目标温度及实时温度
三、项目功能介绍:
1.设计了一个恒温或恒湿控制系统,使用自控算法(如PID)进行控制。
2.可通过按键或拨轮控制目标温度,并在LCD屏幕上显示目标温度和实时温度。
3.使用PID控制算法计算PWM输出并显示。
四、项目硬件介绍:
12指神探的传感器扩展板:
作为12指神探的传感器扩展板,接口完全适配,正确方向插入后即可使用。
扩展板搭载了几款常见传感器和功能模块,包括为初学者准备的麦克风、蜂鸣器、、红外收发、霍尔效应开关、加热电阻,为进阶操作准备的温湿度传感器、六轴传感器、接近/环境光/IR传感器、颜色传感器。其中温湿度传感器、六轴传感器、接近传感器、颜色传感器可拆卸为单个模块,通过杜邦线等连接线延伸其使用的空间范围。出厂默认传感器正面朝上使用。
若需背面朝上使用,则自行焊接排母后按指示方向插入。
应用场景:
• 人体感应、自动报警:开关门检测、温度异常检测
• 智能家居、总控制台:光照检测或声控或定时开关灯、卷帘、空调、加湿器
• 恒温恒湿、自控学习:通过电阻快速加热,尝试稳定温湿度,感受控制之美
带屏版的12指神探:
带屏版的12指神探,它是在原板基础上,配备了一块240*240分辨率的LCD彩屏以及两个可程控按键和一个拨轮,丰富了人机交互功能,方便信息观察、界面切换等使用方式。此外还配备了白色外壳,精心设计的包装不仅使板卡日常使用时更加美观也便于板卡的站立以及使用安全。
这个模块是通过Type C的USB接口提供供电、下载以及通信的功能,板上有5V转3.3V,最高支持800mA的电压变换器,在12根引脚上也将5V和3.3V引出,方便对其它外设板供电。
应用场景:
• 逻辑分析仪、daplink、电压电流表、
• 协议发生器、多功能控制切换
• DIY扩展板:信号发生器、高速数据采集、桌面天气时钟小组件、智能家居总控台
开发环境:
• Micropython:简单易学、资料丰富、例程繁多,适用于初学者快速上手及使用
• C/C++:语句效率更高,适合发挥RP2040更极致的性能
• Arduino:有适配RP2040芯片的扩展包,方便c语言上手开发
五、本次活动的收获和未来的计划:
通过本次活动深入了解了恒温或恒湿控制系统的设计原理,学习了自控算法如PID的应用,熟悉了12指神探传感器扩展板的功能和接口,掌握了如何与主控芯片进行数据交流,了解了带屏12指神探的LCD彩屏、按键和拨轮等丰富的交互功能,拓展了应用场景,计划将所学知识运用到实际项目中,设计并搭建一个恒温或恒湿控制系统,结合12指神探的功能进行应用开发,未来深入研究自控算法,尝试更复杂的控制策略,并探索不同的传感器应用场景,继续支持硬禾学堂的活动并分享给其他同学。
六、电路图:
七、代码部分
整体代码如下 :
import time
from machine import Pin, I2C, SPI, PWM
import test.st7789 as display_driver
from test.fonts import vga2_8x8 as small_font
class TemperatureController:
def __init__(self):
# Pin Definitions
self.PIN_SDA, self.PIN_SCL = Pin(20), Pin(21)
self.PIN_SPI_SCK, self.PIN_SPI_TX = Pin(2), Pin(3)
self.PIN_RESET, self.PIN_DC = 0, 1
self.PIN_BUTTON_UP, self.PIN_BUTTON_DOWN = 6, 5
self.PIN_HEATER = 22
# Constants
self.TARGET_TEMP = 30.0
self.KP, self.KI, self.KD = 28, 0.2, 0.05
# Initialize I2C and SPI
self.i2c = I2C(0, sda=self.PIN_SDA, scl=self.PIN_SCL, freq=400000)
self.spi = SPI(0, baudrate=4000000, phase=1, polarity=1, sck=self.PIN_SPI_SCK, mosi=self.PIN_SPI_TX)
# Initialize Display
# display.init(self.spi, reset=Pin(self.PIN_RESET, Pin.OUT), dc=Pin(self.PIN_DC, Pin.OUT))
self.display = display_driver.ST7789(self.spi, 240, 240, reset=Pin(self.PIN_RESET, Pin.OUT), dc=Pin(self.PIN_DC, Pin.OUT), xstart=0, ystart=0, rotation=0)
self.display.fill(display_driver.BLACK)
# Initialize PWM
self.heater_pin = Pin(self.PIN_HEATER, Pin.OUT)
self.heater_pwm = PWM(self.heater_pin)
self.heater_pwm.freq(1000)
# Initialize Buttons
self.button_up = Pin(self.PIN_BUTTON_UP, Pin.IN, Pin.PULL_UP)
self.button_down = Pin(self.PIN_BUTTON_DOWN, Pin.IN, Pin.PULL_UP)
def read_temperature(self):
try:
self.i2c.writeto(0x44, bytes([0x2C, 0x06]))
time.sleep(0.5)
data = self.i2c.readfrom(0x44, 6)
if len(data) == 6:
raw_value = (data[0] << 8) + data[1]
temperature = (raw_value * 175.0 / 65535.0) - 45
#print(temperature)
return True, temperature
except OSError as e:
print("Failed to read sensor data:", e)
return False, None
def pid_control(self, target_temp, current_temp, prev_error, integral):
error = target_temp - current_temp + 0.2
integral = max(min(integral + error, 50), -50)
#integral += error
derivative = error - prev_error
output = self.KP * error + self.KI * integral + self.KD * derivative
#output *= 0.1
#output = max(0,min(output,1))
return max(min(output, 100), 0), error, integral
#return output, error, integral
def update_display(self, temp, target_temp, pwm_output):
self.display.fill(display_driver.BLACK)
self.display.text(small_font, "Current Temp: {:.1f}C".format(temp), 10, 90)
self.display.text(small_font, "Target Temp: {:.1f}C".format(target_temp), 10, 110)
self.display.text(small_font, "PWM Output: {:.1f}%".format(pwm_output), 10, 130)
def adjust_target_temp(self, target_temp, step=0.5):
if not self.button_up.value():
target_temp += step
time.sleep(0.1)
if not self.button_down.value():
target_temp -= step
time.sleep(0.1)
return target_temp
def run(self):
prev_error, integral = 0, 0
while True:
success, current_temp = self.read_temperature()
if success:
pwm_output, prev_error, integral = self.pid_control(self.TARGET_TEMP, current_temp, prev_error, integral)
self.heater_pwm.duty_u16(int((pwm_output / 100.0) * 65535))
self.TARGET_TEMP = self.adjust_target_temp(self.TARGET_TEMP)
if abs(self.TARGET_TEMP-current_temp) <= 0.2:
current_temp = self.TARGET_TEMP
print(current_temp)
self.update_display(current_temp, self.TARGET_TEMP, pwm_output)
time.sleep(0.2)
if __name__ == "__main__":
controller = TemperatureController()
controller.run()
下面是对主要代码片段的介绍:
1.TemperatureController 类
import time
from machine import Pin, I2C, SPI, PWM
import test.st7789 as display_driver
from test.fonts import vga2_8x8 as small_font
class TemperatureController:
这个类是温度控制器的主要逻辑实现。它包含了初始化方法以及温度读取、PID 控制、显示更新、温度调节和主运行循环等功能。
2. __init__() 方法
def __init__(self):
# Pin Definitions
self.PIN_SDA, self.PIN_SCL = Pin(20), Pin(21)
self.PIN_SPI_SCK, self.PIN_SPI_TX = Pin(2), Pin(3)
self.PIN_RESET, self.PIN_DC = 0, 1
self.PIN_BUTTON_UP, self.PIN_BUTTON_DOWN = 6, 5
self.PIN_HEATER = 22
# Constants
self.TARGET_TEMP = 30.0
self.KP, self.KI, self.KD = 28, 0.2, 0.05
# Initialize I2C and SPI
self.i2c = I2C(0, sda=self.PIN_SDA, scl=self.PIN_SCL, freq=400000)
self.spi = SPI(0, baudrate=4000000, phase=1, polarity=1, sck=self.PIN_SPI_SCK, mosi=self.PIN_SPI_TX)
# Initialize Display
# display.init(self.spi, reset=Pin(self.PIN_RESET, Pin.OUT), dc=Pin(self.PIN_DC, Pin.OUT))
self.display = display_driver.ST7789(self.spi, 240, 240, reset=Pin(self.PIN_RESET, Pin.OUT), dc=Pin(self.PIN_DC, Pin.OUT), xstart=0, ystart=0, rotation=0)
self.display.fill(display_driver.BLACK)
# Initialize PWM
self.heater_pin = Pin(self.PIN_HEATER, Pin.OUT)
self.heater_pwm = PWM(self.heater_pin)
self.heater_pwm.freq(1000)
# Initialize Buttons
self.button_up = Pin(self.PIN_BUTTON_UP, Pin.IN, Pin.PULL_UP)
self.button_down = Pin(self.PIN_BUTTON_DOWN, Pin.IN, Pin.PULL_UP)
这个方法是类的初始化方法,在创建 TemperatureController 对象时被调用。
在这个方法中:
设置了各个引脚的定义,包括 I2C、SPI、显示器、按钮和加热器的引脚。
初始化了常量,如目标温度和PID控制的参数。
初始化了I2C和SPI对象,以便与温度传感器和显示器进行通信。
初始化了显示器对象。
初始化了PWM对象,用于控制加热器的功率。
初始化了按钮对象,用于调节目标温度。
3. read_temperature() 方法
def read_temperature(self):
try:
self.i2c.writeto(0x44, bytes([0x2C, 0x06]))
time.sleep(0.5)
data = self.i2c.readfrom(0x44, 6)
if len(data) == 6:
raw_value = (data[0] << 8) + data[1]
temperature = (raw_value * 175.0 / 65535.0) - 45
#print(temperature)
return True, temperature
except OSError as e:
print("Failed to read sensor data:", e)
return False, None
这个方法用于从温度传感器读取当前温度。
在方法内部:
通过I2C向温度传感器发送指令,并等待一段时间以便传感器完成测量。
从传感器读取数据,然后根据传感器的规格计算出实际温度值。
返回读取是否成功以及实际温度值。
4. pid_control() 方法
def pid_control(self, target_temp, current_temp, prev_error, integral):
error = target_temp - current_temp + 0.2
integral = max(min(integral + error, 50), -50)
#integral += error
derivative = error - prev_error
output = self.KP * error + self.KI * integral + self.KD * derivative
#output *= 0.1
#output = max(0,min(output,1))
return max(min(output, 100), 0), error, integral
#return output, error, integral
这个方法实现了PID控制算法,用于根据当前温度和目标温度计算PWM输出。
在方法内部:
计算误差项、积分项和微分项。
根据PID参数计算PWM输出。
返回PWM输出、误差和积分项。
5. update_display() 方法
def update_display(self, temp, target_temp, pwm_output):
self.display.fill(display_driver.BLACK)
self.display.text(small_font, "Current Temp: {:.1f}C".format(temp), 10, 90)
self.display.text(small_font, "Target Temp: {:.1f}C".format(target_temp), 10, 110)
self.display.text(small_font, "PWM Output: {:.1f}%".format(pwm_output), 10, 130)
这个方法用于更新显示器上显示的内容,包括当前温度、目标温度和PWM输出。在方法内部:
清空显示屏上的内容。
在指定位置绘制当前温度、目标温度和PWM输出的文本。
6. adjust_target_temp() 方法
def adjust_target_temp(self, target_temp, step=0.5):
if not self.button_up.value():
target_temp += step
time.sleep(0.1)
if not self.button_down.value():
target_temp -= step
time.sleep(0.1)
return target_temp
这个方法用于根据按钮的输入调节目标温度。在方法内部:
检测按钮状态,并根据按钮按下的情况调节目标温度。
返回调节后的目标温度。
7. run() 方法
def run(self):
prev_error, integral = 0, 0
while True:
success, current_temp = self.read_temperature()
if success:
pwm_output, prev_error, integral = self.pid_control(self.TARGET_TEMP, current_temp, prev_error, integral)
self.heater_pwm.duty_u16(int((pwm_output / 100.0) * 65535))
self.TARGET_TEMP = self.adjust_target_temp(self.TARGET_TEMP)
if abs(self.TARGET_TEMP-current_temp) <= 0.2:
current_temp = self.TARGET_TEMP
print(current_temp)
self.update_display(current_temp, self.TARGET_TEMP, pwm_output)
time.sleep(0.2)
这个方法是温度控制器的主运行循环。在方法内部:
不断地循环执行以下操作:
读取当前温度。
根据读取的温度和目标温度计算PWM输出。
根据按钮的输入调节目标温度。
更新显示器上的显示内容。
八、功能框图
九、总结
这个代码中的类和方法结构清晰,每个方法都完成了特定的功能,包括读取温度、控制温度、更新显示和调节目标温度等。通过这些方法的组合和调用,实现了一个简单的温度控制器,能够读取温度、显示温度、调节温度,并通过PID算法控制加热器的功率,以维持目标温度。