摘要
本文分别通过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)。
接口信号
- (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_Byte
、OLED_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写入等有了更好的认识。收获很大,也十分有趣。