前情提要
内容主要包含OLED显示中英文混合的代码逻辑。
OLED屏幕介绍
四针脚 OLED 显示屏是一种常见的显示模块,包括一个 OLED 显示屏和 4 个引脚,常用于嵌入式系统、小型电子设备,如智能手表、健康追踪器等3。
引脚功能3
- VCC:电源引脚,一般提供 3.3V 或 5V 电源,为模块供电。
- GND:地引脚,用于提供模块接地。
- SCL:时钟引脚,传输数据时提供时钟信号,需连接到主控芯片时钟引脚。
- SDA:数据引脚,用于传输数据,使用 I2C 接口时也称为串行数据线。
特点
- 接口简单:主要使用 I2C 接口,线路连接少,通信协议简单,降低硬件设计复杂度和成本,也便于软件开发1。
- 显示效果出色:具有高对比度,能呈现清晰锐利的图像;响应速度快,适合显示动态内容;视角宽广,从各个角度观看画面失真小5。
- 低功耗:显示黑色时几乎不耗电,在电池供电设备中能有效延长续航时间5。
- 尺寸小:屏幕尺寸通常较小,如 0.96 英寸等,适用于空间有限的小型设备3。
工作原理
- OLED 发光原理:通过电场驱动,有机半导体材料和发光材料经载流子注入和复合后实现发光,每个像素点可独立控制发光,无需背光源4。
- 数据传输与控制:通过 I2C 或 SPI 通信协议,主控芯片向 OLED 显示屏的控制器发送命令和数据,控制器内部的帧缓冲区存储像素状态,根据接收到的指令和数据更新显示内容。
OLED显示基础(重要)
我单独拿出这个来是因为,写逻辑的时候需要计算显示位置,这张图可以很好的表现出oled屏幕的空间,我们可以将OLED屏幕想象成一个64行128列的表格。
中文一般是16*16,英文类符号一般是8*16(高*长),也就是说一个汉字在oled屏幕上显示需要花费16行16列,英文同理。中英文在行上占格子的不同,就需要设计一个合理的逻辑方便他们可以一起显示。还需要说明,按照以上论述可以得到中文是最多有4行8列,英文则是4行16列。
OLED核心代码逻辑介绍
第一个核心设计是,如何解决通过中文索引取字符点阵?
我设计了一个结构体,构建结构体数组,将汉字作为索引的角色,来取字符点阵,结构体设计如下;Index存储的是3个字节,这里的编码结构是gbk的,所以一个中文是两个字节,然后加上\0构成一个char数组,来作为结构体数组的索引。
typedef struct
{const char Index[3];uint8_t data[32];}Myoled;
取输入,采用循环,每两个字符,为一个中文,将输入的字符串切割成单个中文(SigleChinese),将切割好的单个汉字与Myoled结构体构成的数组中的每一个元素比较,当中文匹配成功,取出中文进行显示。
char SigleChinese[3] = {Chinese[i], Chinese[i + 1], '\0'};uint8_t pIndex;for (pIndex = 0; strcmp(expe[pIndex].Index, "") != 0; pIndex++) {if (strcmp(expe[pIndex].Index, SigleChinese) == 0) {break;}}
第二个核心设计是,如何中英文混合显示?
这就涉及到我上面提到的位置计算(列位置的计算),初始位置+中文输出次数*16+英文输出次数*8。
offsetcol = Column + 16 * ccount + 8 * ecount;
OLED中英文显示核心代码
//显示实验,名字,日期
void OLED_ShowChar1(uint8_t Line, uint8_t Column, char Char, uint8_t ccount, uint8_t ecount)
{ uint8_t i;uint8_t offsetcol=0;offsetcol = Column + 16 * ccount + 8 * ecount;OLED_SetCursor((Line - 1) * 2, offsetcol); //设置光标位置在上半部分for (i = 0; i < 8; i++){OLED_WriteData(OLED_F8x16[Char - ' '][i]); //显示上半部分内容}OLED_SetCursor((Line - 1) * 2 + 1, offsetcol); //设置光标位置在下半部分for (i = 0; i < 8; i++){OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]); //显示下半部分内容}
}void OLED_ShowChinses_oneexpe(uint8_t Line, uint8_t Column,const uint8_t *t, uint8_t ccount, uint8_t ecount)
{ uint8_t i;uint8_t offsetcol=0;offsetcol = Column + 16 * ccount + 8 * ecount;OLED_SetCursor((Line - 1) * 2, offsetcol); //设置光标位置在上半部分for (i = 0; i < 16; i++){OLED_WriteData(t[i]); //显示上半部分内容}OLED_SetCursor((Line - 1) * 2 + 1, offsetcol); //设置光标位置在下半部分for (i = 0; i < 16; i++){OLED_WriteData(t[i+16]); //显示下半部分内容}
}void OLED_ShowChinses_exep1(uint8_t Line, uint8_t Column, char *Chinese)
{//uint8_t currentColumn = Column;uint8_t i = 0;uint8_t j = 1;uint8_t ccount = 0;uint8_t ecount = 0;while (Chinese[i] != '\0') { if (j == 1){if ((Chinese[1] & 0x80) == 0){Column = (Column - 1) * 8;}else{Column = (Column - 1) * 16;}j = 0;}if ((Chinese[i] & 0x80) == 0) { // 单字节字符(英文)OLED_ShowChar1(Line, Column, Chinese[i], ccount, ecount);//currentColumn += 1; i++;ecount++;} else { // 双字节字符(中文)char SigleChinese[3] = {Chinese[i], Chinese[i + 1], '\0'};uint8_t pIndex;for (pIndex = 0; strcmp(expe[pIndex].Index, "") != 0; pIndex++) {if (strcmp(expe[pIndex].Index, SigleChinese) == 0) {break;}}OLED_ShowChinses_oneexpe(Line, Column, expe[pIndex].data, ccount, ecount);//currentColumn += 1; i += 2; // 跳过中文的第二个字节ccount++;}}
}
总结
我认为我在做这个实验的时候遇到的最大的问题不是核心代码的逻辑设计,反而是一个编码问题,就是我的字库文件是utf8的,但是我的编译器是gpk的,这样在汉字匹配的时候就会出现汉字无法正确匹配的问题,我也是在进行调试后才发现,从字模库中取出的汉字并不是我所要的,因为utf8是3字节,然后我的逻辑只取两字节,就会出问题。然后还有一个问题就是,位置的计算,我一开始没反应过来,为啥显示内容没问题,显示位置异常,然后就研究了一下上面的坐标图,就推出了我想的那个公式。总之,遇到过程还是要多调试去发现它中间经历了什么,然后才方便解决,调试真是个好东西。