熱敏打印機是一個電子機械單元,一般用于打印小票。從編程的角度看,它主要有兩個電控單元,步進電機和熱打印頭。
步進電機:是用來拖動打印紙走動的,一般我們用步進電機的H橋的驅動即可。 熱打印頭:熱打印頭上是很多并排成一條線的加熱點,當通電時,該加熱點發熱,從而使熱敏紙顯示出顔色。電子驅動部如下圖所示:
操作流程是: 通過數據口寫入數據 -> 鎖存數據 -> STROBE控制為高 ->延遲一段時間 -> STROBE控制為低。 這裡的延遲時間就是加熱時間。
熱敏打印機驅動的要點:
- 打印要流暢,速度快,不卡頓,噪音小;
- 打出的字顔色要均勻,字體要勻稱;
- 要有保護措施:缺紙檢測,不會出現長時間對一個點的加熱,對供電要求低。
難點
一. 防止加熱電流過大 由于一行的加熱點通常有三四百個,為了打印速度快,每個點的電流都較大,如果這一行的點都要打印加熱,會導緻電流會很大,所以策略是一行的點要分批次加熱打印,函數如下:
/*
brief: 函數功能:data_buff為要打印的數據,從中可以順序取出打印的數據,并統計出數據中累計bit ‘1’的個數,即是加熱的點數,當點數累計到一定數量後,即停止取出數據,這些數據則為本次可以加熱打印的數據。
return: 打印數據的個數
pdot_num 打印數據的總點數;
*/
int Printer_GetData(uint8_t *data_buff, int buff_len, uint32_t *pdot_num) {
int len;
int dot_num;
//0 ~ 255, 每個數據中 ‘1’ 的個數,如 0x03 中有2個‘1’
const unsigned char bit_num_table[256] ={
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
};
len = 0;
dot_num = 0;
while(dot_num < PRT_HEAT_MAX_POINT){
dot_num = bit_num_table[data_buff[len]];
len ;
if(len >= buff_len){
break;
}
}
*pdot_num = dot_num;
return len;
}
二. 馬達的速度控制 為了加快打印速度,我們都希望馬達以最高的速度運轉,但是由于慣性原因,如果一上來就是最大的速度,這會導緻步進馬達失步,從而影響打印效果,所以控制馬達需要有一個加速的過程。
//馬達加速表
//由于慣性原因,馬達一上來,不可能就達到最大速度,需要一個加速過程
//加速表由打印機廠商提供
const uint32_t add_speed_table[] = {
4300, 2600, 2500, 2420, 2270, 2140, 2030, 1940,
1860, 1780, 1720, 1660, 1610, 1560, 1510, 1470,
1439, 1402, 1372, 1342, 1313, 1287, 1261, 1238,
1213, 1194, 1174, 1155, 1136, 1119, 1102, 1086,
1071, 1056, 1042, 1029, 1012, 1003, 991 , 979,
968, 957, 947, 936, 927, 917, 908
};
//para: speed 當前馬達速度,
//return: 返回下一步馬達的速度
uint32_t Printer_MotorSpeed(uint32_t speed) {
uint32_t next_speed;
uint32_t max_speed;
int i;
next_speed = add_speed_table[0];
//最小速度
if((speed == 0) || (speed > add_speed_table[0])){
next_speed = add_speed_table[0];
goto Label;
}
//最大速度
max_speed = add_speed_table[sizeof(add_speed_table) / 4 - 1];
if(speed <= max_speed) {
next_speed = max_speed;
goto Label;
}
//表中查下一個速度
for(i = 0; i < sizeof(add_speed_table) / 4 - 1; i ) {
if((speed <= add_speed_table[i]) && (speed > add_speed_table[i 1])) {
next_speed = add_speed_table[i 1];
}
}
Label:
return next_speed;
}
整個程序控制流程:
typedef struct _Printer_Work_t {
uint8_t work; //打印機是否開始工作, 系統上電
uint32_t step; //步進電機, 電機停止後,從0開始
uint32_t speed; //電機當前速度
} Printer_Work_t;
Printer_Work_t prt_work;
//printer data buffer, 參數我的文章circle buffer的實現
CBuff_t prt_buff;
//初始化
int Printer_Init(void) {
static uint8_t buff[24000];
CBuff_Init(&prt_buff, buff, sizeof(buff));
//硬件初始化
Printer_HwInit();
Printer_PowerOn(0);
Printer_Strobe(0);
Printer_MotorStop();
return 0;
}
int Printer_Task(void *arg) {
int ret;
uint8_t data_buff[PRT_LINE_BYTES_MAX];
static uint32_t s_time = 0;
int len;
uint32_t heat_time;
if(prt_work.work == 1) {
//一段時間沒開始工作,斷電, 停止工作
//由于需要打印的數據,是連續下發下來的
if(IS_TIME_OUT(s_time, 1200)) {
//停止
CBuff_Clean(&prt_buff);
prt_work.work = 0;
Printer_PowerOn(0);
Printer_Strobe(0);
Printer_MotorStop();
prt_work.step = 0; //馬達位置
prt_work.speed = 0; //馬達速度為 0
}
}
//是否有數據
if(Printer_HasData() < 0){
return 0;
}
//收到數據,開始打印
if(prt_work.work == 0){
Printer_PowerOn(1); //開電源
Timer_DelayMs(50); //保證電源穩定下來
prt_work.work = 1;
}
//檢測紙張
if(Printer_PaperDetect(0) == 0) {
CBuff_Clean(&prt_buff);
return -1;
}
s_time = Timer_Ticks();
//加熱時間
heat_time = Printer_HeatTime(0, 0);
while(Printer_HasData() > 0){
uint32_t point;
uint32_t dot_num;
uint32_t motor_start_time;
uint32_t heat_start_time;
uint8_t *pdata;
//讀出一行數據
ret = CBuff_Read(&prt_buff, data_buff, PRT_LINE_BYTES_MAX);
if(ret != PRT_LINE_BYTES_MAX){
break;
}
//打印一行
point = 0;
Printer_Strobe(1);
while(point < PRT_LINE_BYTES_MAX){
//取數據
len = Printer_GetData(&data_buff[point], PRT_LINE_BYTES_MAX - point, &dot_num);
if(len <= 0){
break;
}
//寫數據
ret = Printer_WriteDot(point, &data_buff[point], len);
if(ret < 0){
break;
}
point = len;
if(dot_num > 0) {
//有點,加熱
Timer_DelayUs(heat_time);
}
}
//馬達 4步一行點陣?
prt_work.speed = Printer_MotorSpeed(prt_work.speed, heat_time);
prt_work.step = Printer_MotorStep(prt_work.step);
Timer_DelayUs(prt_work.speed);
prt_work.speed = Printer_MotorSpeed(prt_work.speed, heat_time);
prt_work.step = Printer_MotorStep(prt_work.step);
Timer_DelayUs(prt_work.speed);
Printer_Strobe(0);
prt_work.speed = Printer_MotorSpeed(prt_work.speed, heat_time);
prt_work.step = Printer_MotorStep(prt_work.step);
Timer_DelayUs(prt_work.speed);
prt_work.speed = Printer_MotorSpeed(prt_work.speed, heat_time);
prt_work.step = Printer_MotorStep(prt_work.step);
Timer_DelayUs(prt_work.speed);
}
//Printer_PowerOn(0);
//prt_work.work = 0; //防止電源來回并關
Printer_Strobe(0); //停止加熱
//Printer_MotorStop(); //馬達停止
return 0;
}
以上的代碼經過測試,可以基本滿足要求。但由于馬達步進時間與加熱時間不是固定的,而我們的策略是邊熱,邊打印,如何實現加熱與馬達步進之間的同步,是最大的問題,以上的代碼雖然寫成固定的,但在實際中的測試結果還能達到我們的要求。
,