SSD1306 控制核心思想 2021-08-18

SSD1306 控制核心思想:

  1. 在内存中创建一块内存区域,通过操作内存区域的每一个bit位实现在内存中描点,划线等操作
  2. 一次性将显存全部更新到SSD1306

ssd1306 页模式内存分配


在这里插入图片描述


1. 向SSD1306 写入一个数据或一条命令

在这里插入图片描述

数据变化条件

在这里插入图片描述

图片解释

1、 第一个字节(Slave Address )发送的是地址 和 读写属性(一般只会写数据);SA0(D/C引脚就是SA0)决定了从机的地址; 0x78

2、第二个字节(control byte )决定了第三个字节是命令还是数据

    a) Co = 0 代表后边全是数据

    b) D/C = 0 代表后边是数据(0x00);D/C = 0 代表后边是命令;(0x40)

操作步骤

  1. 写入地址或读写状态,一般是写 :0x78
  2. 写入控制字节,表明下一个字节是命令还是数据
  3. 写入具体的数据
void OLED_WR_Byte(u8 dat, u8 mode)
{
	I2C_Start();
	Send_Byte(0x78);//发送地址和写指令
	I2C_WaitAck();
	if (mode)
	{
		Send_Byte(0x40);//通知oled即将发命令
	}
	else
	{
		Send_Byte(0x00);//通知oled即将发数据
	}
	I2C_WaitAck();
	Send_Byte(dat);//发送真实的数据
	I2C_WaitAck();
	I2C_Stop();
}

定义显存

u8 OLED_GRAM[128][8]; //128*64 的oled

2.画点函数

  1. 获取需要操作的字节
  2. 获取需要操作字节的具体一个bit X
  3. 操作的字节的指定位置置1
//画点 操作的仅仅是内存
//x:0~127
//y:0~63
void OLED_DrawPoint(u8 x, u8 y)
{
	u8 i, m, n;
	i = y / 8;//得到行数
	m = y % 8;  //需要移动的bit数(一个字节内的bit位置) 每行8个bit,需要对指定的bit赋值
	n = 1 << m;  
	OLED_GRAM[x][i] |= n; //指定字节的 指定bit赋值为1
	//OLED_GRAM[x][i].bi
}

3. 划线函数

  1. 划线分为3类,横线、竖线,斜线
    1. 横线 x 不变 ,y变化
    2. 竖线 X 变化 ,Y不变
  2. 画斜线
    1. 需要计算斜率 斜率放大10倍,否则斜率为零
    2. 根据斜率依次变化想,计算得到Y
//画线
//x:0~128
//y:0~64
void OLED_DrawLine(u8 x1, u8 y1, u8 x2, u8 y2)
{
	u8 i, k, k1, k2, y0;
	if ((x1 < 0) || (x2 > 128) || (y1 < 0) || (y2 > 64) || (x1 > x2) || (y1 > y2))
		return;
	if (x1 == x2) //画竖线
	{
		for (i = 0; i < (y2 - y1); i++)
		{
			OLED_DrawPoint(x1, y1 + i);
		}
	}
	else if (y1 == y2) //画横线
	{
		for (i = 0; i < (x2 - x1); i++)
		{
			OLED_DrawPoint(x1 + i, y1);
		}
	}
	else //画斜线
	{
		k1 = y2 - y1;
		k2 = x2 - x1;
		k = k1 * 10 / k2; //系数放大10倍
		for (i = 0; i < (x2 - x1); i++)
		{
			OLED_DrawPoint(x1 + i, y1 + i * k / 10); //结果缩小10倍
		}
	}
}

4.更新显存到oled

原始资料


在这里插入图片描述


  1. 设置行地址 命令
  2. 设置列低地址 命令
  3. 设置列高地址 命令
  4. 发送数据到SSD1306 数据
//更新显存到OLED
void OLED_Refresh(void)
{
	u8 i, n;
	for (i = 0; i < 8; i++)
	{
	    OLED_WR_Byte(0xb0 + i, OLED_CMD); //设置行起始地址
		OLED_WR_Byte(0x00, OLED_CMD);	  //设置 低 列起始地址
		OLED_WR_Byte(0x10, OLED_CMD);	  //设置 高 列起始地址
		for (n = 0; n < 128; n++)
			OLED_WR_Byte(OLED_GRAM[n][i], OLED_DATA);//发送数据到SSD1306
	}
}

5.显示单个字符

  1. 代码的数据是按照下图方式生成数据数据例如1212的数据,字符显示出来是612
  2. 数据显示就是先把一个的一列像素显示完,再显示第二列
  3. 子模数据是字节的高位就是 像素的上方,例如0b 1001 1100 从上到下依次是 亮灭灭亮 亮亮灭灭

字模生成

在这里插入图片描述


补充数据

在这里插入图片描述

//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//
//取模方式 逐列式
/*
	X		写入的X位置 横向x 0-127 
	Y		写入的Y的位置 竖直方向 0-63
	chr		待显示字符
	size1	字符大小  size:选择字体 12/16/24

代码里的数据
{0x00,0x00,0x00,0x00,0x3F,0x40,0x00,0x00,0x00,0x00,0x00,0x00}, // "!",1
oled 从上至下显示

000
000
001
001
001
001
001
001

000
001
000
000
000
000
000
000

自己生成的数据
//0x00H 0x00H 0x00H 0x00H 0x3EH 0x40H 0x00H 0x00H 0x00H 0x00H 0x00H 0x00H;"!",0
*/

void OLED_ShowChar(u8 x, u8 y, u8 chr, u8 size1)
{
	u8 i, m, temp, size2, chr1;
	u8 y0 = y;
	size2 = (size1 / 8 + ((size1 % 8) ? 1 : 0)) * (size1 / 2); //得到一个字符对应点阵集所占的 字节数 12-> 12   16->16   24->36 
	chr1 = chr - ' ';										   //计算偏移后的值
	for (i = 0; i < size2; i++) //循环处理子模的每一个自己,16*8 的子模就有16个字节
	{
		if (size1 == 12)
		{
			temp = asc2_1206[chr1][i];
		} //调用1206字体
		else if (size1 == 16)
		{
			temp = asc2_1608[chr1][i];  //竖直方向16个点,横向8个点
		} //调用1608字体
		else if (size1 == 24)
		{
			temp = asc2_2412[chr1][i];
		} //调用2412字体
		else
			return;
		for (m = 0; m < 8; m++) //一个字节8个bit 所以就是分别更新8个点
		{
			//循环处理每一个bit,先处理高位
			if (temp & 0x80) 
				OLED_DrawPoint(x, y);
			else
				OLED_ClearPoint(x, y);
			temp <<= 1;

			//处理竖直方向依次需要写的点数
			y++;
			if ((y - y0) == size1) 
			{
				y = y0;
				x++;
				break;
			}
		}
	}
}

5. 滚动显示

  1. 建立一个缓冲区,这个缓冲区可以预存一个字符的子模空间
  2. 每次从从右至左每次左移1列像素
  3. 当移到宽度到达一个子模的宽度16列时将下一个子模数据写入缓冲器
  4. 当需要显示的内容循环跟新完成以后,中间的间隔就一次填充 0 (就不显示 黑屏)
//num 显示汉字的个数
//space 每一遍显示的间隔 144 = 128+16 16就是一个中文子模的宽度
void OLED_ScrollDisplay(u8 num, u8 space)
{
	u8 i, n, t = 0, m = 0, r;
	//m 管理移动的列数
	// t 管理字符的索引
	while (1)
	{
		if (m == 0)
		{
			OLED_ShowChinese(128, 24, t, 16); //写入一个汉字保存在OLED_GRAM[][]数组中
			t++;
		}
		if (t == num) //t 表示的汉字序号 所有的汉字已经显示完毕 
		{
			for (r = 0; r < 16 * space; r++) //显示间隔 会在这个循环里一直更新,知道间隔时间到
			{
				for (i = 1; i < 144; i++)
				{
					for (n = 0; n < 8; n++)
					{
						OLED_GRAM[i - 1][n] = OLED_GRAM[i][n];
					}
					OLED_GRAM[143][n]=0; //高位一直填写0,就是不亮
				}
				OLED_Refresh();
			}

			t = 0;//更新显示数据
			m=15; //立即跟新
		}
		 m++;
		if (m == 16) //在一个字显示完成以后就在显示下一个子 例如 中 全部显出来以后就显示 景 
		{
			m = 0;
		}
		//if(t==11){t=0;m=0;}
		for (i = 1; i < 144; i++) //实现左移 
		{
			for (n = 0; n < 8; n++)
			{
				OLED_GRAM[i - 1][n] = OLED_GRAM[i][n];  //144*8
			}
			OLED_GRAM[143][n]=0;
		}
		OLED_Refresh();
		//delay_ms(500*2);
	}
}

Guess you like

Origin blog.csdn.net/u010261063/article/details/119790550