一、串口传输文件
1.串口连接
将两个USB TO TTL串口的RXD、TXD的引脚交叉连接,并将两个USB接口各自接上一台笔记本电脑,实现两台计算机之间的串口传输。
串行接口简称为串口,串行接口 (Serial Interface)是指数据一位一位地顺序传送。实现双向通信就需要一对传输线,即TX与RX线。
电路连接方式:串口如果要实现双向传输,则设备1与设备2,TX与RX要交叉相连。
起始位: 数据线TX由高电平变为低电平。
停止位: 数据线TX由低电平变为高电平。
起始位和停止位作用:如果接收设备检测到数据线由高电平变为低电平,就是接收到了来自发送设备的起始信号,表示开始数据的传输。如果接收设备检测到数据线由低电平变为高电平,就是接收到了来自发送设备的停止信号,表示一帧数据的结束。
常用串口传输格式:1bit 起始位+8bit 数据位+1bit 停止位(无奇偶校验位)
2.文件传输
1.速率为115200
发送端:
接收端:
将保存的文件拓展改为jpg文件,收到结果如下:
2.设置速率为2000000
发送端:
接收端:
3.结论:
由以上实验结果可知,文件大小、波特率、传输时间三者关系可概括为
传输时间=文件大小/波特率
利用串口传输文件,实际得到的传输时间比预计的传输时间要大,存在延时情况。且在一定程度上,相同波特率下传输不同大小的文件,传输时间随文件大小的增大而延长;在不同的波特率下传输相同的文件需要的传输时间随波特率的增大而缩短。
二、点阵汉字的字模读取与显示
1.原理
1.区位码
在国标GD2312—80中规定,所有的国标汉字及符号分配在一个94行、94列的方阵中,方阵的每一行称为一个“区”,编号为01区到94区,每一列称为一个“位”,编号为01位到94位,方阵中的每一个汉字和符号所在的区号和位号组合在一起形成的四个阿拉伯数字就是它们的“区位码”。区位码的前两位是它的区号,后两位是它的位号。用区位码就可以唯一地确定一个汉字或符号,反过来说,任何一个汉字或符号也都对应着一个唯一的区位码。
2.机内码
汉字的机内码是指在计算机中表示一个汉字的编码。机内码与区位码稍有区别。如上所述,汉字区位码的区码和位码的取值均在1~94之间,如直接用区位码作为机内码,就会与基本 ASCII 码混淆。为了避免机内码与基本 ASCII 码的冲突,需要避开基本 ASCII 码中的控制码(00H~1FH),还需与基本 ASCII 码中的字符相区别。为了实现这两点,可以先在区码和位码分别加上20H,在此基础上再加80H(此处“H”表示前两位数字为十六进制数)。经过这些处理,用机内码表示一个汉字需要占两个字节,分别 称为高位字节和低位字节,这两位字节的机内码按如下规则表示:
高位字节 = 区码 + 20H + 80H(或区码 + A0H)
低位字节 = 位码 + 20H + 80H(或位码 + AOH)
由于汉字的区码与位码的取值范围的十六进制数均为01H5EH),所以汉字的高位字节与低位字节的取值范围则为A1HFEH
3.汉字点阵获取
1)利用区位码获取汉字
汉字点阵字库是根据区位码的顺序进行存储的,因此,我们可以根据区位来获取一个字库的点阵,它的计算公式如下:
点阵起始位置=((区码-1)94 +(位码–1)) 汉字点阵字节数
获取点阵起始位置后,我们就可以从这个位置开始,读取出一个汉字的点阵。
2)利用汉字机内码获取汉字
前面我们己经讲过,汉字的区位码和机内码的关系如下:
机内码高位字节=区码+20H+ 8OH(或区码+A0H)
机内码低位字节=位码+20H+ 8OH(或位码+AOH)
反过来说,我们也可以根据机内码来获得区位码:
区码=机内码高位字节一AOH
位码=机内码低位字节一AOH
2.实现步骤
1.将Asci0816.zf,HZKf2424.hz,logo.txt和sanli.jpg放在该文件夹下:
其中logo.txt文件内容如下:
2.编写代码
代码如下:
#include<iostream>
#include<opencv/cv.h>
#include"opencv2/opencv.hpp"
#include<opencv/cxcore.h>
#include<opencv/highgui.h>
#include<math.h>
using namespace cv;
using namespace std;
void paint_chinese(Mat& image,int x_offset,int y_offset,unsigned long offset);
void paint_ascii(Mat& image,int x_offset,int y_offset,unsigned long offset);
void put_text_to_image(int x_offset,int y_offset,String image_path,char* logo_path);
int main()
{
String image_path="sanli.jpg";//图片路径
char* logo_path=(char*)"logo.txt";//学号姓名路径
put_text_to_image(270,280,image_path,logo_path);
return 0;
}
void paint_ascii(Mat& image,int x_offset,int y_offset,unsigned long offset)
{
//绘制的起点坐标
Point p;
p.x=x_offset;
p.y=y_offset;
char buff[16]; //存放ascii字模
//打开ascii字库文件
FILE *ASCII;
if((ASCII=fopen("Asci0816.zf","rb"))==NULL)
{
printf("Can't open ascii.zf,Please check the path!");
//getch();
exit(0);
}
fseek(ASCII,offset,SEEK_SET);
fread(buff,16,1,ASCII);
int i,j;
Point p1=p;
for(i=0;i<16;i++) //十六个char
{
p.x=x_offset;
for(j=0;j<8;j++) //一个char八个bit
{
p1=p;
if(buff[i]&(0x80>>j)) //测试当前位是否为1
{
//由于原本ascii字膜是8*16的,不够大,所以原本的一个像素点用4个像素点替换,替换后就有16*32个像素点
circle(image,p1,0,Scalar(0,0,255),-1);
p1.x++;
circle(image,p1,0,Scalar(0,0,255),-1);
p1.y++;
circle(image,p1,0,Scalar(0,0,255),-1);
p1.x--;
circle(image,p1,0,Scalar(0,0,255),-1);
}
p.x+=2; //原来的一个像素点变为四个像素点,所以x和y都应该+2
}
p.y+=2;
}
}
void paint_chinese(Mat& image,int x_offset,int y_offset,unsigned long offset)
{
//实际在图片上绘制的像素点坐标
Point p;
p.x=x_offset;
p.y=y_offset;
//打开hzk24汉字库文件
FILE *HZK;
char buff[72];//存放汉字字模
if((HZK=fopen("HZKf2424.hz","rb"))==NULL)
{
printf("Can't open HZKf2424.hz,Please check the path!");
//getch();
exit(0);//退出
}
fseek(HZK,offset,SEEK_SET);//将文件指针移动到偏移量的位置
fread(buff,72,1,HZK);//从偏移量的位置读取72个字节,每个汉字占72个字节
bool mat[24][24];//定义一个新的矩阵存放转置后的文字字模
//转置汉字字模矩阵,因为汉字字模存储的是装置后的数据(反的)
int i,j,k;
for(i=0;i<24;i++) //24x24点阵汉字,一共有24行
{
for(j=0;j<3;j++) //横向有3个字节,循环判断每个字节的
for(k=0;k<8;k++) //每个字节有8位,循环判断每位是否为1
if(buff[i*3+j]&(0x80>>k)) //测试当前位是否为1
{
mat[j*8+k][i]=true; //为1的存入新的字模中
}
else
{
mat[j*8+k][i]=false;
}
}
for(i=0;i<24;i++)
{
p.x=x_offset;
for(j=0;j<24;j++)
{
if(mat[i][j])
circle(image,p,1,Scalar(255,0,0),-1); //写(替换)像素点
p.x++; //右移一个像素点
}
p.y++; //下移一个像素点
}
}
void put_text_to_image(int x_offset,int y_offset,String image_path,char* logo_path)
{
//通过图片路径获取图片
Mat image=imread(image_path);
int length=18;//要打印的字符长度
unsigned char qh,wh;//定义区号,位号
unsigned long offset;//偏移量
unsigned char hexcode[30];//用于存放记事本读取的十六进制
FILE* file_logo;
if ((file_logo=fopen(logo_path,"rb"))==NULL)
{
printf("Can't open txtfile,Please check the path!");
//getch();
exit(0);
}
fseek(file_logo,0,SEEK_SET);//将文件指针移动到偏移量的位置
fread(hexcode,length,1,file_logo);
int x=x_offset,y=y_offset;//x,y:在图片上绘制文字的起始坐标
for(int m=0;m<length;)
{
if(hexcode[m]==0x23)
{
break;//读到#号时结束
}
//判断高阶两个十六进制数,大于或等于b0(汉字第一个是b0a1)的都应从汉字字库找
else if(hexcode[m]>0xaf)
{
qh=hexcode[m]-0xaf;//计算区码
wh=hexcode[m+1]-0xa0;//计算位码
offset=(94*(qh-1)+(wh-1))*72L;//计算该汉字在字库中偏移量
paint_chinese(image,x,y,offset);
m=m+2;//一个汉字占两个char,所以加2
x+=24;//一个汉字在图片上占24个像素点,所以水平坐标每次+24
}
else
{
wh=hexcode[m];
offset=wh*16l;//计算英文字符的偏移量
paint_ascii(image,x,y,offset);
m++;//一个char
x+=16;//原本8*16,改为16*32,原本的一个像素点现用四个像素点绘画
}
}
cv::imshow("image",image);
cv::waitKey();
}
3.编译执行文件
输入以下命令
g++ F.cpp -o F `pkg-cnfig --cflags --libs opencv`
结果如下:
三、总结
通过本次实验,我学会了如何使用串口进行文件传输、如何在Ubuntu下使用C/C++调用opencv库来将logo中的名字和学号叠加显示在图片右下位置,同时,我还了解到了文件大小、波特率和传输时间三者之间的关系,以及汉字的机内码、区位码编码规则和字形数据存储格式。很有趣。