SPI与I2C接口下的OLED显示

摘要

本文分别通过IIC和SPI两种方式,实现在OLED上显示姓名、学号、温湿度及滚动效果。


导语

前面博客中介绍过IIC这里就不再过多介绍,同样作为另外一种通信方式SPI也同样被广泛应用。这篇文章将介绍在两种方式下在OLED显示内容。


一、SPI简介

SPI( Serial Peripheral Interface-串行外设接口)总线系统是一种同步串行外设接口,它可以使MCU与各种外围设备以串行方式进行通信以交换信息。SPI有三个寄存器分别为: 控制寄存器SPCR,状态寄存器SPSR,数据寄存器SPDR。外围设备包括 FLASHRAN、网络控制器、LCD显示驱动器、A/D转换器和MCU等。SPI总线系统可直接与各个厂家生产的多种标准外围器件直接接口,该接口一般使用4条线:串行时钟线(SCLK)、主机输入/从机输出数据线MISO、主机输出/从机输入数据线MOS和低电平有效的从机选择线NSS(有的SP接口芯片带有中断信号线NT、有的SPI接口芯片没有主机输出/从机输入数据线MOSI)。

img

接口信号

  • (1)MOSI-主器件数据输出,从器件数据输入
  • (2)MISO-主器件数据输入,从器件数据输出
  • (3)SCLK-时钟信号,由主器件产生,最大为 fPCLK2,从模式频率最大为fCPU/2
  • (4)NSS-从器件使能信号,由主器件控制,有的IC会标注为CS( Chip select)
  • 在点对点的通信中,SPI接口不需要进行寻址操作,且为全双工通信,显得简单高效。

与IIC相比

  • 在点对点的通信中,SPI接口不需要进行寻址操作,且为全双工通信,显得简单高效。

  • 在多个从器件的系统中,每个从器件需要独立的使能信号,硬件上比I2C系统要稍微复杂一些。

SPI有四种工作方式

上升沿、下降沿、前沿、后沿触发。还有MSB和LSB传输方式


二、资料获取

店铺资料有卖的,下载相应的资料,选择自己喜欢的OLED,即可,这里我做的就是0.96寸SPI 7针和0.96寸SPI4针。

三、SPI接口实验

1)准备工作

step0:打开工程文件

这个是我已经做好的。

setp1:查看工程下的文件(1)

这两个文件对应的是对应的SPI相关的操作实现与OLED的读入写入函数。

spi.c中定义了下列函数

这三个函数分别用于写入函数、初始化函数、与设置SPI速率

而oled.c函数中包含了如下控制函数

比较重要的就有OLED_WR_ByteOLED_Set_Pixel函数。其中前者定义如下

经过SPI_WriteByte函数往SPI函数里写东西。

setp2:查看工程下的文件(2)

在USER文件夹下,gui.c函数给出了厂家已经定义好的一些现实函数,包括了显示不同大小的函数,显示字符串、汉字、字符的函数。而在test.c里则是写了一些测试的函数

test.c的测试函数不用理会。我们可以在里面加入我们自己的函数,通过调用gui.c里的函数,或者直接操作OLED来实现我们的功能。

step3:生成中文点阵字模,与修改oledfont.c

在正式做之前,必须的一步就是生成字模

在OLED上如果要显示汉字,就必须生成字模,OLED显示的汉字大多都属于点阵汉字,需要按照需求生成字模,调用指针才行,同样,店铺资料可以下载取模软件,在字符取模里选择PCtoLCD2002.exe文件,根据要求设置相应的选项,输入汉字,即可得到想要的文字

操作界面如下:

然后打开oledfont.c文件,这里因为要显示16*16的大小,生成字模就是宋体、16乘16、逐行式、顺向,对应选择相应的选项:

这里有必要介绍一下,例程里的字模库,例程里的字模库通过定义结构体,为字模创建好索引

typedef struct 
{
    
    
	unsigned char Index[2];	
	char Msk[32];
}typFNT_GB16;

再通过font函数里,字模库字模个数,循环查找一一对比,找到即返回,这样做就使得调用时可以直接传递想要显示的汉字,(前提是把需要的汉字全都取了字模,加进去)。

num = sizeof(cfont16)/sizeof(typFNT_GB16);
  for(i=0;i<num;i++)
	{
    
    
		if((cfont16[i].Index[0]==*s)&&(cfont16[i].Index[1]==*(s+1)))

这里我在大小为16的数组里加,因为考虑尽量显示多的内容,就用小号字

这样,就做好了,就可以编写函数了。

2)显示学号姓名

定义show_my_num函数,这里直接根据GUI函数里的show_string函数来写,需要留意,OLED的屏幕为每页8行,每行128个像素点

gui.c中的GUI_ShowString与GUI_ShowCHinese的前两行参数分别为横纵坐标的值,GUI_ShowString的第三个参数和GUI_ShowCHinese的第第四个参数分别是显示内容,最后一个参数为颜色,1白,0黑。而剩下的一个参数对应大小,调用不同font函数。最后代码如下。

/*显示学号代码*/
void show_my_num(void) 
{
    
    
	//GUI_Fill(0,0,WIDTH-1,HEIGHT/2-1,0);
	GUI_ShowString(15,0,"631807030509",16,1);//显示学号
}

/*显示姓名*/
void show_my_name(void)
{
    
    	
	GUI_ShowCHinese(45,16,16,"李卓",1);
}

然后在test.h文件里面做出声明,就可以了。

3)显示温湿度

接着上篇的工程,将相应的文件拷入,添加进去,得到如下:

在test.c下构造如下函数,参数为传递的温湿度。

void show_tem(int c1,int t1)
{
    
    
	GUI_ShowString(8,32,"Hem: ",16,1);
	GUI_ShowNum(48,32,c1,2,16,1);
	GUI_ShowString(64,32,"%",16,1);
	GUI_ShowString(8,48,"Tem: ",16,1);
	GUI_ShowNum(48,48,t1,2,16,1);
	GUI_ShowCHinese(64,48,16,"℃",1);
}

加入声明。

在主函数中计算温度并传递给此函数。具体温度采集可以参考上一篇博客

4)滚动显示

滚动字现实,我用了两种方法,第一种是直接控制硬件刷新效果,第二种就是通过调用函数算法实现。

1、直接控制硬件刷新效果

原理:左右滚动滑动的实现,是使用单独的1KB内存,去处理图片拼接后的图像。而由于刷屏方式比较特殊,左右拼接的方式就是内存移位,而且是Byte与Byte间的移位,这就更方便了。

一般有如下滚动方式:

0x2F:激活滚动        				0x2E:关闭滚动
0x26:水平滚动右向	   				0x27:水平滚动左向
0x29:水平和垂直滚动右向				0x2A:水平和垂直滚动左向
0xA3:垂直滚动

因为函数中已经有如下函数定义,当然也可以直接写SPI_WriteByte(SPI2,dat)

test.c定义如下函数并声明:

//硬件滚动
void roll(void)
{
    
      
	OLED_WR_Byte(0x2F, OLED_CMD);  
	OLED_WR_Byte(0x27, OLED_CMD);   
	OLED_WR_Byte(0x00,OLED_CMD);       
  	OLED_WR_Byte(0x00,OLED_CMD);        
  	OLED_WR_Byte(0x07,OLED_CMD);        
  	OLED_WR_Byte(0x2E,OLED_CMD); 	
  	while(1);
}

这样就能实现滚动了。

这种滚动方式更为平滑,实现也相对简单。目前多采用这种方法。

2、通过编程实现改变坐标来实现滚动

可能刚上手之后不知道有第一种方法,直接想到的就是坐标来实现滚动,这种方法也是可行的。我通过操作GUI_ShowFont16(u8 x,u8 y,u8 *s,u8 mode)来实现了这种方法。但是相对来说就没那么流畅了。

具体代码如下:

void Roll_display(void)
{
    
    
	int i,j;
	for(j=0;j<=1;j++)
		for(i=120;i>=-176;i-=8)
		{
    
    
			if(i<120&&i>0)
				GUI_ShowFont16(i,22,"信",1);
			if(i+16<120&&i+16>0)
				GUI_ShowFont16(i+16,22,"息",1);
			if(i+32<120&&i+32>0)
				GUI_ShowFont16(i+32,22,"科",1);
			if(i+48<120&&i+48>0)	
				GUI_ShowFont16(i+48,22,"学",1);
			if(i+64<120&&i+64>0)
				GUI_ShowFont16(i+64,22,"与",1);
			if(i+80<120&&i+80>0)
				GUI_ShowFont16(i+80,22,"工",1);
			if(i+96<120&&i+96>0)
				GUI_ShowFont16(i+96,22,"程",1);
			if(i+112<120&&i+112>0)	
				GUI_ShowFont16(i+112,22,"学",1);
			if(i+128<120&&i+128>0)
				GUI_ShowFont16(i+128,22,"院",1);
			if(i+144<120&&i+144>0)
				GUI_ShowFont16(i+144,22,"通",1);
			if(i+160<120&&i+160>0)
				GUI_ShowFont16(i+160,22,"信",1);
			if(i+176<120&&i+176>0)
				GUI_ShowFont16(i+176,22,"工",1);
			if(i+192<120&&i+192>0)
				GUI_ShowFont16(i+192,22,"程",1);
			delay_ms(200);
			OLED_Clear(0); //清屏
		}
}

5)主函数

显示姓名、学号、温湿度切屏显示滚动效果

硬件就注释Roll_display();加入roll()即可

#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "gui.h"
#include "test.h"
#include "24cxx.h"	 
#include "AHT20.h"
struct m_AHT20 AHT20;
int main(void)
{
    
    	
	int c1,t1;
	delay_init();	    	       //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	 //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	IIC_Init();							//IIC初始化
	AHT20.alive=!AHT20_Init();	        //AHT20初始化
	OLED_Init();			         //初始化OLED  
	OLED_Clear(0);             //清屏(全黑)
	while(1) 
	{
    
    	
		show_my_num(); //显示学号
		
		show_my_name();//显示姓名
		
		/*读取测量温湿度数据*/
		if(AHT20.alive)//是否存活
		{
    
    
			//读取其原始数据
			AHT20.flag = AHT20_ReadHT(AHT20.HT);
			c1 = AHT20.HT[0]*100*10/1024/1024/10;  //湿度
			t1 = (AHT20.HT[1]*200*10/1024/1024-500)/10;//温度计算公式
		}
		
		show_tem(c1,t1);//显示温湿度
		
		delay_ms(9000);//延时切屏
		delay_ms(9000);//延时切屏
		
		OLED_Clear(0); //清屏
		
		Roll_display();//滚动显示
		
		delay_ms(100);
	}
}

6)接线及演示效果(软硬件)

按照提示接线:

演示图片:


整体效果动图(硬件):
在这里插入图片描述

整体效果动图(软件):

在这里插入图片描述

三、附加:IIC接口实验

区别

IIC介绍,前面博客有提到过,这里不再说了

IIC其实同SPI的实验步骤一样,但是不同的就是字模不再有索引了,定义二维数组,根据顺序读出值。同样倒入字模如下:

根据数组标号进行索引

		OLED_Clear();
		OLED_ShowCHinese(18,0,0);//通
		OLED_ShowCHinese(36,0,1);//信
		OLED_ShowCHinese(54,0,2);//工
		OLED_ShowCHinese(72,0,3);//程

接口:

最终显示(这里没有加温度采集,故没有给与显示)


四、总结

分别做了SPI与IIC的OLED显示实验,实现了显示姓名、学号、滚动等。熟悉了取字模显示中文,等。对SPI写入等有了更好的认识。收获很大,也十分有趣。

猜你喜欢

转载自blog.csdn.net/lee_goi/article/details/111680271