基于STM32的0.96寸OLED显示屏显示数据

一、SPI

1.简介

SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设备接口,是一种高速全双工的通信总线。它被广泛地使用在 ADC、LCD 等设备与 MCU 间,要求通讯速率较高的场合。

2.SPI 的物理层和协议层

2.1物理层

SPI 通讯设备之间的常用连接方式见图
基于STM32的0.96寸OLED显示屏显示数据_第1张图片
SPI 通讯使用 3 条总线及片选线,3 条总线分别为 SCK、MOSI、MISO,片选线为SS,它们的作用介绍如下。

SS( Slave Select):从设备选择信号线,常称为片选信号线,也称为 NSS、CS,以下用 NSS 表示。当有多个 SPI从设备与 SPI主机相连时,设备的其它信号线 SCK、MOSI及 MISO同时并联到相同的 SPI总线上,即无论有多少个从设备,都共同只使用这 3条总线;而每个从设备都有独立的这一条 NSS 信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。I2C 协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;而 SPI 协议中没有设备地址,它使用 NSS 信号线来寻址,当主机要选择从设备时,把该从设备的 NSS 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI通讯。所以 SPI通讯以 NSS 线置低电平为开始信号,以 NSS 线被拉高作为结束信号。

SCK (Serial Clock):时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如 STM32 的 SPI 时钟频率最大为fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。

MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。

MISO(Master Input,,Slave Output):主设备输入/从设备输出引脚。主机从这条信号线读入数据,从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。

2.2协议层

2.2协议层
与 I2C 的类似,SPI 协议定义了通讯的起始和停止信号、数据有效性、时钟同步等环节。

SPI 通讯的通讯时序,如图所示:
基于STM32的0.96寸OLED显示屏显示数据_第2张图片

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

停止信号:NSS 信号由低变高,是 SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。

数据有效性:SPI 使用 MOSI 及 MISO 信号线来传输数据,使用 SCK 信号线进行数据同步。MOSI 及MISO 数据线在 SCK 的每个时钟周期传输一位数据,且数据输入输出是同时进行的。数据传输时,MSB 先行或 LSB先行并没有作硬性规定,但要保证两个 SPI 通讯设备之间使用同
样的协定,一般都会采用图中的 MSB 先行模式。

MOSI 及 MISO 的数据在 SCK 的上升沿期间变化输出,在SCK 的下降沿时被采样。即在 SCK 的下降沿时刻,MOSI 及 MISO 的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。在其它时刻,数据无效,MOSI及MISO 为下一次表示数据做准备。

SPI 每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制。

CPOL/CPHA 及通讯模式:SPI 一共有四种通讯模式,它们的主要区别是总线空闲时 SCK 的时钟状态以及数据采样时刻。

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

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

基于STM32的0.96寸OLED显示屏显示数据_第3张图片

3.通讯引脚

SPI 的所有硬件架构都从图 25-5 中左侧 MOSI、MISO、SCK 及 NSS 线展开的。STM32芯片有多个 SPI 外设,它们的 SPI 通讯信号引出到不同的 GPIO 引脚上,使用时必须配置到这些指定的引脚,见表 25-2。关于 GPIO 引脚的复用功能,可查阅《STM32F10x 规格书》,以它为准。
基于STM32的0.96寸OLED显示屏显示数据_第4张图片
其中 SPI1 是 APB2 上的设备,最高通信速率达 36Mbtis/s,SPI2、SPI3 是 APB1 上的设备,最高通信速率为 18Mbits/s。除了通讯速率,在其它功能上没有差异。其中 SPI3 用到了下载接口的引脚,这几个引脚默认功能是下载,第二功能才是 IO 口,如果想使用 SPI3 接口,则程序上必须先禁用掉这几个 IO 口的下载功能。一般在资源不是十分紧张的情况下,这几个 IO 口是专门用于下载和调试程序,不会复用为 SPI3。

二、使用0.96寸OLED显示屏显示数据

1.显示屏介绍

产品参数
工作电压: 3.3V/5V
通信接口: 3-wire SPI, 4-wire SPI, I2C
屏幕类型: OLED
控制芯片: SSD1306
分辨率: 12864(Pixel)
外形尺寸: 12864(Pixel)
显示颜色: 黄蓝(双色块屏)
工作温度: -20°C ~ 70°C
存储温度: -30°C ~ 80°C
视角: >160°
基于STM32的0.96寸OLED显示屏显示数据_第5张图片
功能引脚:
基于STM32的0.96寸OLED显示屏显示数据_第6张图片
硬件配置:
OLED模块提供三种驱动接口:分别为3-wire SPI、4-wire SPI 和I2C接口,模块背面有两个可选择焊接的电阻, 通过电子的选择来选择相应的通信方式, 如图:
基于STM32的0.96寸OLED显示屏显示数据_第7张图片
模块出厂默认使用4线SPI通信模式, 即BS0,BS1默认接0(1和0不全代表电平,只是电阻接上或者接下的焊接方式,具体硬件链接见下表):
注:上图为硬件上的焊接, 下表为实际的硬件连接
在这里插入图片描述

2.题目要求

理解OLED屏显和汉字点阵编码原理,使用STM32F103的SPI或IIC接口实现以下功能:

  1. 显示自己的学号和姓名;

  2. 显示AHT20的温度和湿度;

  3. 上下或左右的滑动显示长字符,比如“Hello,欢迎来到重庆交通大学物联网205实训室!”或者一段歌词或诗词(最好使用硬件刷屏模式)。

三、显示学号和姓名

1.工程建立

运行厂家给出的Demo程序
打开压缩包找到符合自己芯片的项目工程,使用keil5打开项目
打开程序后用keil进入工程,在工程中找到test.c函数,改写如下代码:
test.c:

void TEST_MainPage(void)
{	
	GUI_ShowCHinese(28,20,16,"王之源",1);      //中文姓名
	GUI_ShowString(4,48,"632007060309",16,1);  //数字详细
	delay_ms(1500);		
	delay_ms(1500);
}

根据题目要求需要输出学号姓名,我们在test.c中编写我们想要输出的内容,因为我们所用到的字模是16×16,所以看到我们这里三个括号内的参数是16,在中文的输出前面是GUI_ShowCHinese,这里需要注意。

存储代码,就是我们的字模库,我们要显示输出相关内容我们的库中必须有相应的字模数据,这里我用到的汉字点阵字模生成工具界面如下:
基于STM32的0.96寸OLED显示屏显示数据_第8张图片
得到相应的字模后我们在oledfont.h中的16×16中加入如下字模:

oledfont.h:

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

//宋体
//16*16大小
//逐行式,顺向(高位在前)
const typFNT_GB16 cfont16[] = 
{
"王",0x00,0x00,0x7F,0xFC,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x3F,0xF8,
0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xFF,0xFE,0x00,0x00,/*"王",0*/
"之",0x02,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x7F,0xF8,0x00,0x10,0x00,0x20,0x00,0x40,
0x00,0x80,0x01,0x00,0x02,0x00,0x04,0x00,0x18,0x00,0x24,0x00,0x43,0xFE,0x00,0x00,/*"之",1*/
"源",0x00,0x00,0x27,0xFE,0x14,0x20,0x14,0x40,0x85,0xFC,0x45,0x04,0x45,0xFC,0x15,0x04,
0x15,0xFC,0x25,0x24,0xE4,0x20,0x24,0xA8,0x29,0x24,0x2A,0x22,0x30,0xA0,0x00,0x40,/*"源",2*/

};

main.c函数:

int main(void)
{	
	delay_init();	    	       //延时函数初始化	  
	NVIC_Configuration(); 	   //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 	
	OLED_Init();			         //初始化OLED  
	OLED_Clear(0);             //清屏(全黑)
	while(1) 
	{	
		TEST_MainPage();         //主界面显示测试
	}
}

2.编译测试

在这里插入图片描述

3.电路连接

基于STM32的0.96寸OLED显示屏显示数据_第9张图片

4.结果

基于STM32的0.96寸OLED显示屏显示数据_第10张图片

四、滚动显示长字符

1.代码改写

基于上一项目进行改写
main.c:

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(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);        //虚拟字节
	TEST_MainPage();
	OLED_WR_Byte(0x2F,OLED_CMD);        //开启滚动
}

test.c显示程序:

void TEST_MainPage(void)
{	
	GUI_ShowCHinese(10,20,16,"欢迎来吃老八秘制小汉堡",1);
	delay_ms(1500);		
	delay_ms(1500);
}

字模库添加:

    "欢",0x00,0x80,0x00,0x80,0xFC,0x80,0x04,0xFC,0x05,0x04,0x49,0x08,0x2A,0x40,0x14,0x40,
    0x10,0x40,0x28,0xA0,0x24,0xA0,0x45,0x10,0x81,0x10,0x02,0x08,0x04,0x04,0x08,0x02,/*"欢",0*/
    "迎",0x00,0x00,0x20,0x80,0x13,0x3C,0x12,0x24,0x02,0x24,0x02,0x24,0xF2,0x24,0x12,0x24,
    0x12,0x24,0x12,0xB4,0x13,0x28,0x12,0x20,0x10,0x20,0x28,0x20,0x47,0xFE,0x00,0x00,/*"迎",0*/
    "来",0x01,0x00,0x01,0x00,0x01,0x00,0x7F,0xFC,0x01,0x00,0x11,0x10,0x09,0x10,0x09,0x20,
    0xFF,0xFE,0x03,0x80,0x05,0x40,0x09,0x20,0x31,0x18,0xC1,0x06,0x01,0x00,0x01,0x00,/*"来",0*/
    "吃",0x00,0x80,0x00,0x80,0x79,0x00,0x49,0xFE,0x4A,0x00,0x4C,0x00,0x49,0xF8,0x48,0x08,
    0x48,0x10,0x48,0x60,0x78,0x80,0x49,0x00,0x02,0x02,0x02,0x02,0x01,0xFE,0x00,0x00,/*"吃",0*/
    "老",0x02,0x00,0x02,0x08,0x3F,0xD0,0x02,0x20,0x02,0x40,0xFF,0xFE,0x01,0x00,0x02,0x00,
	0x0C,0x10,0x18,0xE0,0x2F,0x00,0x48,0x08,0x88,0x08,0x08,0x08,0x07,0xF8,0x00,0x00,/*"老",9*/
	"八",0x00,0x00,0x00,0x40,0x04,0x40,0x04,0x40,0x04,0x40,0x04,0x40,0x04,0x40,0x04,0x20,
	0x08,0x20,0x08,0x20,0x08,0x10,0x10,0x10,0x10,0x08,0x20,0x08,0x20,0x04,0x40,0x02,/*"八",9*/
	"秘",0x08,0x00,0x1C,0x40,0xF0,0x20,0x10,0x28,0x10,0x08,0xFC,0x88,0x10,0x90,0x3A,0x94,
	0x36,0xA2,0x52,0xA2,0x54,0xC2,0x90,0x88,0x11,0x88,0x12,0x88,0x14,0x78,0x10,0x00,/*"秘",9*/
	"制",0x04,0x04,0x24,0x04,0x24,0x04,0x3F,0xA4,0x44,0x24,0x04,0x24,0xFF,0xE4,0x04,0x24,
	0x04,0x24,0x3F,0xA4,0x24,0xA4,0x24,0xA4,0x26,0x84,0x25,0x04,0x04,0x14,0x04,0x08,/*"制",9*/
	"小",0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x09,0x30,0x19,0x10,
	0x21,0x08,0x41,0x0c,0x81,0x04,0x01,0x00,0x01,0x00,0x01,0x00,0x05,0x00,0x02,0x00,/*"小",9*/
	"汉",0x00,0x00,0x27,0xF8,0x12,0x08,0x12,0x08,0x82,0x08,0x41,0x10,0x49,0x10,0x09,0x10,
	0x10,0xA0,0x10,0xA0,0xE0,0x40,0x20,0x40,0x20,0xA0,0x21,0x10,0x22,0x08,0x0C,0x06,/*"汉",9*/
    "堡",0x08,0x00,0x0B,0xF8,0x12,0x08,0x33,0xF8,0x50,0x40,0x97,0xFC,0x11,0x50,0x12,0x48,
    0x14,0x46,0x11,0x40,0x01,0x00,0x3F,0xF8,0x01,0x00,0x01,0x00,0xFF,0xFE,0x00,0x00,/*"堡",0*/

2.编译执行

在这里插入图片描述

3.烧录测试

基于STM32的0.96寸OLED显示屏显示数据_第11张图片

4.结果

五、温湿度显示

1.下载模板

代码模板:https://github.com/Sunlight-Dazzling/stm32_AHT20_OLED/tree/main/User/usart

2.代码修改

0x03,0x80,0x1C,0x70,0xE0,0x0E,0x1F,0xF0,0x10,0x10,0x10,0x10,0x1F,0xF0,0x10,0x10,/*"客",0*/
	"温",0x00,0x00,0x23,0xF8,0x12,0x08,0x12,0x08,0x83,0xF8,0x42,0x08,0x42,0x08,0x13,0xF8,
  0x10,0x00,0x27,0xFC,0xE4,0xA4,0x24,0xA4,0x24,0xA4,0x24,0xA4,0x2F,0xFE,0x00,0x00,/*"温",0*/
	"度",0x01,0x00,0x00,0x80,0x3F,0xFE,0x22,0x20,0x22,0x20,0x3F,0xFC,0x22,0x20,0x22,0x20,
  0x23,0xE0,0x20,0x00,0x2F,0xF0,0x24,0x10,0x42,0x20,0x41,0xC0,0x86,0x30,0x38,0x0E,/*"度",0*/
	"湿",0x00,0x00,0x27,0xF8,0x14,0x08,0x14,0x08,0x87,0xF8,0x44,0x08,0x44,0x08,0x17,0xF8,
  0x11,0x20,0x21,0x20,0xE9,0x24,0x25,0x28,0x23,0x30,0x21,0x20,0x2F,0xFE,0x00,0x00,/*"湿",0*/
	"显",0x00,0x00,0x1F,0xF0,0x10,0x10,0x10,0x10,0x1F,0xF0,0x10,0x10,0x10,0x10,0x1F,0xF0,
  0x04,0x40,0x44,0x44,0x24,0x44,0x14,0x48,0x14,0x50,0x04,0x40,0xFF,0xFE,0x00,0x00,/*"显",0*/
	"示",0x00,0x00,0x3F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFE,0x01,0x00,
  0x01,0x00,0x11,0x10,0x11,0x08,0x21,0x04,0x41,0x02,0x81,0x02,0x05,0x00,0x02,0x00,/*"示",0*/

在bsp_i2c.c中修改read_AHT20函数,修改为

void read_AHT20(void)
{
	uint8_t   i;
	for(i=0; i<6; i++)
	{
		readByte[i]=0;
	}

	I2C_Start();

	I2C_WriteByte(0x71);
	ack_status = Receive_ACK();
	readByte[0]= I2C_ReadByte();
	Send_ACK();

	readByte[1]= I2C_ReadByte();
	Send_ACK();

	readByte[2]= I2C_ReadByte();
	Send_ACK();

	readByte[3]= I2C_ReadByte();
	Send_ACK();

	readByte[4]= I2C_ReadByte();
	Send_ACK();

	readByte[5]= I2C_ReadByte();
	SendNot_Ack();
	//Send_ACK();

	I2C_Stop();

	if( (readByte[0] & 0x68) == 0x08 )
	{
		H1 = readByte[1];
		H1 = (H1<<8) | readByte[2];
		H1 = (H1<<8) | readByte[3];
		H1 = H1>>4;

		H1 = (H1*1000)/1024/1024;

		T1 = readByte[3];
		T1 = T1 & 0x0000000F;
		T1 = (T1<<8) | readByte[4];
		T1 = (T1<<8) | readByte[5];

		T1 = (T1*2000)/1024/1024 - 500;

		AHT20_OutData[0] = (H1>>8) & 0x000000FF;
		AHT20_OutData[1] = H1 & 0x000000FF;

		AHT20_OutData[2] = (T1>>8) & 0x000000FF;
		AHT20_OutData[3] = T1 & 0x000000FF;
	}
	else
	{
		AHT20_OutData[0] = 0xFF;
		AHT20_OutData[1] = 0xFF;

		AHT20_OutData[2] = 0xFF;
		AHT20_OutData[3] = 0xFF;
		printf("lyy");
	}
	printf("\r\n");
	
	printf("温度:%d%d.%d",T1/100,(T1/10)%10,T1%10);
	printf("湿度:%d%d.%d",H1/100,(H1/10)%10,H1%10);
	printf("\r\n");
	t=T1/10;
	t1=T1%10;
	a=(float)(t+t1*0.1);
	h=H1/10;
	h1=H1%10;
	b=(float)(h+h1*0.1);
	sprintf(strTemp,"%.1f",a);   //调用Sprintf函数把DHT11的温度数据格式化到字符串数组变量strTemp中  
	sprintf(strHumi,"%.1f",b);    //调用Sprintf函数把DHT11的湿度数据格式化到字符串数组变量strHumi中  
	//printf(strTemp);
	//printf("/r/n");
	GUI_ShowCHinese(16,00,16,"温湿度显示",1);
	GUI_ShowCHinese(16,20,16,"温度",1);
	GUI_ShowString(53,20,strTemp,16,1);
	GUI_ShowCHinese(16,38,16,"湿度",1);
	GUI_ShowString(53,38,strHumi,16,1);
	delay_ms(1500);		
	delay_ms(1500);
	delay_ms(1500);
	delay_ms(1500);	
}

修改main函数:

int main(void)
{	
	delay_init();	    	       //延时函数初始化    	  
	uart_init(115200);	 
	IIC_Init();
		  
	NVIC_Configuration(); 	   //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 	
	OLED_Init();			         //初始化OLED  
	OLED_Clear(0); 
	while(1)
	{
		//printf("温度湿度显示");
		read_AHT20_once();
		OLED_Clear(0); 
		delay_ms(1500);
  }

3.烧录运行

4.结果展示

基于STM32的0.96寸OLED显示屏显示数据_第12张图片

你可能感兴趣的:(stm32,单片机,嵌入式硬件)