🖨️ ESP32 Mini打印机:打印机芯控制模块
驱动热敏打印头,实现图像数据的逐行加热与走纸同步
基于SPI传输 + 选通脉冲 + 步进电机联动
📌 为什么需要打印头控制?
热敏打印头是打印机的核心部件,工作时需要:
- 接收图像数据:每个点对应一个加热点(黑色/白色)
- 精确选通:分时段加热不同区域的加热点,避免瞬间电流过大
- 同步走纸:打印完一行后,电机步进到下一行位置
打印头控制模块就是打印机的“打印引擎”,必须高速、同步、可靠。
直观理解:
就像点阵式打印机的针头,但这里是用热来“烫”出图像,每个加热点就是一“针”。
一、硬件原理简述
热敏打印头结构
- 通常有 384个加热点(48字节 × 8位)
- 分 6个通道(STB1~STB6) 轮流加热,每个通道控制64个点
- 加热时需要提供 VH(电源),通过
PIN_VHEN控制通断
接口定义
| 引脚 | 功能 | 说明 |
|---|---|---|
| SCK | SPI时钟 | 数据传输时钟 |
| SDA | SPI数据 | MOSI,主机发送数据 |
| LAT | 数据锁存 | 数据发送完毕,锁存到移位寄存器 |
| STB1~STB6 | 通道选通 | 高电平使能对应通道加热 |
| VHEN | 加热电源使能 | 高电平允许VH供电 |
工作流程
- 打开加热电源(
VHEN = HIGH) - 通过SPI发送48字节数据(384位)到打印头
- 发出锁存信号(
LAT低脉冲),将数据锁存到输出寄存器 - 依次选通各个通道(
STB1~STB6高电平),每通道加热一定时间 - 加热期间电机同步走纸(步进电机移动)
二、代码实现
以下代码实现了打印头初始化、数据发送、单通道加热、整张图片打印等功能。
1. 引脚与参数定义
#include <Arduino.h>
#include "SPI_CODE.h"
#include "MOTOR.h"
#define PIN_SCK 15
#define PIN_SDA 13
#define PIN_LAT 12
#define PIN_STB1 26
#define PIN_STB2 27
#define PIN_STB3 14
#define PIN_STB4 32
#define PIN_STB5 33
#define PIN_STB6 25
#define PRINT_TIME 1700 // 打印加热时间(微秒)
#define PRINT_END_TIME 200 // 冷却时间(微秒)
#define LAT_TIME 1 // 数据锁存时间(微秒)
#define PIN_VHEN 17 // 加热电源控制引脚
核心参数说明:
PRINT_TIME:每个通道加热时长,影响打印黑度PRINT_END_TIME:通道之间的冷却间隔,防止过热LAT_TIME:锁存脉冲宽度,一般1微秒足够
2. 辅助函数:通道关闭、初始化打印、停止打印
// 关闭所有通道(STB1~STB6)
static void set_stb_idle(){
digitalWrite(PIN_STB1, LOW);
digitalWrite(PIN_STB2, LOW);
digitalWrite(PIN_STB3, LOW);
digitalWrite(PIN_STB4, LOW);
digitalWrite(PIN_STB5, LOW);
digitalWrite(PIN_STB6, LOW);
}
// 初始化打印状态:关闭所有通道、锁存器置高、开启加热电源
static void init_printing(){
set_stb_idle();
digitalWrite(PIN_LAT, HIGH);
digitalWrite(PIN_VHEN, HIGH);
}
// 停止打印:关闭加热电源、关闭所有通道、锁存器置高
void stop_printing(){
digitalWrite(PIN_VHEN, LOW);
set_stb_idle();
digitalWrite(PIN_LAT, HIGH);
}
直观理解:
init_printing 就像打印机准备好,接通电源,等待数据;stop_printing 就是紧急断电,立刻停止加热。
3. 发送一行数据(通过SPI)
void send_one_line_data(uint8_t *data){
spi_transmit_data(data, 48); // 发送48字节(384位)
digitalWrite(PIN_LAT, LOW); // 锁存信号低电平
delayMicroseconds(LAT_TIME); // 保持低电平一段时间
digitalWrite(PIN_LAT, HIGH); // 恢复高电平,数据锁存完成
}
核心作用:
将一行图像数据(48字节)发送到打印头内部的移位寄存器,然后通过锁存信号让数据进入输出寄存器,准备加热。
4. 单个通道加热
const uint8_t stb_pins[] = {PIN_STB1, PIN_STB2, PIN_STB3, PIN_STB4, PIN_STB5, PIN_STB6};
static void run_stb(uint8_t now_stb_num) {
if (now_stb_num < 6) {
digitalWrite(stb_pins[now_stb_num], HIGH); // 选通当前通道
delayMicroseconds(PRINT_TIME); // 加热指定时间
digitalWrite(stb_pins[now_stb_num], LOW); // 关闭选通
delayMicroseconds(PRINT_END_TIME); // 冷却等待
}
}
核心作用:
对指定通道进行加热,加热时间由 PRINT_TIME 控制,加热后冷却一段时间,避免过热。
5. 电机走纸与加热同步
bool move_and_start_std(bool need_stop, uint8_t stbnum)
{
if (need_stop == true) // 数据已发完,需要停止
{
motor_stop(); // 停止电机
stop_printing(); // 关闭打印头
return true; // 返回true表示结束
}
motor_run_step(1); // 先走一步(进纸准备)
run_stb(stbnum); // 加热当前通道
motor_run_step(3); // 继续走三步(完成一行)
return false; // 返回false表示继续打印
}
核心作用:
将电机步进与加热动作结合,实现“走纸-加热-走纸”的同步。每打印一行,电机共走4步(1+3),使纸张前进一行高度。
注意:
此函数假设电机步距与行间距匹配,实际需根据机械结构调整。
6. 单通道打印整张图片
void start_printing_by_onestb(uint8_t stbnum, uint8_t *data, uint32_t len)
{
uint32_t offset = 0;
uint8_t *ptr = data;
bool need_stop = false;
init_printing(); // 初始化打印环境
while (1)
{
Serial.printf("printer %d\n", offset);
if (len > offset)
{
// 发送一行数据(48字节)
send_one_line_data(ptr);
offset += 48;
ptr += 48;
}
else
need_stop = true; // 所有数据已发完
// 走纸+加热,如果need_stop为true,则停止并退出循环
if (move_and_start_std(need_stop, stbnum))
break;
// 可选:检查是否出错(如温度过高),这里注释了
//if(printing_error_check(false))
// break;
}
motor_run_step(20); // 打印完成后多走一些纸,吐出纸张
motor_stop();
}
核心作用:
将传入的图像数据(每48字节为一行)逐行发送,每发送一行就调用 move_and_start_std 进行加热和走纸,直到数据全部发送完毕。
7. 测试数据生成与通道测试
static void setDebugData(uint8_t *print_data)
{
for (uint32_t index = 0; index < 48 * 5; ++index)
{
// 0x55 = 01010101,0为白,1为黑,形成黑白相间的条纹
print_data[index] = 0x55;
}
}
void testSTB()
{
uint8_t print_data[48*5];
uint32_t print_len;
Serial.println("开始打印打印头选通引脚测试\n顺序: 1 2 3 4 5 6");
print_len = 48*5;
// 依次对6个通道各打印5行
setDebugData(print_data);
start_printing_by_onestb(0, print_data, print_len);
setDebugData(print_data);
start_printing_by_onestb(1, print_data, print_len);
setDebugData(print_data);
start_printing_by_onestb(2, print_data, print_len);
setDebugData(print_data);
start_printing_by_onestb(3, print_data, print_len);
setDebugData(print_data);
start_printing_by_onestb(4, print_data, print_len);
setDebugData(print_data);
start_printing_by_onestb(5, print_data, print_len);
Serial.println("测试完成");
}
核心作用:
生成测试图案(0x55,黑白相间),分别用6个通道各打印5行,用于验证每个通道是否正常工作。
8. 初始化函数
void init_printer()
{
// 初始化电机
motor_init();
// 初始化打印头控制引脚
pinMode(PIN_LAT, OUTPUT);
pinMode(PIN_SCK, OUTPUT);
pinMode(PIN_SDA, OUTPUT);
pinMode(PIN_STB1, OUTPUT);
pinMode(PIN_STB2, OUTPUT);
pinMode(PIN_STB3, OUTPUT);
pinMode(PIN_STB4, OUTPUT);
pinMode(PIN_STB5, OUTPUT);
pinMode(PIN_STB6, OUTPUT);
// 关闭所有通道
set_stb_idle();
// 初始化加热电源控制引脚,并关闭电源
pinMode(PIN_VHEN, OUTPUT);
digitalWrite(PIN_VHEN, LOW);
// 初始化SPI
spi_init();
}
核心作用:
配置所有相关引脚为输出,关闭通道和电源,初始化SPI通信。
三、代码使用示例
void setup() {
Serial.begin(115200);
init_printer();
Serial.println("打印机模块初始化完成");
}
void loop() {
// 运行通道测试
testSTB();
delay(5000); // 测试间隔5秒
}
四、代码分析
1. 优点
- 模块化清晰:功能拆分明确(数据发送、通道加热、电机同步、主流程)
- 参数可调:加热时间、冷却时间、锁存时间均用宏定义,便于调试
- SPI高效:一次传输48字节,减少中断开销
- 测试友好:
testSTB可用于快速验证每个通道是否正常 - 同步走纸:电机步进与加热动作紧密耦合,确保打印行间距一致
2. 改进方向(供参考)
- 使用定时器中断实现非阻塞加热,释放CPU
- 加入温度传感器读取,超过阈值暂停打印或降低加热时间
- 增加打印进度回调,允许外部程序控制暂停/继续
- 动态调整加热时间,根据当前温度自适应
五、使用总结
| 关键词/技巧 | 一句话记忆 |
|---|---|
| SPI传输 | 48字节一行,高速发送数据 |
| 锁存信号 | LAT低脉冲,数据正式生效 |
| 通道选通 | STB1~STB6轮流加热,避免电流过大 |
| 加热时间 | PRINT_TIME越大,打印越黑,但发热越大 |
| 冷却时间 | 通道间休息,防止过热 |
| 电机同步 | 走纸与加热交错,形成完整行 |
| 测试模式 | 0x55条纹,检查所有点是否正常 |
六、写在最后
打印头控制模块是ESP32 Mini打印机的“核心输出单元”,通过SPI传输图像数据,配合选通信号和步进电机,将数字图像转化为热敏纸上的物理点阵。本文从硬件原理到代码实现,详细解析了每一个环节。