1. OLED显示字符串
代码中,最外部的while循环用来控制显示字符串中的一个字符,当指针str超出字符串后,while循环跳出,函数返回。
while循环中,还做了判断,判断当前要显示的字符是否超出了屏幕的x轴,如果超出,则换行显示。接着判断要显示的字符是否超出了屏幕的y轴,如果超出,需要自己想解决方法,例如:滚动显示,延迟再分页显示,清屏再接着显示。我采用的是清屏再继续显示字符串剩下的内容。
相关代码:
/**
* @brief OLED显示字符串(如果超出x轴会换行显示)
*
* @param x x轴起始位置
* @param y y轴起始位置
* @param str 要显示的字符串
* @param mode 是否要反白显示
* 1,不反白显示
* 0,反白显示
*/
void OLED_ShowStr(uint8_t x, uint8_t y, char *str, uint8_t mode)
{
// 判断字符是否符合ASCII的范围
while ((*str) >= ' ' && (*str) <= '~')
{
if (x > OLED_PIXEL_X - OLED_FONT_WIDTH)
{
// 如果要显示的字符超出了屏幕的横向范围,换行显示
x = 0;
y += OLED_FONT_HEIGHT;
}
if (y > OLED_PIXEL_Y - OLED_FONT_HEIGHT)
{
// 如果要显示的字符超出了屏幕的纵向范围
// 做清屏操作,从(0,0)坐标继续显示
OLED_Clear();
x = y = 0;
}
OLED_ShowChar(x, y, *str++, mode);
x += OLED_FONT_WIDTH;
}
}
2. OLED显示数字
在函数中,通过for循环将要显示的数字一位一位地显示出来。在for循环内,通过计算将要显示的数字的一位(例如个位、十位、百位等)存放到temp中。
接着判断当前要显示的这一位数字是否是这个数的有效数字(通过enshow这个变量作为标志位,若enshow为初始值0,说明要显示的数字是000xxxx,前面几位数字还不是这个数的有效数字;直到第四位才是,到第四位时,enshow标志位会变成 1,表示这个数接下来的数位都是有效数字)。
显示一位数完成后,将显示下一位数字,这是判断显示是否会超出屏幕的x轴范围,若超出则换行显示。再判断显示是否会超出屏幕的y轴范围,若超出则清空屏幕,继续显示接下来的数字。
相关代码:
/**
* @brief 求一个数的多次方(n的m次方)
*
* @param n 底数
* @param m 幂
* @retval 返回这个底数的m次方
*/
static uint64_t __mypow(uint32_t n, uint32_t m)
{
uint64_t ret = 1;
while (m--)
{
ret *= n;
}
return ret;
}
/**
* @brief OLED显示数字(不考虑负数)
*
* @param x x轴起始位置
* @param y y轴起始位置
* @param num 要显示的数字
* @param len 数字的长度
* @param mode 是否要反白显示
* 1,不反白显示
* 0,反白显示
*/
void OLED_ShowNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t mode)
{
uint8_t i; // 循环计数值
uint8_t temp; // 临时变量
uint8_t enshow = 0; // 有效数字标志位,1:是有效数字,0,不是有效数字
// 通过循环将每一个数位显示出来
for (i = 0; i < len; i++)
{
// 通过计算得到当前最高位数的值
temp = num / __mypow(10, len - 1 - i) % 10;
if (temp == 0)
{
if (enshow == 0)
{
// 如果最高位是0,并且不是这个数的有效数字
OLED_ShowChar(x, y, ' ', mode);
}
else
{
// 如果当前位是0,并且是这个数的有效数字
OLED_ShowChar(x, y, '0', mode);
}
}
else
{
// 如果遇到非零的数位
enshow = 1;
OLED_ShowChar(x, y, '0' + temp, mode);
}
x += OLED_FONT_WIDTH;
if (x > OLED_PIXEL_X - OLED_FONT_WIDTH)
{
// 如果要显示的字符超出了屏幕的横向范围,换行显示
x = 0;
y += OLED_FONT_HEIGHT;
}
if (y > OLED_PIXEL_Y - OLED_FONT_HEIGHT)
{
// 如果要显示的字符超出了屏幕的纵向范围
// 做清屏操作,从(0,0)坐标继续显示
OLED_Clear();
x = y = 0;
}
}
}
3. OLED显示汉字
要显示汉字,需要准备汉字对应的字库,我使用 阴码 + 逐列式 + 逆向 + C51格式 生成并编写了如下字库:
struct CH32CharTypeDef
{
char Index[3]; // 汉字内码索引,一个汉字2字节,加上'\0'共三个字节
uint8_t Msk[32]; // 点阵码数据
};
static const struct CH32CharTypeDef CHCHar_16_16[] = {
"温", {0x10,0x04,0x60,0x04,0x02,0x7E,0x8C,0x01,0x00,0x40,0x00,0x7E,0xFE,0x42,0x92,0x42,0x92,0x7E,0x92,0x42,0x92,0x7E,0x92,0x42,0xFE,0x42,0x00,0x7E,0x00,0x40,0x00,0x00},/*"温",0*/
"湿", {0x10,0x04,0x60,0x04,0x02,0x7E,0x8C,0x01,0x00,0x44,0xFE,0x48,0x92,0x50,0x92,0x7F,0x92,0x40,0x92,0x40,0x92,0x7F,0x92,0x50,0xFE,0x48,0x00,0x44,0x00,0x40,0x00,0x00},/*"湿",1*/
"度", {0x00,0x40,0x00,0x30,0xFC,0x8F,0x24,0x80,0x24,0x84,0x24,0x4C,0xFC,0x55,0x25,0x25,0x26,0x25,0x24,0x25,0xFC,0x55,0x24,0x4C,0x24,0x80,0x24,0x80,0x04,0x80,0x00,0x00},/*"度",2*/
",", {0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*",",3*/
":", {0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*":",4*/
};
在字库中,首先定义了一个结构体类型,成员1是汉字内码索引,用于比对要打印的汉字是否与当前汉字内码索引的汉字一致。
成员2是在OLED上显示使用到的数据,对应每个像素的亮/灭情况。
在字库中,接着结构体类型,定义了该结构体类型的变量,存放可能会用上的 汉字内码索引 和 OLED显示相关的点阵数据。
OLED显示汉字相关代码:
/**
* @brief OLED显示汉字
*
* @param x x轴起始位置
* @param y y轴起始位置
* @param pHZ 要显示的汉字
* @param mode 是否要反白显示
* 1,不反白显示
* 0,反白显示
*/
void OLED_ShowHZ(uint8_t x, uint8_t y, char *pHZ, uint8_t mode)
{
uint8_t i; // 循环计数值
uint8_t j; // 循环计数值
uint8_t k;
uint8_t temp; // 临时变量
uint8_t y0 = y;
// 计算出汉字字库中数组元素的个数
uint8_t len = sizeof(CHCHar_16_16) / sizeof(CHCHar_16_16[0]);
// 字体的大小(所占多少个字节)
// 通过三目运算符判断字体是跨 奇数个页 还是 偶数个页
uint8_t size = OLED_CH_FONE_WIDTH * ((OLED_CH_FONT_HEIGHT % 8) ? (OLED_CH_FONT_HEIGHT / 8 + 1) : (OLED_CH_FONT_HEIGHT / 8));
while (*pHZ != '\0')
{
// 当前要显示的汉字不为'\0'时,继续循环
for (i = 0; i < len; i++)
{
if (pHZ[0] == CHCHar_16_16[i].Index[0] && pHZ[1] == CHCHar_16_16[i].Index[1])
{
// 如果在字库中找到了该汉字
// 判断当前汉字有没有超出屏幕显示
if (x > OLED_PIXEL_X - OLED_CH_FONE_WIDTH)
{
// 如果要显示的字符超出了屏幕的横向范围,换行显示
x = 0;
y += OLED_CH_FONT_HEIGHT;
y0 = y;
}
if (y > OLED_PIXEL_Y - OLED_CH_FONT_HEIGHT)
{
// 如果要显示的字符超出了屏幕的纵向范围
// 做清屏操作,从(0,0)坐标继续显示
OLED_Clear();
x = y = 0;
}
for (j = 0; j < size; j++)
{
temp = CHCHar_16_16[i].Msk[j];
for (k = 0; k < 8; k++)
{
if (temp & 0x01)
{
// 如果temp当前最低位是1,画点
OLED_DrawPoint(x, y, mode);
}
else
{
// 如果temp当前最低位是0,不画点,向显存写入0
OLED_DrawPoint(x, y, !mode);
}
temp >>= 1; // temp的次低位成为最低位,准备画点
y++; // 列,也就是纵坐标,加一
if (y - y0 >= OLED_CH_FONT_HEIGHT)
{
// 如果画完了一列,就要换下一列了
y = y0;
x++; // x轴加一
break;
}
}
}
break; // 如果在字库中找不到汉字
}
}
pHZ += 2;
}
}