单片机 并口 控制爱普生打印机开发流程记录

最近做一个项目,需求是通过昆仑通态的组态触摸屏作为上位机监测数据,然后将数据整合最后打印报表输出。昆仑通态以前可以直接将预览页的所有数据都打印出来,包括整屏未显示完全的。但是现在昆仑通态将WIN平台换为Linux了(据说是因为版权授权问题),于是就没有这个功能了。因为需求量不够,也不能获得厂家的直接支持,于是只能想办法通过自己下位机去驱动打印机,直接完成数据的打印输出。
市面上目前主要是惠普与爱普生打印机用得比较多,都有USB与并口两种接口,因为通过USB控制需要单独协议,对电路的外围设计与程序编写都麻烦许多,于是打算用并口。其实并口好像是专门为打印机而生的,爱普生的针式打印机还保留并口。
最后选用了爱普生的LQ-630II,查询资料,其使用的是ESC语言。

问题1:单片机如何与打印机通过并口连接起来?
答:打印机的并口线分为两端,一端连接打印机36针,一端连接PC或者单片机等控制端25针,这里作为控制端操作我们就只关心25针接口。

单片机 并口 控制爱普生打印机开发流程记录_第1张图片
25针控制端接线说明如下:
单片机 并口 控制爱普生打印机开发流程记录_第2张图片
PCB电路设计接线说明:
单片机 并口 控制爱普生打印机开发流程记录_第3张图片
问题一总结:
1.25针接线并没有完全用到,
引脚18~25:8根引脚都是GND。
引脚1:STB,作为数据选通,也就是当STB从高电平变化为低电平时,数据引脚的数据才会被打印机读取,也就是告诉打印机数据已经准备完成,可以读取。
引脚14:FEED,自动进纸与否,控制端控制打印机状态。
引脚15:ERROR,打印机出错与否,控制端读取打印机状态。
引脚16:INIT,硬件上重启打印机,大于50us的低电平就将重启打印机,回到初始状态,比如清除打印机缓存区。
引脚11:BUSY,打印机忙标志位,高电平表示打印机正在处理数据,不能接收新的数据,直到变为低电平为止。
引脚10:ACK,我暂时没用到。
引脚2~9:共8位作为并口数据传输位,也就是一次传输8位数据(1byte)。
注意:
1.也就是说其实我们只需要 STB、BUSY、DATA 几组引脚就可以控制打印机了。
2.各个引脚上拉电阻不能少!

问题2:控制端的数据是如何到达打印机的?
答:
1.并口8根数据线赋值需要发送的数据,比如0x80。
2.判断BUSY引脚状态,是否空闲,若忙则等待。
3.空闲状态时,STB从低电平到高电平脉冲,告诉 打印机可以读取数据了。
4.打印机获取数据存于打印缓冲区,遇到换行或者回车数据,或者缓存区满则自动打印数据。

问题3:一个8位数据如何发送给打印机?
答:
1.其实就是前面问题2的解读过程,具体代码我在测试阶段使用了死循环while来等待BUSY空闲,这样保证数据不丢失,每个字节都要等待打印机接收到。

void printByte(unsigned char ch)
{
	while(P_BUSY);

	P_DATA = ch;
	printDelay(500);
	P_STB = 0;
	printDelay(1000);//延时保证 STB > 1um
	P_STB = 1;
	printDelay(1000);
}

2.其实很明显,上面的死循环在操作系统使用多线程还可以,如果是纯单片机那就是完全不能用的,所以后来改进了一下,采用轮训方式,发送成功返回1下次发送下一个数据,失败返回0并且下次继续发送该数据。

unsigned char printByte(unsigned char ch)
{
	if(0 == P_BUSY)
	{
		P_DATA = ch;
		printDelay(500);
		P_STB = 0;
		printDelay(1000);//延时保证 STB > 1um
		P_STB = 1;
		printDelay(1000);
		
		return 1;
	}
	return 0;
}

问题4:一次发送多个数据是如何操作的?需要注意些什么?
答:
1.首先是前面提到的数据到达打印机过程,也就是先解决了发送一个字节数据的问题(见问题3过程)。
2.其实多个字节发送的实质也是一个个字节发送的,第一个字节发送完成,判断忙状态,空闲则继续发送下一个字节。
3.为了保证每一个数据都到达打印机,不存在控制端发送了,不管打印机是否正确接收。我想的方法是:用一个本地BUF将我这次需要发送的N个字节数据一次存入其中,给一个从0开始的BUF下标计数值,采用轮训去发送该BUF,每次发送成功则下标加1,即发送下一个数据,否则下标保持不变,继续发送该数据。

#define _PRINT_BUF_NUM_ 250

unsigned int Print_Sign = 0;
unsigned char  Print_Buf[_PRINT_BUF_NUM_]; //

//将指定一字节数据存入Print_Buf中,其存入数据个数与下标对应
void PrintDataToBuf(unsigned int *dataAddr, unsigned char byte)
{
	if((*dataAddr) < _PRINT_BUF_NUM_)
		Print_Buf[(*dataAddr)++] = byte;
}
//将指定字符串存入Print_Buf中,注意不能包含0x00,字符串会自动切断
void PrintDataToBufs(unsigned int *dataAddr, unsigned char mydata[])
{
	unsigned int i = 0;
	
	for(i = 0;i < strlen(mydata);i++)
	{
		if((*dataAddr) < _PRINT_BUF_NUM_)
		{
			Print_Buf[(*dataAddr)++] = mydata[i];
		}
	}
}

问题5:这两个函数怎么使用的?
答:
1.主函数中对命令等数据操作都是一个一个存入BUF中的,而对于字符串则使用第二个函数一次存入。

	#define _GJJLBB_ "打印测试内容\r\n"
	
	Print_Sign = 0;//每次存入前复位下标,表示从0开始
	//设置左边界
  	PrintDataToBuf(&Print_Sign, 0x1B);
	PrintDataToBuf(&Print_Sign, 0x6C);
	PrintDataToBuf(&Print_Sign, 0x01);
	//设置下划线							
	PrintDataToBuf(&Print_Sign, 0x1C);
	PrintDataToBuf(&Print_Sign, 0x2D);
	PrintDataToBuf(&Print_Sign, 1);
	
	PrintDataToBufs(&Print_Sign, _GJJLBB_); //真实打印数据		

	//取消下划线							
	PrintDataToBuf(&Print_Sign, 0x1C);
	PrintDataToBuf(&Print_Sign, 0x2D);
	PrintDataToBuf(&Print_Sign, 0);
	
	PrintDataToBufs(&Print_Sign, "\r\n"); //打印机遇到回车换行才回去马上打印出来
	
	RowDataCombinationOK = 1;		//本次数据组合完成,需要开始打印标志				

2.到这里有没有发现,我本次需要打印的数据都存在Print_Buf中,从下标0开始,一共存入了多少数据呢?其实就是Print_Sign的值,因为传入的指针,所以每次存数据后其值也会对应改变。

问题6:数据已经存入BUF中,个数也已经知道,如何轮训发送给打印机呢?
答:
1.关键步骤如下,具体在哪里轮训需要根据你自己的程序需求。

//PrintDataReadAddr 是从0 开始的读取BUF数据的下标地址
//Print_Sign代表本次BUF中一个有多少数据
if(PrintDataReadAddr < Print_Sign)
{
	//BUF中读取数据下标地址与其当前读取成功的个数相等,也就是读取地址==打印机获取个数,则代表读取后已经写入打印机,下次可以读取下一个数据。
	if(PrintWriteNum == PrintDataReadAddr)
	{
		PrintWriteNum++;
		
		readPrintBufData = Print_Buf[PrintDataReadAddr];

	}	
	//将读取值发送给打印机,成功才累加读取地址
	if(printByte(readPrintBufData))
	{
		PrintDataReadAddr++;
	}
}
//本次一行数据写入完成
else
{
	PrintRowOK = 1; //该标志位用于允许新的数据写入BUF中,本次读取完全才能重新写入新的数据,从0地址开始。
}

问题7:上位机有很多数据需要发送给打印机打印,怎能操作呢?
答:
1.打印机是接收到一行数据(若该行数据中包含回车换行则自动立即打印),否则需要等待打印机的缓存区满才自动打印。
2.LQ-630II打印机缓存区 32K。
3.为了保证打印机每次完整打印一行数据后再去打印下一行,我们使用的是应答方式,也就是下位机作为直接驱动打印机的控制端,在打印机完成一行打印后主动请求下一行数据,上位机收到新的数据请求后将数据发送给下位机。这中间最重要的是通讯协议中需要定义行标志,上下统一,从而保证请求与接收数据的唯一性。
总结:下位机需要打印第一行,则向上位机发送1,上位机收到1则将第一行数据按照特定格式与编号1一起发送给下位机,下位机收到1与自己请求编号1相同,则获取该消息后的数据包,解析数据,打印出来。接着将请求编号设置为2,去请求第二行数据,依次类推。

目前已经完成所有打印需求,文档更新不够详细,很多具体细节有需要的可以留言讨论。

你可能感兴趣的:(笔记)