1、前言
本文章主要讲述热敏打印机的基本工作原理,涉及到更流畅的逻辑功能本文不展示,但可以简单的教明白你热敏打印机的工作流程,完成类似毕业设计,学习科目的小项目算是卓卓有余;写这篇文章主要原因也是因为网上关于热敏打印机的文章,少之又少,工作中如果项目涉及到,又没有开发经验的话,希望这篇文章可以帮助到你。
2、热敏打印机的优势
3、打印机特性(摘重点)
1、有效打印宽度(毫米):48
2、点密度(点/毫米):8
3、打印点数:384 点/行(市面上还有576 点/行,工作原理一样,只是加热脚多了)
4、打印机工作电压:3.13~8.5V(重要,会影响打印浓度和走纸马达),开机上电,同时给马达和加热,瞬间电流可能有3A以上,需要注意!!!
5、支撑板最大温度:65°C
4、打印机——步进电机
步进电机是一种将电脉冲信号转换成相应角位移或相位移的电动机。每输入一个脉冲信号,转子就转动一个角度或前进一步,其输出的角位移或相位移与输入的脉冲数成正比,转速与脉冲频率成正比。因此,步进电动机又称脉冲电动机。
4.1、驱动方式
步进电机相信很多人在开发项目中都有使用过,热敏打印机的步进电机一般采用“1-2相”或“2-2相”的驱动方式。
4.2、步进电机驱动芯片
打印机的步进电机需要步进电机驱动芯片来驱动马达,我使用的驱动芯片是HR8833,比较常见的一款驱动芯片;HR8833驱动芯片有个特性,芯片需要一个限流电阻(重要!),电阻越大,电流越小,反之,越大;电流越大,马达的走纸的声音就越大,发热也越快;我们要控制好走纸马达的噪声和发热,就绪要我们去将电阻调到一个合理的数值,但是不能太小,电阻太大,电流太小,走纸力度就越小;驱动芯片的限流模式是,加大电阻之后,想达到限流的效果,它是通过PWM的方式实现的,就是电阻越大,PWM的占空比就越小,反之,越大;
波形图:
4.3、时序图
1-2相:半步
2-2相:全步
4.4、使能驱动
只要主控给出驱动芯片需要的高低电平,驱动芯片就会输出相应的电平给到打印机机芯,就可以转动马达走纸。
4.5、走纸幅度
打印机走一点行需要走的相位。
1-2相的驱动方式走8个相位马达才会转动一圈,但走一点行只需走4个相位。
2-2相的驱动方式走4个相位马达才会转动一圈,但走一点行只需走2个相位。
4.6、驱动程序
1-2相
void TestMotor(int i)
{
while(i--)
{
PRINTER_MOTOR_A1_ON; //1
PRINTER_MOTOR_A2_ON;
PRINTER_MOTOR_B1_ON;
PRINTER_MOTOR_B2_OFF;
udelay(400);
PRINTER_MOTOR_A1_ON; //2
PRINTER_MOTOR_A2_OFF;
PRINTER_MOTOR_B1_ON;
PRINTER_MOTOR_B2_ON;
udelay(400);
PRINTER_MOTOR_A1_ON; //3
PRINTER_MOTOR_A2_OFF;
PRINTER_MOTOR_B1_ON;
PRINTER_MOTOR_B2_ON;
udelay(400);
PRINTER_MOTOR_A1_ON; //4
PRINTER_MOTOR_A2_OFF;
PRINTER_MOTOR_B1_OFF;
PRINTER_MOTOR_B2_ON;
udelay(400);
PRINTER_MOTOR_A1_ON; //5
PRINTER_MOTOR_A2_ON;
PRINTER_MOTOR_B1_OFF;
PRINTER_MOTOR_B2_ON;
udelay(400);
PRINTER_MOTOR_A1_OFF; //6
PRINTER_MOTOR_A2_ON;
PRINTER_MOTOR_B1_OFF;
PRINTER_MOTOR_B2_ON;
udelay(400);
PRINTER_MOTOR_A1_OFF; //7
PRINTER_MOTOR_A2_ON;
PRINTER_MOTOR_B1_ON;
PRINTER_MOTOR_B2_ON;
udelay(400);
PRINTER_MOTOR_A1_OFF; //8
PRINTER_MOTOR_A2_ON;
PRINTER_MOTOR_B1_ON;
PRINTER_MOTOR_B2_OFF;
udelay(400);
}
}
2-2相
void FirstFeed(int i)
{
while(i--)
{
PRINTER_MOTOR_A1_ON; //1
PRINTER_MOTOR_A2_OFF;
PRINTER_MOTOR_B1_ON;
PRINTER_MOTOR_B2_OFF;
udelay(1000);
PRINTER_MOTOR_A1_ON; //2
PRINTER_MOTOR_A2_OFF;
PRINTER_MOTOR_B1_OFF;
PRINTER_MOTOR_B2_ON;
udelay(1000);
PRINTER_MOTOR_A1_OFF; //3
PRINTER_MOTOR_A2_ON;
PRINTER_MOTOR_B1_OFF;
PRINTER_MOTOR_B2_ON;
udelay(1000);
PRINTER_MOTOR_A1_OFF; //4
PRINTER_MOTOR_A2_ON;
PRINTER_MOTOR_B1_ON;
PRINTER_MOTOR_B2_OFF;
udelay(1000);
}
}
5、打印机——通信
5.1、SPI介绍
打印机采用SPI通信,相信大家对SPI通信再熟悉不过了;SPI接口共有4根信号线,分别是:设备选择线、时钟线、串行输出数据线、串行输入数据线。
(1)MOSI:主器件数据输出,从器件数据输入
(2)MISO:主器件数据输入,从器件数据输出
(3)SCLK:时钟信号,由主器件产生
(4)/SS:从器件使能信号,由主器件控制
5.2、优缺点
优点:
1) 支持全双工操作;
2) 操作简单;
3) 数据传输速率较高。
缺点:
1) 需要占用主机较多的口线(每个从机都需要一根片选线);
2) 只支持单个主机。
3) 没有指定的流控制,没有应答机制确认是否接收到数据。
5.3、SPI通信的四种模式
SPI的四种模式,简单地讲就是设置SCLK时钟信号线的那种信号为有效信号
SPI通信有4种不同的操作模式,不同的从设备可能在出厂是就是配置为某种模式,这是不能改变的;但我们的通信双方必须是工作在同一模式下,所以我们可以对我们的主设备的SPI模式进行配置,通过CPOL(时钟极性)和CPHA(时钟相位)来
控制我们主设备的通信模式,具体如下:
时钟极性(CPOL)定义了时钟空闲状态电平:
CPOL=0,表示当SCLK=0时处于空闲态,所以有效状态就是SCLK处于高电平时
CPOL=1,表示当SCLK=1时处于空闲态,所以有效状态就是SCLK处于低电平时
时钟相位(CPHA)定义数据的采集时间。
CPHA=0,在时钟的第一个跳变沿(上升沿或下降沿)进行数据采样。,在第2个边沿发送数据
CPHA=1,在时钟的第二个跳变沿(上升沿或下降沿)进行数据采样。,在第1个边沿发送数据
例:
Mode0:CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据采样是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在上升沿(准备数据),(发送数据)数据发送是在下降沿。
Mode1:CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据发送是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。
Mode2:CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采集是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。
Mode3:CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。
5.4、时序图分析
CLK低电平时空闲,CLK下降沿采集数据。
5.5、程序
程序就不放了,每个芯片的SPI的通信都会有点差别,不过都是大同小异,具体用什么芯片就找对应芯片的参考案列即可。打印机只负责接收,不负责发送,你只需要把你需要打印的数据发送过去就好了。
6、打印机——数据锁存
LATCH:锁存脚,数据发送到打印机,我们需要将数据锁住,然后再进行加热。
空闲的时候,我们把锁存脚置高,当你发送完数据之后,就把锁存脚拉低,延时1us之后,拉高即刻锁存数据;延时的时间其实也不需要1us,最短时间规格书有明确写,如下:
延时时间100ns即可,但是延时长一点也无所谓的。
7、打印机——加热
加热其实就是一个管脚给出高低电平即可,加热把GPIO拉高,加一定的延时,一般不要太长,不过需要看你的电压,电压越高,加热时间尽量越短,加热完把GPIO管脚拉低;加热的浓度需要看你的需要,加热太长时间,会出现拖影,打印效果不佳!(特别注意加热过度,会烧坏加热值和加热板,延时时间一定要控制好)
8、整体思路流程
while(1)
{
1、发送数据
2、锁存数据
3、加热数据
4、转动走纸马达,走一点行
}
9、附简单测试代码
9.1、主程序
while(1)
{
if(!PRINTER_GPIO_KEY_READ) //按按键出发
{
mdelay(50);
if(!PRINTER_GPIO_KEY_READ)
{
PRINTER_POWER_ON; //打开打印机电源
mdelay(1000); //适当延时
TestPrinterGoOn();
PRINTER_POWER_OFF; //关闭打印机电源
}
}
}
9.2、打印测试函数
u8 const MOTOR_CTRL_BYTE_TABLE_2_2[5][4] =
{
{1,0,1,0},
{1,0,0,1},
{0,1,0,1},
{0,1,1,0},
{0,0,0,0},
};
#define PRN_MOT_OUT_2_2(x) do{if(MOTOR_CTRL_BYTE_TABLE_2_2[x][0])GPIO_SetBits(GPIOC,
GPIO_Pin_6);else GPIO_ResetBits(GPIOC, GPIO_Pin_6);\
if(MOTOR_CTRL_BYTE_TABLE_2_2[x][1])GPIO_SetBits(GPIOC, GPIO_Pin_7);else
GPIO_ResetBits(GPIOC, GPIO_Pin_7);\
if(MOTOR_CTRL_BYTE_TABLE_2_2[x][2])GPIO_SetBits(GPIOC, GPIO_Pin_8);else
GPIO_ResetBits(GPIOC, GPIO_Pin_8);\
if(MOTOR_CTRL_BYTE_TABLE_2_2[x][3])GPIO_SetBits(GPIOC, GPIO_Pin_9);else
GPIO_ResetBits(GPIOC, GPIO_Pin_9);\}while(0) //转动马达 直接写寄存器
static const char *mystr[]={
" VERY GOOD BOY ",
"VB B B B B B B B B B B B B B B B B B B B B B B ",
"M C C C C C C C C C C C C C C C C C C C C C C C",
"U D D D D D D D D D D D D D D D D D D D D D D D",
"TE E E E E E E E E E E E E E E E E E E E E E E ",
"GFF FF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"MGG GGGGG GGGGGG XGGGGG ZGGGGG YGGGGG WGGG GGGG",
};
static int extract_32X32(uint8_t *str,uint8_t *otmep,uint8_t *outbuf,int *olen)
{
uint8_t *ptr = str;
uint8_t *cp = str;
uint8_t ch;
int k = 0;
for (int j = 0; j < PRINTER_LINE_LEN; j++)
{ //一行有多少个字符
ptr = str + k; //一行中的第几个字
if (*ptr < 0x80) { //英文
if (*ptr < 0x21)
{
j++; //一个字节占两个字节
k++;
//continue;
}
else
{
cp = (uint8_t *)&ASCII16x32[((*ptr) - 0x21) * 64];
memcpy(otmep, cp,64);
for(int i = 0; i < 32; i++)
{
memcpy(&outbuf[i*PRINTER_LINE_LEN+ j],&otmep[i * 2], 2);
}
j++;
k++;
}
}
}
}
void TestPrinterGoOn(void)
{
static int pos = 0; //解析字符数据行数
int i = 0;
int nlines = 32;
u32 g_motor_phase = 0;
uint8_t *bux = malloc(48 * 48);
uint8_t *tmx = malloc(48 * 48);
int strlen = sizeof(mystr)/sizeof(mystr[0]);
memset(bux,0,48 * 48);
memset(tmx,0,48 * 48);
FirstFeed(20);
while(pos < 7)
{
//mystr数组有七行,将每一行字符分解为32行的数据放到bux里面。
extract_32X32((uint8_t *)mystr[pos%strlen],tmx,bux,NULL);
mdelay(10);
while(nlines)
{
PRN_MOT_OUT_2_2(g_motor_phase);
udelay(400); //换一次相需要停一下,太快
g_motor_phase = (g_motor_phase + 1) & 0x03;
if ((g_motor_phase % 2) == 0) //走两个相就发一次数据
{
prtdrv_hspi_write(bux + i,48); //一行发送48个数据
PRN_LATCH_PLUS;
PRINTER_STB_ON;
udelay(1500);
PRINTER_STB_OFF;
i += 48;
nlines--; //发送一次减一行,总共32行
}
}
TestMotor(5);
pos++;
nlines = 32;
i = 0;
}
TestMotor(100);
pos = 0;
printf("print end\r\n");
}
9.3、
提取码:i038
10、总结
热敏打印机其实玩起来也是蛮有意思的,有兴趣的可以自己买一个开发玩玩,也不是很难,市面很多设备都有应用到打印机,最多的就是付款的时候用到,都是用这种热敏打印机;做毕业的话用热敏打印机杠杠的完爆身边同学的流水灯。