依旧是在例程的基础上进行增改,处理的是对诗词的分析、显示。遇到标点符号,进行换行处理。例程中,实际上把文字转换为了点阵图的图形来显示的。但我没有对例程进行仔细分析,所以走了一条弯路。基本思路是,分析需要显示的汉字、符号,总字库中找到对应的点阵数据,然后根据显示特点,再设置与画面点对点对应的图片上。这个图片定义的宽度与墨水屏的现实宽度一致,高度是墨水屏高的4倍,主要是为了方便实现滚动显示。在转换过程中,因为诗词容量通常不大,所以一次性把所有文字的点阵数据全部保存在图像变量中。这样在处理滚动的时候,只需要控制显示范围的起始、结束行号就行,相当于拿一个框子在图片上上下移动,这样显示起来不用加入过多的计算。
程序主要使用两个函数,一个是把诗词内容的点阵数据转化到图片变量中。一个是根据当前滚动点,把图片数据反映到墨水屏上。
// 将诗词文字转换为背景图像,方便上下移动显示
// 遇到标点符号,自动追加换行符,将文字表示为一行一行的内容
// 墨水屏的分辨率为296*128,对16点阵字符为每行18.5个文字,可显示8行
// 作为示例,下面的诗词每行最多8个文字,因此不用考虑一行显示不下,需要换行的问题
void loadTextToBmp(unsigned char *shici) {
unsigned int screen_x=0, screen_y=0; // 屏幕上点的坐标
unsigned int chridx=0; // 字符在字符串中的索引
uint32_t i=0, j=0;
uint32_t idx=0;
unsigned char chr[3]={'\0','\0', '\0'};
const unsigned char *pData = NULL;
unsigned long lineData;
unsigned long pos;
unsigned char tmpstr[32] = {'\0'};
clearScreen();
// 初始化bmp数组
for (j = 0; j < 512; j++) {
for (i = 0; i < 296; i++) {
bmp[i][j] = 0;
}
}
// 定位到bmp的左上角位置,坐标0,0
screen_x =0; // 汉字位置坐标x ,对应在汉字字符串中的水平方向位置,每个汉字占横向占两个字节
screen_y = 0; // 汉字位置坐标y ,对应在汉字字符串中的垂直方向位置,每个汉字占纵向16行
idx = 0;
// 分析文字,取得对应的汉字点阵,放入bmp中
// 每次取出两个字节,一个汉字两个字节
for (chridx=0; chridx<strlen(shici); chridx+=2) {
chr[0] = shici[chridx];
chr[1] = shici[chridx+1];
byteImage[idx++] = chr[0];
byteImage[idx++] = chr[1];
// 计算汉字在字库中的位置:偏移量
pos = ((chr[0] - 0xA1) * 94 + (chr[1] - 0xA1)) * (GB2312_FONT_H * GB2312_FONT_W / 8);
// 获得该汉字所在地址
pData = acHZK16C + pos;
// 该汉字所在位置开始,(GB2312_FONT_W/8) * GB2312_FONT_H 个字节为该汉字占用的点阵数据
for (i=0; i<16; i++) {
for (j=0; j<8; j++) {
bmp[screen_x + j][screen_y+i] = (pData[i*2] & (1<<(7-j)))?1:0;
bmp[screen_x + j+8][screen_y+i] = (pData[i*2+1] & (1<<(7-j)))?1:0;
}
}
// 横向坐标向右移动16个像素点,设置下一个汉字的显示位置
screen_x = screen_x + GB2312_FONT_W;
// 遇到,和。要换行
if ((chr[0] == 0xA3 && chr[1] == 0xAC ) || (chr[0] == 0xA1 && chr[1] == 0xA3 ) || screen_x>= 296 || (chr[0] == 13 && chr[1] == 10 )) {
// 从头开始显示
screen_x = 0;
// 纵坐标向下移动16个像素点
screen_y += GB2312_FONT_H + 5;
}
}
}
// 一行可以容纳 18.5 个 16*16点阵的汉字
// 显示bmp映射的图像
void loadBmp(uint16_t rowno) {
uint32_t x=0, y = 0;
uint32_t idx = 0;
uint16_t screen_x=0, screen_y=0; // 屏幕上点的坐标(0,0) - (295,127)
if (rowno >=256) {
//clearScreen();
EPD_2IN9D_Clear();
return;
} else {
for (y=rowno; y<128+rowno; y++) {
for (x=0; x<296; x++ ) {
if (y>=272) {
clear_fb_point(x, y-rowno);
} else {
if (bmp[x][y]) {
draw_fb_point(x, y-rowno);
} else {
clear_fb_point(x, y-rowno);
}
}
}
}
}
updata_to_epd(DISPLAY_PART);
}显示效果如下:


整个电路,没有其他多余的电路。按照发货过来时的情况,两个模块直接对接就行。我这里为了方便调试,做了个简单的板子,相当于使用面包板中心进行连接。电路上因为没有附加其它的东西,所以很简单。

墨水屏使用SPI模式通讯,通过接口直接接入Pico的SPI外设。这样控制起来就相对简单了。以滚动方式显示文字内容,相对于通过按钮,替换屏幕文字的一个优势是:显示的内容是以图像平移方式显示,看起来顺滑一些,但比较消耗内存。如果是以按钮方式改变显示内容的话,通常是整行替换文字,对内存的要求不高,控制起来也相对容易一些。总之,各有优缺点吧。
参加这次活动的木器,是学习墨水屏的驱动方式。作为显示器件,虽然在构造和显示风格上存在不同的地方,但在外部驱动上,与其它TFT屏,区别还真不是很大。一样采用SPI接口。墨水屏和其它LCD、TFT最大的不同在于:断电后,墨水屏依然可以保持住断电前的显示状态,这一点对于某些场景(比如超市价签、电子工牌)非常实用。
我要赚赏金
