依旧是在例程的基础上进行增改,处理的是对诗词的分析、显示。遇到标点符号,进行换行处理。例程中,实际上把文字转换为了点阵图的图形来显示的。但我没有对例程进行仔细分析,所以走了一条弯路。基本思路是,分析需要显示的汉字、符号,总字库中找到对应的点阵数据,然后根据显示特点,再设置与画面点对点对应的图片上。这个图片定义的宽度与墨水屏的现实宽度一致,高度是墨水屏高的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最大的不同在于:断电后,墨水屏依然可以保持住断电前的显示状态,这一点对于某些场景(比如超市价签、电子工牌)非常实用。