我将针对这款FDM-3D打印机耗材干燥箱,从需求分析到系统实现,再到测试验证和维护升级,详细阐述如何构建一个可靠、高效、可扩展的系统平台。
关注微信公众号,提前获取相关推文
一、 需求分析
核心功能:
- 恒温控制: 使用PID算法精确控制干燥箱内部温度,确保耗材在最佳环境下干燥。
- 热风循环: 通过风扇产生热风循环,均匀加热箱内耗材,避免局部温度过高或过低。
- 温湿度显示: 使用0.91寸OLED屏幕实时显示箱内温度和湿度。
- 用户交互: 简单的按键操作,用于设置目标温度,以及查看当前状态。
扩展需求:
- 多档温度设置: 提供多档预设温度,适应不同耗材的干燥需求。
- 定时功能: 可以设置定时烘干时间,避免长时间加热。
- 异常报警: 当温度过高或过低时发出报警。
- 自适应PID: PID参数可以根据实际环境进行自适应调整。
- 远程监控: 可选,未来可增加WiFi模块实现远程监控。
可靠性要求:
- 安全可靠: 系统运行稳定,避免过热、短路等安全隐患。
- 精度要求: 温度控制精度在±1℃内。
- 长时间运行: 能够长时间稳定工作。
可扩展性要求:
- 软件架构: 软件架构模块化、易于扩展和维护。
- 硬件接口: 预留足够的硬件接口,方便添加新功能。
二、 系统架构设计
硬件架构:
- 主控芯片: 选择低功耗的单片机,如STM32F103C8T6(或ESP32等),具有足够的处理能力和外设接口。
- 温度传感器: 使用高精度温湿度传感器,如DHT22或SHT30。
- 加热模块: 使用PTC加热片或加热丝,配合PWM调速控制加热功率。
- 风扇: 使用直流风扇,配合PWM调速控制风速。
- OLED显示屏: 使用0.91寸I2C OLED显示屏。
- 按键: 若干按键用于用户交互。
- 电源模块: 为整个系统提供稳定的电源。
软件架构:
- 分层架构: 采用分层架构,将系统划分为硬件抽象层(HAL)、驱动层、服务层和应用层。
- 模块化设计: 每个功能模块都是独立的,方便开发和维护。
- 状态机: 使用状态机管理系统运行状态。
- 实时操作系统(RTOS): 可以考虑使用 FreeRTOS 或类似 RTOS 来管理任务。
三、 详细的代码设计架构及C代码实现
1. 硬件抽象层 (HAL)
- 目的: 屏蔽底层硬件差异,提供统一的接口给上层使用。
- 文件:
hal.h
,hal.c
1 | // hal.h |
1 | // hal.c |
2. 驱动层
- 目的: 封装硬件的具体操作,为上层提供统一的驱动接口。
- 文件:
driver_sensor.h
,driver_sensor.c
,driver_display.h
,driver_display.c
,driver_heater.h
,driver_heater.c
,driver_fan.h
,driver_fan.c
,driver_button.h
,driver_button.c
2.1 传感器驱动
1 | // driver_sensor.h |
1 | // driver_sensor.c |
2.2 OLED显示驱动
1 | // driver_display.h |
// driver_display.c
#include "driver_display.h"
#include "hal.h"
#include <stdio.h>
#include <string.h>
#define OLED_ADDR 0x3C // OLED的I2C地址
#define OLED_WIDTH 128 // OLED宽度
#define OLED_HEIGHT 64 // OLED高度
//OLED初始化数据
static const uint8_t OLED_INIT_CMD[] = {
0xAE, //--display off
0x00, //---set low column address
0x10, //---set high column address
0x40, //--set start line address Set Mapping RAM Display Start Line (0x00~0x3F)
0xB0, //--set page address
0x81, //--set contrast control register
0xCF, // Set SEG Output Current Brightness
0xA1, //--Set SEG/Column Mapping 0xa0左右反置 0xa1正常
0xC8, //Set COM/Row Scan Direction 0xc0上下反置 0xc8正常
0xA6, //--set normal display
0xA8, //--set multiplex ratio(1 to 64)
0x3f, //--1/64 duty
0xD3, //-set display offset Shift Mapping RAM Counter (0x00~0x3F)
0x00, //-not offset
0xd5, //--set display clock divide ratio/oscillator frequency
0x80, //--set divide ratio, Set Clock as 100 Frames/Sec
0xD9, //--set pre-charge period
0xF1, //Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
0xDA, //--set com pins hardware configuration
0x12,
0xDB, //--set vcomh
0x40, //Set VCOM Deselect Level
0x20, //-Set Page Addressing Mode (0x00/0x01/0x02)
0x02, //
0x8D, //--set Charge Pump enable/disable
0x14, //--set(0x10) disable
0xA4, // Disable Entire Display On (0xa4=Normal, 0xa5=All On)
0xA6,
0xAF //--display on
};
//OLED显存
uint8_t oled_buffer[OLED_WIDTH * OLED_HEIGHT / 8];
void display_write_cmd(uint8_t cmd) {
hal_i2c_write(OLED_ADDR, 0x00, cmd);
}
void display_write_data(uint8_t data) {
hal_i2c_write(OLED_ADDR, 0x40, data);
}
// 初始化显示
void display_init() {
hal_i2c_init();
hal_delay_ms(100);
for (int i = 0; i < sizeof(OLED_INIT_CMD); i++) {
display_write_cmd(OLED_INIT_CMD[i]);
}
display_clear();
display_update();
}
// 清除显示
void display_clear() {
memset(oled_buffer, 0x00, sizeof(oled_buffer));
}
// 显示字符串
void display_string(int x, int y, const char *str) {
uint8_t c;
while (*str) {
c = *str++;
if (c < 32 || c > 127) continue;
for (int i = 0; i < 8; i++) {
uint8_t line = (uint8_t)font8x16[(c - 32) * 16 + i*2];
uint8_t line2 = (uint8_t)font8x16[(c - 32) * 16 + i*2+1];
if (y + 2*i < OLED_HEIGHT)
{
oled_buffer[(y + 2 * i) / 8 * OLED_WIDTH + x + 0] |= line;
oled_buffer[(y + 2 * i + 1) / 8 * OLED_WIDTH + x + 0] |= line2;
}
}
x += 8;
if (x >= OLED_WIDTH) break; // 超出屏幕宽度,停止显示
}
}
// 显示数字
void display_number(int x, int y, int32_t num) {
char buffer[12];
sprintf(buffer, "%ld", num);
display_string(x, y, buffer);
}
//显示温度浮点型
void display_float(int x, int y, float num){
char buffer[12];
sprintf(buffer, "%.1f", num);
display_string(x, y, buffer);
}
// 更新显示
void display_update() {
for (int i = 0; i < OLED_HEIGHT / 8; i++) {
display_write_cmd(0xb0 + i); // 设置页地址
display_write_cmd(0x00); // 设置列地址低四位
display_write_cmd(0x10); // 设置列地址高四位
for (int j = 0; j < OLED_WIDTH; j++) {
display_write_data(oled_buffer[i * OLED_WIDTH + j]);
}
}
}
const uint8_t font8x16[][16] = {
// 0x20 Space
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
// 0x21 !
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00},
// 0x22 "
{ 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00},
// 0x23 #
{ 0x00, 0x38, 0x38, 0x7e, 0x38, 0x38, 0x38, 0x00, 0x00, 0x38, 0x38, 0x7e, 0x38, 0x38, 0x38, 0x00},
// 0x24 $
{ 0x00, 0x00, 0x30, 0x7c, 0x06, 0x7c, 0x18, 0x00, 0x00, 0x18, 0x7c, 0x06, 0x7c, 0x30, 0x00, 0x00},
// 0x25 %
{ 0x00, 0x63, 0x30, 0x18, 0x0c, 0x63, 0x00, 0x00, 0x00, 0x4e, 0x0c, 0x18, 0x30, 0x63, 0x00, 0x00},
// 0x26 &
{ 0x00, 0x38, 0x54, 0x54, 0x1c, 0x40, 0x3c, 0x00, 0x00, 0x0c, 0x2a, 0x2a, 0x1e, 0x01, 0x3e, 0x00},
// 0x27 '
{ 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00},
// 0x28 (
{ 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x00},
// 0x29 )
{ 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00},
// 0x2a *
{ 0x00, 0x18, 0x3c, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x3c, 0x18, 0x00, 0x00},
// 0x2b +
{ 0x00, 0x10, 0x10, 0x3c, 0x10, 0x10, 0x00, 0x00, 0x00, 0x10, 0x10, 0x3c, 0x10, 0x10, 0x00, 0x00},
// 0x2c ,
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00},
// 0x2d -
{ 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00},
// 0x2e .
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00},
// 0x2f /
{ 0x00, 0x00, 0x00, 0x40, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x10, 0x00, 0x00, 0x00},
// 0x30 0
{ 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00},
// 0x31 1
{ 0x00, 0x0c, 0x0c, 0x7c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x18, 0x7f, 0x00, 0x00, 0x00},
// 0x32 2
{ 0x00, 0x3c, 0x42, 0x32, 0x2a, 0x3c, 0x00, 0x00, 0x00, 0x7e, 0x03, 0x1c, 0x60, 0x7f, 0x00, 0x00},
// 0x33 3
{ 0x00, 0x3c, 0x42, 0x4a, 0x4a, 0x3c, 0x00, 0x00, 0x00, 0x7e, 0x03, 0x3c, 0x03, 0x7e, 0x00, 0x00},
// 0x34 4
{ 0x00, 0x06, 0x0e, 0x16, 0x7e, 0x06, 0x00, 0x00, 0x00, 0x18, 0x19, 0x1b, 0x7f, 0x00, 0x00, 0x00},
// 0x35 5
{ 0x00, 0x7e, 0x4a, 0x4a, 0x4a, 0x3e, 0x00, 0x00, 0x00, 0x7f, 0x40, 0x7c, 0x03, 0x7e, 0x00, 0x00},
// 0x36 6
{ 0x00, 0x3c, 0x4a, 0x4a, 0x4a, 0x3c, 0x00, 0x00, 0x00, 0x7e, 0x40, 0x7c, 0x43, 0x7e, 0x00, 0x00},
// 0x37 7
{ 0x00, 0x7e, 0x02, 0x04, 0x08, 0x10, 0x00, 0x00, 0x00, 0x7f, 0x01, 0x02, 0x04, 0x08, 0x00, 0x00},
// 0x38 8
{ 0x00, 0x3c, 0x4a, 0x4a, 0x4a, 0x3c, 0x00, 0x00, 0x00, 0x7e, 0x43, 0x7e, 0x43, 0x7e, 0x00, 0x00},
// 0x39 9
{ 0x00, 0x3c, 0x4a, 0x4a, 0x4a, 0x3c, 0x00, 0x00, 0x00, 0x7f, 0x03, 0x7e, 0x40, 0x7e, 0x00, 0x00},
// 0x3a :
{ 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x00, 0x00},
// 0x3b ;
{ 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00},
// 0x3c <
{ 0x00, 0x00, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0a, 0x11, 0x00, 0x00, 0x00},
// 0x3d =
{ 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x28, 0x28, 0x28, 0x28, 0x28, 0x00, 0x00},
// 0x3e >
{ 0x00, 0x00, 0x44, 0x28, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x0a, 0x04, 0x00, 0x00, 0x00},
// 0x3f ?
{ 0x00, 0x3c, 0x42, 0x02, 0x18, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x03, 0x0c, 0x00, 0x00, 0x00, 0x00},
// 0x40 @
{ 0x00, 0x3c, 0x42, 0x54, 0x54, 0x7c, 0x00, 0x00, 0x00, 0x7e, 0x01, 0x31, 0x41, 0x7f, 0x00, 0x00},
// 0x41 A
{ 0x00, 0x3c, 0x44, 0x44, 0x44, 0x7e, 0x00, 0x00, 0x00, 0x7f, 0x04, 0x08, 0x10, 0x7f, 0x00, 0x00},
// 0x42 B
{ 0x00, 0x7c, 0x4a, 0x4a, 0x4a, 0x3c, 0x00, 0x00, 0x00, 0x7f, 0x49, 0x49, 0x49, 0x36, 0x00, 0x00},
// 0x43 C
{ 0x00, 0x3c, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x7e, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00},
// 0x44 D
{ 0x00, 0x7c, 0x42, 0x42, 0x42, 0x7c, 0x00, 0x00, 0x00, 0x7f, 0x41, 0x41, 0x41, 0x3e, 0x00, 0x00},
// 0
}