本次的diy活动主要的是实现电子书功能,我们分析一下具体的任务和需求以及本次提供的基础工程都有哪些。
任务分析:
1、 编程实现LED灯的闪烁。
学习GPIO输出能力的控制,该部分我们在商议章节已经实现
2、 编程实现按键控制LED灯的亮灭(按下一次LED亮,再按一次LED灭) 。
学习GPIO输出采集功能,通过上一章节综合了三个按键和LED进行了综合展示,已实现
3、 基于屏幕驱动函数(在屏幕任意位置画点)实现字符的显示,例如显示Hello EEPW & DigiKey,也可显示自己喜欢的名言警句 。
这一部分实际上是对墨水屏驱动的全面掌握,本章也重点实现对象。
4、 实现电子书基本功能,实现按键翻页(上一页、下一页等)。
电子书功能实际上是墨水屏显示和按键功能的结合,主要设计一些逻辑问题。
资源分析:本次提供的工程中已经包含了相关的BSP基础函数文件和墨水屏的驱动文件,基本上实现了写点操作。我们需要做的实际上是着重是对引用层的实现,也就是把字如何显示在墨水屏山。
根据目前的资源来看,已经提供了一个HZK16的汉字库文件和Font_16x24_h的ASCII的点阵函数,实际上这两种字库尺寸是不匹配的,汉字库基本上是16*16的,内存中也占用的是两个字节,ASCII占用一个自己,最好是8*16的,所以这里我们使用点阵生成工具生成一个Font_8x16_h。
显示内容我们可以自行进行电子书的内容复制过来显示,不过本次提供了一个“大学之道”的数组指针,我们就把他作为电子书的内容进行展示。
到这里我们基本完成了实现前的准备工作,将任务3和任务2同时进行实现。
基本功能
开机进行初始化(LED、按键、墨水屏),然后进行墨水瓶初始化内容展示(显示Hello EEPW & DigiKey),等待按键操作;按键B开始进行电子书功能,这个时候其他按键是失效的;按键A进行上一页的操作,按键C进行下一页的操作,注意要进行防抖设计,不然会识别很多次操作。
代码实现
首先我们要实现单个ASCII字符的实现:
void draw_fb_ASCII(int x, int y, char word) { const uint8_t *pData; uint8_t FONT_cut; unsigned char idxX, idxY; int lineData; short int FONT_H,FONT_W; FONT_H = ASCII_16_FONT_H; FONT_W = ASCII_16_FONT_W; for (idxY = 0; idxY < FONT_H; idxY++) { if(FONT_W % 8 == 0) { FONT_cut = FONT_W/8; } else { FONT_cut = FONT_W/8 + 1; } for (uint8_t i = 0; i < FONT_cut; i++) { lineData = lineData << 8; lineData = lineData | pData[idxY * FONT_cut + FONT_cut - i - 1]; } for (idxX = 0; idxX < FONT_W; idxX++) { if (lineData & (0x01 << idxX)) { draw_fb_point(x + idxX, y + idxY); } else { clear_fb_point(x + idxX, y + idxY); } } } }
接下来是汉字单字的显示控制:
void draw_fb_GB2132(int x, int y, unsigned int bankCode, unsigned int posCode) { const uint8_t *pData; unsigned char idxX, idxY; int lineData; int pos; short int FONT_H,FONT_W; FONT_H = GB2312_FONT_H; FONT_W = GB2312_FONT_W; pos = ((bankCode - 0xA1) * 94 + (posCode - 0xA1)) * (FONT_H * FONT_W / 8); pData = acHZK16C + pos; for (idxY = 0; idxY < FONT_W; idxY++) { lineData = pData[idxY * 2]; lineData = lineData << 8; lineData = lineData | pData[idxY * 2 + 1]; for (idxX = 0; idxX < FONT_H; idxX++) { if (lineData & (0x01 << idxX)) { draw_fb_point(x + FONT_H - idxX, y + idxY); } else { clear_fb_point(x + FONT_H - idxX, y + idxY); } } } }
因为ASCII和汉字的首字节的范围是不一样的,我们通过这个进行区分:
void draw_fb(int x, int y, unsigned char *data, char Mode) { short int word,FONT_H,FONT_W; int i, dataLen = My_strlen(data); for (i = 0; i < dataLen;){ if(data[i] > 0x80){ FONT_H = GB2312_FONT_H; FONT_W = GB2312_FONT_W; if ((x + FONT_W) > EPD_H) { x = 0; if(y+FONT_H>=EPD_W) break; else y += FONT_H; } draw_fb_GB2132(x, y, data[i], data[i+1]); x += FONT_W; i += 2; } else{ FONT_H = ASCII_16_FONT_H; FONT_W = ASCII_16_FONT_W; if ((x + FONT_W) > EPD_H) { x = 0; y += FONT_H; } draw_fb_ASCII(x, y, data[i]); x += FONT_W; i++; } } }
接下来就是初始化以及main中的内容了,由于汉字是16*16的,所以一页屏幕可以显示144个汉字符号,对应的字节数是288个,简单实现就可以通过这种固定数字显示的方式进行:
int main(void) { system_init(); LED_init(); key_init(); epd_init(); MY_DEV_Delay_ms(500); draw_fb(0, 50, "Hello EEPW & DigiKey"); draw_fb(0, 70, "by 孤独的写手"); updata_to_epd(DISPLAY_PART); LED_ON(); while (true) { if(gpio_get(INKY_KEYA_PIN) == 0) { MY_DEV_Delay_ms(10); if(gpio_get(INKY_KEYA_PIN) == 0) { while(gpio_get(INKY_KEYA_PIN) == 0); if(HZK_offise >= 288 && text_mode == 1){ HZK_offise -= 288; draw_fb(0, 0, dx_dzs+HZK_offise,FONT_Mode); updata_to_epd(DISPLAY_PART); } } } if(gpio_get(INKY_KEYB_PIN) == 0) { MY_DEV_Delay_ms(10); if(gpio_get(INKY_KEYB_PIN) == 0) { while(gpio_get(INKY_KEYB_PIN) == 0); text_mode = 1; HZK_offise = 0; draw_fb(0, 0, dx_dzs,FONT_Mode); updata_to_epd(DISPLAY_PART); } } if(gpio_get(INKY_KEYC_PIN) == 0) { MY_DEV_Delay_ms(10); if(gpio_get(INKY_KEYC_PIN) == 0) { while(gpio_get(INKY_KEYC_PIN) == 0); if(HZK_offise < My_strlen(dx_dzs && text_mode == 1){ HZK_offise += 288; draw_fb(0, 0, dx_dzs+HZK_offise,FONT_Mode); updata_to_epd(DISPLAY_PART); } } } } }
效果如下:
【Let's do第二期成果视频】 https://www.bilibili.com/video/BV1osaXeEEhd/?share_source=copy_web&vd_source=b04da51472e5874aa452f4a273672c46