您当前的位置:首页 > 电子 > 光电显示与成像

STM32+OLED屏(软件IIC+位带+帧缓冲区)刷新速率优化(四)

时间:08-16来源:作者:点击数:
城东书院 www.cdsy.xyz

1.优化刷新速率

前三篇几乎已经完成了OLED屏显示的全部内容,当然,还缺少一些精致的图形,这方面可以自己动手做一些喜欢的图形,也可以移植一下大佬们图形库,并不是很复杂所有就不多赘述了。这一篇主要是优化屏幕的刷新速率,让OLED的刷新率尽可能的快一些,也就是经常说的高帧,优化刷新速率分为两篇,分别用软件IIC和硬件IIC,这一篇是软件IIC。

之前的屏幕刷新主要是对屏幕的某一页的一个区间读写,如果一个屏幕要写两个或者两个以上的字符时,就要调用多次寻址操作,但是如果在我们在单片机中构建一个和SSD1306芯片的GRAM等大的SRAM内存,直接在单片机的SRAM数组上写,最后再SRAM数组的数据一次性写到SSD1306中,就只用调用一次写函数,可以大大提高芯片的读写操作。

构建帧缓冲区后,再优化软件IIC,使用stm32位带操作,直接操作GPIO管脚的高低电平,更快速简洁,位带是一种在单片机中使用的技术,它可以将单个位(bit)与一个特定的内存地址关联起来。

2.帧缓冲区

就如先前所说, 屏幕刷新主要是对屏幕的某一页的一个区间读写,但是每写一个字符就必须要寻址一次,当要写十个字符就必须要寻址十次,不仅效率不高还非常麻烦,为了解决这一麻烦提高工作效率,可以在单片机内部构建一个和OLED屏幕SSD1306芯片的GRAM等大的SRAM缓冲区(在单片机内部构建缓冲区数组),因为是为刷新帧率而存在的数组也称为帧缓冲区或显存。有了这片缓冲区就不用频繁的寻址了,如果要写十个字符就直接在帧缓冲区中操作,等这十个字符写好后再一次性把缓冲区的数据写入屏幕的GRAM中,这种操作只需要一次寻址,大大提高工作效率。按照正点原子的说法:

单片机有无构建帧缓冲区示意图:

2.1 构建帧缓冲区与画点函数

构建一个缓冲数组OLED_GRAM[8][128](实际上是SRAM)为帧缓冲区,之后所有的显示操作都放在这个帧缓冲区中,修改完成图形数据后,再一次性将单片机内部的OLED_GRAM写入SSD1306的GRAM中。

//构建OLED帧缓冲区
uint8_t OLED_GRAM[64/8][128];

构建帧缓冲区后,就面临两个问题,1、如何在帧缓冲区写数据?2如何将帧缓冲区的数据写入OLED屏幕上?在帧缓冲区中写数据可以使用画点函数,把帧缓冲区的数据写入SSD1306的GRAM中使用IIC通信。

先说在帧缓冲区写数据,编写画点函数,对帧缓冲区写入数据。保存当前要写入的页面中八个点,再修改其中要修改的点,最后把修改好的八个点写入帧缓冲区,就修改了其中一个点了。画点函数对每次对帧缓冲区的一个点进行写0和1,需要那个点亮起就对该点写1,需要那个点灭掉就对该点写0,但是如果把该写1的地方写0,把该写0的地方写1,那么就会出现截然相反的效果,这就是帧缓冲区反显操作,这种操作并不需要对模块写入反显命令就可以实现。

/**
  * @brief  OLED帧缓冲画点函数
  * @param  x 行位置
  * @param  y 列位置
  * @param  mode 显示模式:1--正显  0--反显
  * @retval 无
  * @explain 在帧缓冲区任意位置正/反显示一个点
  */
void OLED_Framebuffer_Drawpoint(uint8_t x, uint8_t y, uint8_t mode)
{
	uint8_t page, line, temp = 0;
	
	if(x>128 || y>64) return;//超出屏幕范围保护
	page = y/8; // y方向8个字节 8Byte*8Bit = 64Bit  y坐标除以8得要操作的Byte位
	line = y%8; // y方向8个字节 8Byte*8Bit = 64Bit  y坐标取余8得要操作的Bit位 
	temp = OLED_GRAM[page][x]; 
	if(mode) temp |= 1<<line;
	else temp &= ~(1<<line);
	OLED_GRAM[page][x] = temp;
}

2.2 在帧缓冲区绘制一个字符

写入字符和之前一样,画点函数和写数据函数(OLED_Write_Data()) 有部分差异,写数据函数(OLED_Write_Data()) 是一次写入8 bit,画点函数一次只写入一个点,所以在我们写入字符时要在之前的基础上加上八次循环,再相应的偏移八位即可,mode表示正/反显。

/**
  * @brief  OLED在帧缓冲绘制一个字符
  * @param  x 行位置
  * @param  y 列位置
  * @param  Fontsize 字体大小
  * @param  Char 要显示的一个字符
  * @param  mode 显示模式:1--正显  0--反显
  * @retval 无
  */
void OLED_Framebuffer_DrawChar(uint8_t x, uint8_t y, uint16_t Fontsize, const char Char, uint8_t mode)
{
	uint8_t i, j, k;
	uint8_t temp;
	
	for(k=0; k<Fontsize/8; k++) {
		switch(Fontsize) {
			case 8:{
				for(i=0; i<6; i++){//字宽为6
					temp = Ascii_6x8[Char - ' '][i+k*6];
					for (j=0; j<8; j++) {//画8个点
						if(mode) OLED_Framebuffer_Drawpoint(x+i, y+j+k*8, (temp>>j & 0x01) ? 1:0);
						else     OLED_Framebuffer_Drawpoint(x+i, y+j+k*8, (temp>>j & 0x01) ? 0:1); 
					}
				}
				break;
			}
			case 16: {
				for(i=0; i<8; i++) {//字宽为8
					temp = Ascii_8x16[Char - ' '][i+k*8];
					for (j=0; j<8; j++) {//画8个点
						if(mode) OLED_Framebuffer_Drawpoint(x+i, y+j+k*8, (temp>>j & 0x01) ? 1:0);
						else     OLED_Framebuffer_Drawpoint(x+i, y+j+k*8, (temp>>j & 0x01) ? 0:1); 
					}
				}
				break;
			}
			case 24: {
				for(i=0; i<12; i++) {//字宽为12
					temp = Ascii_12x24[Char - ' '][i+k*12];
					for (j=0; j<8; j++) {//画8个点
						if(mode) OLED_Framebuffer_Drawpoint(x+i, y+j+k*8, (temp>>j & 0x01) ? 1:0);
						else     OLED_Framebuffer_Drawpoint(x+i, y+j+k*8, (temp>>j & 0x01) ? 0:1); 
					}
				}
				break;
			}
		}
	}
}

2.3 在帧缓冲区绘制一串字符

写入字符串可以照搬,和没有显存之前相差无异,只是在后面加上一个mode表示正/反显。

/**
  * @brief  OLED在帧缓冲绘制字符串
  * @param  x 行位置
  * @param  y 列位置
  * @param  Fontsize 字体大小
  * @param  String 显示字符串,
  * @param  mode 显示模式:1--正显  0--反显
  * @retval 无
  */
#include <string.h>
void OLED_Framebuffer_DrawString(uint8_t x, uint8_t y, uint16_t Fontsize, const char* String, uint8_t mode)
{
	uint8_t i, len;
	len = strlen(String);
	
	for(i=0; i<len; i++) {
		switch(Fontsize) {
			case 8:OLED_Framebuffer_DrawChar(x+i*6, y, Fontsize, String[i], mode);
			break;
			case 16:OLED_Framebuffer_DrawChar(x+i*8, y, Fontsize, String[i], mode);
			break;
			case 24:OLED_Framebuffer_DrawChar(x+i*12, y, Fontsize, String[i], mode);
			break;
		}
	}
}

2.4 在帧缓冲区绘制一个汉字

在原有的基础上,和写入字符一样,偏移八位即可。

/**
  * @brief  OLED在帧缓冲区绘制汉字
  * @param  x 行位置
  * @param  y 列位置
  * @param  Chinese 汉字,
  * @param  mode 显示模式:1--正显  0--反显
  * @retval 无
  */
void OLED_Framebuffer_DrawChinese(uint8_t x, uint8_t y, uint8_t Chinese, uint8_t mode)
{
	uint8_t i, j, k;
	uint8_t temp;
	
	for(k=0; k<2; k++) {
		for (i=0; i<16; i++) {//字宽为24
			temp = Chinese_16x16[Chinese][i+k*16];
			for (j=0; j<8; j++) {//画8个点
				if(mode) OLED_Framebuffer_Drawpoint(x+i, y+j+k*8, (temp>>j & 0x01) ? 1:0);
				else     OLED_Framebuffer_Drawpoint(x+i, y+j+k*8, (temp>>j & 0x01) ? 0:1); 
			}
		}
	}
}

2.5 在帧缓冲区绘制一张图片

图片也一样,照猫画虎,直接写入。

/**
  * @brief  OLED显示图片
  * @param  mode 显示模式:1--正显  0--反显
  * @retval 无
  * @explain 无
  */
void OLED_Framebuffer_DrawImageBMG(uint8_t mode)
{
	uint8_t i, j, k;
	uint8_t temp;
	
	for(k=0; k<8; k++) {
		for (i=0; i<128; i++) {
			temp = ImageBMG64x128[i+k*128];
			for (j=0; j<8; j++) {//画8个点
				if(mode) OLED_Framebuffer_Drawpoint(i, j+k*8, (temp>>j & 0x01) ? 1:0);
				else     OLED_Framebuffer_Drawpoint(i, j+k*8, (temp>>j & 0x01) ? 0:1); 
			}
		}
	}
}
城东书院 www.cdsy.xyz
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐