喵喵蓝牙热敏打印机(上)


基于嘉立创开源项目进行开发:点击直达

功能

1.能够模拟作业帮旗下喵喵机的通信协议,通过手机app喵喵机(V6.2.80)进行蓝牙连接打印
2.能够通过图片取模打印图片
3.打印机进行打印时,LED灯快闪提示用户。打印完LED灯1s闪一次,提示用户打印机进入待机状态。
4.能够进行缺纸检测,缺纸时会进行电机微动提示用户。
5.能够进行电量检测并将结果发送给app端,能进行打印头温度检测,过热时自动停止加热并闪灯+电机微动。
6.能够进行按键出纸,按下按键打印机能够自动出纸。


开发环境

VScode + PlatFromIO插件 + ESP32 Dev Module + Arduino 框架

简单说一下开发界面
在这里插入图片描述
注意:由于该打印机上电后,针头会一直加热,为防止针头烧坏,先在工程中加入以下代码初始化针头停止加热,最后在setup中调用。

//6个打印头
#define PIN_STB1 14  
#define PIN_STB2 27  
#define PIN_STB3 26  
#define PIN_STB4 25  
#define PIN_STB5 33  
#define PIN_STB6 32 
//打印头电源升压控制引脚
#define PIN_VHEN 17

void Pin_Stb_Low()
{
	pinMode(PIN_STB1, OUTPUT);
    pinMode(PIN_STB2, OUTPUT);
    pinMode(PIN_STB3, OUTPUT);
    pinMode(PIN_STB4, OUTPUT);
    pinMode(PIN_STB5, OUTPUT);
    pinMode(PIN_STB6, OUTPUT);
    digitalWrite(PIN_STB1, LOW);
    digitalWrite(PIN_STB2, LOW);
    digitalWrite(PIN_STB3, LOW);
    digitalWrite(PIN_STB4, LOW);
    digitalWrite(PIN_STB5, LOW);
    digitalWrite(PIN_STB6, LOW);
}

void Init_Print()
{
    Pin_Stb_Low();
     // 初始化打印电源控制引脚、并关闭电源
    pinMode(PIN_VHEN, OUTPUT);
    digitalWrite(PIN_VHEN, LOW);
}

一、按键控制LED灯

目标:通过按键控制LED灯四种状态的切换。
状态1:亮
状态2:灭
状态3:慢闪
状态4:快闪

1.按键、LED初始化

原理图:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
电路分析:LED灯为引脚18控制,引脚18输出低电平时亮。按键由引脚5控制,检测到引脚5为低电平时,此时按键按下。
LED和按键的初始化步骤:

// 按键和 LED 的引脚编号
#define LED 18
#define BUTTON 5
#define LED_ON() digitalWrite(LED, LOW)
#define LED_OFF() digitalWrite(LED, HIGH)
// 初始化 LED 和按键
void Led_Config(void)
{
    // 配置 IO 口模式
    pinMode(BUTTON, INPUT);
    pinMode(LED, OUTPUT);
    // 关灯
    LED_OFF();
}

1.定义引脚18为LED,引脚5为BUTTON
2.定义LED_ON()为digitalWrite(LED, LOW),代表此时灯亮,定义LED_OFF()为digitalWrite(LED, HIGH),代表此时灯灭,方便后续操作。
3.配置引脚模式,引脚5连接按键(BUTTON),为了能够检测该引脚的高低电平,将该引脚配置为输入模式。引脚18连接LED灯,为了能够输出高电平控制LED灯的亮灭,将该引脚配置为输出模式。
4.为了保证代码的严谨性,配置完LED后随即将其关掉。
注意:单独写文件时,记得包含#include "Arduino.h"头文件。

2.按键控制LED灯状态

先写一个LED灯的闪烁

//闪烁
void LED_quick(int a)
{
    LED_ON();
    vTaskDelay(a);
    LED_OFF();
    vTaskDelay(a);   
}

该函数通过改变a的值来改变LED灯的闪烁状态(快闪、慢闪)。
使用vTaskDelay主要是为了方便后续使用FreeRTOS整合该项目。

四种状态Switch转换:

/*
    状态0:灯灭
    状态1:灯亮
    状态2:待机慢烁
    状态3:打印快闪
*/
void Led_Status(int a)
{
    switch (a)
    {
    case 0:
        LED_OFF();
        break;
    case 1:
        LED_ON();
        break;
    case 2:
        LED_quick(1000);
        break;
    case 3:
        LED_quick(50);
        break;
    }
}

最后加上一个按键控制,逻辑整合一下就可以实现了。

#include <Arduino.h>
#include "LED.h"
int a;
void setup()
{
  Init_Print();
  Led_Config();
  int a=0;
}
void loop()
{
  if(digitalRead(BUTTON)==0){
    delay(10);
    if(digitalRead(BUTTON)==0){
      a++;
      a%=4;
    }
  }
  Led_Status(a);
}

二、步进电机控制

步进电机规格参数:
在这里插入图片描述
驱动方式:
在这里插入图片描述
驱动时序
在这里插入图片描述

原理图:
在这里插入图片描述
在这里插入图片描述
通过步进电机的说明手册,我们可以得知该步进电机的驱动方式。
其中,比较关键的就是该步进电机的驱动时序,只要按照驱动时序来控制引脚输出高地电平,我们就能驱动该电机。
该电机由TC1508S芯片作为ESP32和 步进电机之间的桥梁进行控制。
引脚输出对应如下:

ESP32TC1508S步进电机
IO23IN1M_A+(A)
IO22IN2M_A-(Ā)
IO21IN3M_B+(B)
IO19IN4M_B-(B̄)

1.电机初始化

由上可知,初始化电机时需配置IN1-IN4四个引脚为输出模式,然后将四个引脚配置为低电平。

// 定义步进电机引脚
#define PIN_MOTOR_AP    23        //M_A+   IN1
#define PIN_MOTOR_AM    22        //M_A-   IN2
#define PIN_MOTOR_BP    21        //M_B+   IN3
#define PIN_MOTOR_BM    19        //M_B-   IN4

void init_motor()
{
    pinMode(PIN_MOTOR_AP, OUTPUT);
    pinMode(PIN_MOTOR_AM, OUTPUT);
    pinMode(PIN_MOTOR_BP, OUTPUT);
    pinMode(PIN_MOTOR_BM, OUTPUT);

    digitalWrite(PIN_MOTOR_AP, 0);
    digitalWrite(PIN_MOTOR_AM, 0);
    digitalWrite(PIN_MOTOR_BP, 0);
    digitalWrite(PIN_MOTOR_BM, 0);
}

2.电机驱动

只需按照时序图控制四个引脚的高低电平输出即可:

//运行8步
void motor_run8(){
    digitalWrite(PIN_MOTOR_AM, 1);
    digitalWrite(PIN_MOTOR_BM, 0);
    digitalWrite(PIN_MOTOR_AP, 0);
    digitalWrite(PIN_MOTOR_BP, 1);
    delay(2); 
    digitalWrite(PIN_MOTOR_AM, 0);
    digitalWrite(PIN_MOTOR_BM, 0);
    digitalWrite(PIN_MOTOR_AP, 0);
    digitalWrite(PIN_MOTOR_BP, 1);
    delay(2);  
    digitalWrite(PIN_MOTOR_AM, 0);
    digitalWrite(PIN_MOTOR_BM, 0);
    digitalWrite(PIN_MOTOR_AP, 1);
    digitalWrite(PIN_MOTOR_BP, 1);
    delay(2);  
    digitalWrite(PIN_MOTOR_AM, 0);
    digitalWrite(PIN_MOTOR_BM, 0);
    digitalWrite(PIN_MOTOR_AP, 1);
    digitalWrite(PIN_MOTOR_BP, 0);
    delay(2);  
    digitalWrite(PIN_MOTOR_AM, 0);
    digitalWrite(PIN_MOTOR_BM, 1);
    digitalWrite(PIN_MOTOR_AP, 1);
    digitalWrite(PIN_MOTOR_BP, 0);
    delay(2); 
    digitalWrite(PIN_MOTOR_AM, 0);
    digitalWrite(PIN_MOTOR_BM, 1);
    digitalWrite(PIN_MOTOR_AP, 0);
    digitalWrite(PIN_MOTOR_BP, 0);
    delay(2); 
    digitalWrite(PIN_MOTOR_AM, 1);
    digitalWrite(PIN_MOTOR_BM, 1);
    digitalWrite(PIN_MOTOR_AP, 0);
    digitalWrite(PIN_MOTOR_BP, 0);
    delay(2);
    digitalWrite(PIN_MOTOR_AM, 1);
    digitalWrite(PIN_MOTOR_BM, 0);
    digitalWrite(PIN_MOTOR_AP, 0);
    digitalWrite(PIN_MOTOR_BP, 0);
    delay(2);  
}

以上是该电机转动一个周期(8步)的示例。
每步中间的延时决定了每个步进的时间间隔,从而影响电机的步进频率。
delay(2) 表示每个步骤之间的间隔是 2 毫秒。这相当于 500 Hz 的频率(因为 1 / 0.002 = 500)。通过调整这个延时,你可以控制电机的转速。

学明白了以上代码和原理我们就可以优化代码并写点花的。
将电机的时序放在数组里

 //步进电机节拍
uint8_t motor_table[8][4] = { //JX 系列
 {1, 0, 0, 0},
 {1, 0, 1, 0},
 {0, 0, 1, 0},
 {0, 1, 1, 0},
 {0, 1, 0, 0},
 {0, 1, 0, 1},
 {0, 0, 0, 1},
 {1, 0, 0, 1}
};

走一个周期

void motor_run()
{
    digitalWrite(PIN_MOTOR_AP, motor_table[motor_pos][0]);
    digitalWrite(PIN_MOTOR_AM, motor_table[motor_pos][1]);
    digitalWrite(PIN_MOTOR_BP, motor_table[motor_pos][2]);
    digitalWrite(PIN_MOTOR_BM, motor_table[motor_pos][3]);
    motor_pos++;
    if (motor_pos >= 8)
    {
        motor_pos = 0;
    }
}

按指定步数走

//指定步数运行
void motor_run_step(uint32_t steps)
{
    while (steps)
    {
        digitalWrite(PIN_MOTOR_AP, motor_table[motor_pos][0]);
        digitalWrite(PIN_MOTOR_AM, motor_table[motor_pos][1]);
        digitalWrite(PIN_MOTOR_BP, motor_table[motor_pos][2]);
        digitalWrite(PIN_MOTOR_BM, motor_table[motor_pos][3]);
        motor_pos++;
        if (motor_pos >= 8)
        {
            motor_pos = 0;
        }
        delay(2);
        steps--;
    }
}

3.按键走纸

按键走纸实际上就是按下按键,步进电机开始转动,如下所示:

//按键走纸
void ButtonRun(){
  if(digitalRead(BUTTON)==0){
    delay(10);
    if(digitalRead(BUTTON)==0){
     motor_run_step(4);
    }
  }
}

三、图片取模打印图片

打印头原理
在这里插入图片描述
在这里插入图片描述
时序:
在这里插入图片描述
在这里插入图片描述

1.打印头初始化

由上图可知,该打印模块是由6个加热针头控制的。
每个针头控制8位,相当于每行传输48个数据,共384位。
数据的获取是靠SPI传输的,PIN_SCK(CLK )作为SPI 时钟线,PIN_SDA(DI)作为数据传输。因为是打印且仅有一个打印模块所以主机输入和片选线均无。

打印流程
1.VH电源置为高电平。
2.SPI传输需要打印的数据。
3.LAT从低拉高1us锁存数据。
4.STB1~6开始按照数据逐个开启加热,加热时间越长,打印颜色越深。
5.加热完成后,就相当于打印了 一行,此时控制电机移动4步(该硬件设置的是移动4步相当于打印行高)。
6. 之后就是重复上面的打印步骤,重新开始下一行的SPI数据传输,锁存器锁存打印,直到所有数据打印完毕。

初始化spi

#include <myspi.h>
#include <spi.h>
#define PIN_SCK 15
#define PIN_SDA 13

SPIClass printerSPI = SPIClass(HSPI)
SPISettings print_SPI_Set = SPISettings(1000000, SPI_MSBFIRST, SPI_MODE0);
void init_spi()
{
    myspi.begin(PIN_SCK, -1, PIN_SDA, -1);
    myspi.setFrequency(2000000);
}

1.选总线,此处配置的为HSPI总线。
在 ESP32 上,HSPI 和 VSPI 是两个硬件 SPI 总线接口。每个总线接口都有不同的默认引脚:
HSPI(High-Speed SPI):通常使用 GPIO 14 (SCK), GPIO 12 (MISO), GPIO 13 (MOSI), 和 GPIO 15 (SS)。
VSPI(Very-Speed SPI):通常使用 GPIO 18 (SCK), GPIO 19 (MISO), GPIO 23 (MOSI), 和 GPIO 5 (SS)。
2.配SPI
创建一个 SPISettings 对象 print_SPI_Set,用于设置 SPI 配置:
1000000:SPI 的时钟频率设置为 1 MHz(1,000,000 Hz)。
SPI_MSBFIRST:指定数据传输时的字节顺序为最高位优先(MSB First)。
SPI_MODE0:指定 SPI 模式为 0(CPOL = 0, CPHA = 0),表示时钟极性和相位都为 0。
2.初始化参数
mySPI.begin() 用于初始化 SPI 总线。 SPI 时钟引脚 (SCK)、SPI 从输入引脚 (MISO)、SPI 主输出引脚 (MOSI)、和 SPI 片选引脚 (SS)。
PIN_SCK 和 PIN_SDA 是 SCK 和 MOSI 指定的引脚。-1 表示使用默认引脚,不指定 MISO 和 SS 引脚的情况。
setFrequency() 用于设置 SPI 总线的频率。这里设置为 2 MHz。
频率决定了数据传输的速度。2MHz 意味着 SPI 总线每秒可以传输 2,000,000 个时钟周期的数据。

最终初始化函数:

void init_printer()
{
    // 初始化电机IO
    init_motor();
    // 初始化数据引脚、通道引脚
    pinMode(PIN_LAT, OUTPUT);
    pinMode(PIN_SCK, OUTPUT);
    pinMode(PIN_SDA, OUTPUT);
    // 加热通道全部关闭
	Init_Print();
    // 初始化打印电源控制引脚、并关闭电源
    pinMode(PIN_VHEN, OUTPUT);
    digitalWrite(PIN_VHEN, LOW);
    // 初始化SPI
    init_spi();
}

2.图片取模

在这里插入图片描述
 该蓝牙打印机只能支持黑白两种颜色,所以我们需要先准备一个单色的图片,可以借助电脑上的取模软件PCtoLCD2002实现。

先找到想要打印的图片,用电脑画图软件打开,属性修改如下所示。
在这里插入图片描述
最后另存为BMP文件,保存好之后,通过取模软件打开,需要先选择取模软件的模式为图形模式,然后打开自己的文件。
在这里插入图片描述
文件正常打开之后还需要在配置中,对取模方式进行是的那个修改,点击齿轮图标进入设置模式,然后按照下图红框所示内容进行修改,修改完成之后点击保存。
在这里插入图片描述
最后将取模后的数组单独保存在文件中,总共是9600个数组。
在这里插入图片描述
在这里插入图片描述

3.图片打印

主要函数

void Print_Img()
{
    uint8_t print_data[48];
    uint32_t print_len = 48;
    for (uint8_t i = 0; i < 200; i++)
    {
        set_img_Data(print_data, i + 1);
        // 发数据
        Send_Run(print_data, print_len);//
        motor_run_step(4);
    }
   printf("图片打印完成!");
}

逻辑:每行打印48个数据,所以每次传输48个数据,总共9600个数据,所以循环200次。每次打印完一行后,步进电机跑4步,相当于一行。循环往复最终就能将照片打印出来了。

其中包含的其他函数:

#define PRINT_TIME 2000        //打印加热时间
#define PRINT_END_TIME 10      //冷却时间
#define LAT_TIME 1              //数据锁存时间
void start_send_data(uint8_t *data, uint32_t len)
{
    uint32_t offset = 0;
    uint8_t *ptr = data;
    while (1)
    {
        if (len > offset)
        {
            // 发送一行数据 48byte*8=384bit
            send_one_line_data(ptr);
            // 每次偏移48
            offset += 48;
            ptr += 48;
        }
        //下面就是根据上面传输的48位数据进行加热针头,一个一个加热然后冷却
       run_stb(0);
       run_stb(1);
       run_stb(2);
       run_stb(3);
       run_stb(4);
       run_stb(5);
        break;
    }
}
static void set_img_Data(uint8_t *print_data, uint8_t a)//分割图片取模代码,48一组
{
    for (uint32_t index = 0; index < 48; ++index)
    {
        print_data[index] = img_data[index + 48 * a];
    }
}
static void send_one_line_data(uint8_t *data)//发送第一行代码
{
    spiCommand(data, 48);
    digitalWrite(PIN_LAT, LOW);//这个地方是数据锁存拉低锁存,拉高清除
    delayMicroseconds(LAT_TIME);
    digitalWrite(PIN_LAT, HIGH);
}

void spiCommand(uint8_t *data_buffer, uint8_t data_len)//SPI发送数据
{
    myspi.beginTransaction(print_SPI_Set);
    myspi.transfer(data_buffer, data_len);
    myspi.endTransaction();
}

void run_stb(uint8_t stb_num)//单个引脚加热,冷却
{
    switch (stb_num)
    {
    case 0:
        digitalWrite(PIN_STB1, 1);
        delayMicroseconds(5500);//这个地方单独设置5500是因为我的针头1被我玩坏了,所以需要加热时间长一点,正常的话填PRINT_TIME 加热2000us即可
        digitalWrite(PIN_STB1, 0);
        delayMicroseconds(PRINT_END_TIME);
        break;
    case 1:
        digitalWrite(PIN_STB2, 1);
        delayMicroseconds(PRINT_TIME);
        digitalWrite(PIN_STB2, 0);
        delayMicroseconds(PRINT_END_TIME);
        break;
    case 2:
        digitalWrite(PIN_STB3, 1);
        delayMicroseconds(PRINT_TIME);
        digitalWrite(PIN_STB3, 0);
        delayMicroseconds(PRINT_END_TIME);
        break;
    case 3:
        digitalWrite(PIN_STB4, 1);
        delayMicroseconds(PRINT_TIME);
        digitalWrite(PIN_STB4, 0);
        delayMicroseconds(PRINT_END_TIME);
        break;
    case 4:
        digitalWrite(PIN_STB5, 1);
        delayMicroseconds(PRINT_TIME);
        digitalWrite(PIN_STB5, 0);
        delayMicroseconds(PRINT_END_TIME);
        break;
    case 5:
        digitalWrite(PIN_STB6, 1);
        delayMicroseconds(PRINT_TIME);
        digitalWrite(PIN_STB6, 0);
        delayMicroseconds(PRINT_END_TIME);
        break;
    }
}

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值