玩转oled屏(基于SPI协议)

玩转OLED屏

  • 一、简介
    • (一)SPI协议简介
    • (二)OLED简介
  • 二、OLED滚动显示长字符
    • (一)常用OLED滚屏命令
      • 1.水平左/右移
      • 2.垂直和水平移动
    • (二)取字模
    • (三)OLED屏滑动演示
  • 三、OLED显示温湿度
  • 总结


一、简介

(一)SPI协议简介

玩转oled屏(基于SPI协议)_第1张图片
通讯开始/停止

  • 标号①处,NSS信号线由高变低,是SPI通讯的起始信号。NSS是每个从机各自独占的信号线,当从机检在自己的NSS线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。

  • 在图中⑥的标号处,NSS信号由低变高,是SPI通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。

数据有效传输

  • SPI使用MOSI及MISO信号线来传输数据,使用SCK信号线来进行数据同步。

  • MOSI及MISO数据线在SCK的每个时钟周期传输一位数据,且数据输入输出是同时进行的。

玩转oled屏(基于SPI协议)_第2张图片
图片来源:SPI协议详解(图文并茂+超详细)

从上图可以看到传输数据0X53时,数据是一位一位开始传输的,并且在时钟的上升沿采集数据。

时钟极性与时钟周期

时钟极性CPOL是指SPI通讯设备处于空闲状态时,SCK信号线的电平信号(即SPI通讯开始前、 NSS线为高电平时SCK的状态)。CPOL=0时, SCK在空闲状态时为低电平,CPOL=1时,则相反。

时钟相位CPHA是指数据的采样的时刻,当CPHA=0时,MOSI或MISO数据线上的信号将会在SCK时钟线的“奇数边沿”被采样。当CPHA=1时,数据线在SCK的“偶数边沿”采样。

玩转oled屏(基于SPI协议)_第3张图片

玩转oled屏(基于SPI协议)_第4张图片

(二)OLED简介

OLED(OrganicLight-Emitting Diode),又称为有机电激光显示、有机发光半导体(OrganicElectroluminesence Display,OLED)。OLED属于一种电流型的有机发光器件,是通过载流子的注入和复合而致发光的现象,发光强度与注入的电流成正比。OLED在电场的作用下,阳极产生的空穴和阴极产生的电子就会发生移动,分别向空穴传输层和电子传输层注入,迁移到发光层。当二者在发光层相遇时,产生能量激子,从而激发发光分子最终产生可见光。

实物图
玩转oled屏(基于SPI协议)_第5张图片
模块引脚介绍

玩转oled屏(基于SPI协议)_第6张图片
有关0.96寸OLED显示屏更多介绍,可参考下面链接

http://www.lcdwiki.com/zh/0.96inch_SPI_OLED_Module

标准例程接线方式

玩转oled屏(基于SPI协议)_第7张图片

二、OLED滚动显示长字符

(一)常用OLED滚屏命令

1.水平左/右移

玩转oled屏(基于SPI协议)_第8张图片

  OLED_WR_Byte(0x2E,OLED_CMD);        //关闭滚动
  OLED_WR_Byte(0x27,OLED_CMD);        //水平滚动 26(从左向右滑动)  /  27(从右向左滑动)
  OLED_WR_Byte(0x00,OLED_CMD);        //虚拟字节
  OLED_WR_Byte(0x00,OLED_CMD);        //起始页 0
  OLED_WR_Byte(0x07,OLED_CMD);        //滚动时间间隔
  OLED_WR_Byte(0x07,OLED_CMD);        //终止页 7
  OLED_WR_Byte(0x00,OLED_CMD);        //虚拟字节
  OLED_WR_Byte(0xFF,OLED_CMD);        //虚拟字节                
  OLED_WR_Byte(0x2F,OLED_CMD);        //开启滚动

注意:此指令用于配置水平滚动参数和确定滚动起始页、终止页和滚动速度
水平滚动需要在调用此命令前禁用(2Eh), 否则RAM中的内容将会出错。

2.垂直和水平移动

玩转oled屏(基于SPI协议)_第9张图片

OLED_WR_Byte(0x2e,OLED_CMD);        //关闭滚动
OLED_WR_Byte(0x2a,OLED_CMD);        //垂直和水平滚动 29(垂直向上,水平向右)/2a(垂直向上,水平向左)
OLED_WR_Byte(0x00,OLED_CMD);        //虚拟字节
OLED_WR_Byte(0x00,OLED_CMD);        //起始页 0
OLED_WR_Byte(0x07,OLED_CMD);        //滚动时间间隔
OLED_WR_Byte(0x07,OLED_CMD);        //终止页 1
OLED_WR_Byte(0x01,OLED_CMD);        //垂直滚动偏移量
OLED_WR_Byte(0x2F,OLED_CMD);        //开启滚动

此指令用于配置垂直和水平滚动参数和确定滚动起始页、终止页、滚动速度和垂直滚动偏移。

OLED_WR_Byte(0x01,OLED_CMD);        //垂直滚动偏移量

注意:如果垂直滚动偏移为0, 那么只将发生水平滚动(和命令26/27h一样)
滚动需要在调用此命令前禁用(2Eh), 否则RAM中的内容将会出错

(二)取字模

对想要显示的字体进行取模

取模选项
玩转oled屏(基于SPI协议)_第10张图片
修改相应的格式如下并添加到对应程序中去


"山",0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x21,0x08,0x21,0x08,0x21,0x08,0x21,0x08
0x21,0x08,0x21,0x08,0x21,0x08,0x21,0x08,0x21,0x08,0x3F,0xF8,0x00,0x08,0x00,0x00/*"山",0*/

"有",0x02,0x00,0x02,0x00,0xFF,0xFE,0x04,0x00,0x04,0x00,0x0F,0xF0,0x08,0x10,0x18,0x10
0x2F,0xF0,0x48,0x10,0x88,0x10,0x0F,0xF0,0x08,0x10,0x08,0x10,0x08,0x50,0x08,0x20/*"有",1*/

"木",0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x7F,0xFC,0x03,0x80,0x05,0x40,0x05,0x40
0x09,0x20,0x11,0x10,0x21,0x08,0x41,0x04,0x81,0x02,0x01,0x00,0x01,0x00,0x01,0x00/*"木",2*/

"兮",0x04,0x40,0x04,0x40,0x08,0x20,0x08,0x20,0x10,0x10,0x20,0x08,0xDF,0xF6,0x04,0x00
0x08,0x00,0x0F,0xE0,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x01,0x40,0x00,0x80/*"兮",3*/

(三)OLED屏滑动演示

显示数据函数

void TEST_MainPage(void)
{	
	GUI_ShowCHinese(28,20,16,"山有木兮",1);
	GUI_ShowString(4,38,"631904110219",16,1);
	delay_ms(1500);		
	delay_ms(1500);
}

垂直和水平滚动

int main(void)
{	
	delay_init();	    	    //延时函数初始化	  
	NVIC_Configuration(); 	   	//设置NVIC中断分组2:2位抢占优先级,2位响应优先级 	
	OLED_Init();			    //初始化OLED  
	OLED_Clear(0);             //清屏(全黑)

	
	OLED_WR_Byte(0x2e,OLED_CMD);        //关闭滚动
	OLED_WR_Byte(0x2a,OLED_CMD);        //垂直和水平滚动 29(垂直向上,水平向右)/2a(垂直向上,水平向左)
	OLED_WR_Byte(0x00,OLED_CMD);        //虚拟字节
	OLED_WR_Byte(0x00,OLED_CMD);        //起始页 0
	OLED_WR_Byte(0x07,OLED_CMD);        //滚动时间间隔
	OLED_WR_Byte(0x07,OLED_CMD);        //终止页 1
	OLED_WR_Byte(0x01,OLED_CMD);        //垂直滚动偏移量
	TEST_MainPage();                    //测试函数
	OLED_WR_Byte(0x2F,OLED_CMD);        //开启滚动
	while(1)
	{
	
	}

演示效果

水平滑动显示歌词

参考以上方式,修改代码,水平滑动显示歌词

三、OLED显示温湿度

  • main函数
int main(void)
{	
	delay_init();	    	       //延时函数初始化	  
	NVIC_Configuration(); 	   //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 	
	OLED_Init();			         //初始化OLED  
	OLED_Clear(0);             //清屏(全黑)

	while(1) 
	{		
    	TEST_MainPage();      //学号、姓名显示
		OLED_Clear(0); 
        for(i=0;i<5;i++)
        {
            TEST_Menu2();            //AHT20温湿度显示
            delay_ms(1000);
        }
        
        OLED_Clear(0); 
    }
}
  • TEST_Menu2()函数
void TEST_Menu2(void)
{
	u32 CT_data[2];
    volatile int  c1=0,t1=0;    
	srand(123456);
	delay_ms(40);
	AHT20_Read_CTdata(CT_data);       //不经过CRC校验,直接读取AHT20的温度和湿度数据    推荐每隔大于1S读一次
	c1 = CT_data[0]*100*10/1024/1024;    //湿度
    t1 = CT_data[1]*200*10/1024/1024-500; //温度
	
	GUI_DrawLine(0, 10, WIDTH-1, 10,1);
	GUI_DrawLine(WIDTH/2-1,11,WIDTH/2-1,HEIGHT-1,1);
	GUI_DrawLine(WIDTH/2-1,10+(HEIGHT-10)/2-1,WIDTH-1,10+(HEIGHT-10)/2-1,1);
	
	GUI_ShowString(0,1,"2021-11-21",8,1);
	GUI_ShowString(74,1,"Sunday",8,1);
	GUI_ShowString(14,HEIGHT-1-10,"Cloudy",8,1);
	GUI_ShowString(WIDTH/2+1,13,"TEMP",8,1);	//显示温度
	GUI_DrawCircle(WIDTH-20, 25, 1,2);
	GUI_ShowString(WIDTH-15,20,"C",16,1);
	GUI_ShowNum(WIDTH/2+8,20,t1/10,2,16,1);
	GUI_ShowString(WIDTH-41,26,".",8,1);
	GUI_ShowNum(WIDTH-35,20,t1%10,1,16,1);
	
	GUI_ShowString(WIDTH/2+1,39,"HUM",8,1);		//显示湿度
	GUI_ShowNum(WIDTH/2+8,46,c1/10,2,16,1);
	GUI_ShowString(WIDTH-41,52,".",8,1);
	GUI_ShowNum(WIDTH-35,46,c1%10,1,16,1);
	GUI_ShowString(WIDTH-21,46,"%",16,1);
	GUI_DrawBMP(6,16,51,32, BMP5, 1);
	delay_ms(1000);
}

其中AHT20温湿度采集的部分,可参考小编的另一篇博客

AHT20温湿度采集(I2C协议)

演示效果


总结

对比分析I2C与SPI协议,两者都有其优缺点。SPI协议支持全双工、传输速率较高、有很好的扩展性;但是SPI只支持一个主机模式,并且设备越多,所要占用主机的接口就越多。相比而言,I2C用很轻盈的架构实现了多主设备仲裁和设备路由,没有固定的主机,便于转换;但是数据大小限制在8位、传输速度低于SPI。
总的来说,I2C、SPI协议是用于系统内各设备芯片的主流协议,这些协议是如此的优美,以至于我们从系统内的“小协议”到理解系统间的“大协议”是很有帮助的。

以上若有不当之处,敬请指教。

参考

《零死角玩转STM32-基于野火指南者开发板PDF》及系列资料

SSD1306(OLED驱动芯片)指令详解

基于SPI通信方式的OLED显示

SSD1306-0.96寸oled屏-滚动指令介绍

基于STM32的0.96寸OLED屏滚动显示长字符

SPI协议详解(图文并茂+超详细)

你可能感兴趣的:(嵌入式入门,stm32,arm,嵌入式硬件)