基于SPI方式实现OLED屏显

本文详细介绍了如何使用STM32通过SPI协议驱动OLED显示屏,涵盖SPI协议原理、OLED显示屏的定义、优势以及工作模式选择。实验部分包括硬件连接、程序烧录和汉字显示,通过字模软件生成汉字点阵并在OLED上显示。此外,还使用逻辑分析仪分析了SPI协议的工作过程。
摘要由CSDN通过智能技术生成

一、SPI简介

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

1.1物理层

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

SS: 从设备选择信号线,常称为片选信号线,也称为NSS,CS。

  • 每个从设备都有独立的这一条SS信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。I2C协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;
  • 而SPI协议中没有设备地址,它使用SS信号线来寻址,当主机要选择从设备时,把该从设备的SS信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行SPI通讯。
  • 所以SPI通讯以SS线置低电平为开始信号,以SS线被拉高作为结束信号。

SCK: 时钟信号线,用于通讯数据同步。

  • 它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如STM32的SPI时钟频率最大为fpclk/2(选APB1或APB2,最大为42MHz或者21MHz),两个设备之间通讯时,通讯速率受限于低速设备。

MOSI:(Master Output,Slave Input) 主设备输出/从设备输入引脚。

  • 主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。

MISO:(Master Input,Slave Output) 主设备输入/从设备输出引脚。

  • 主机从这条信号线读入数据,从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。

WP:

  • 写保护。低电平有效。

HOLD:

  • 暂停功能,处理比当前任务更重要的任务时,把当前任务暂停,保持当前状态,先处理另一个任务。

1.2协议层

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

在这里插入图片描述

这是一个主机的通讯时序。NSS、SCK、MOSI信号都由主机控制产生,而MISO的信号由从机产生,主机通过该信号线读取从机的数据。 MOSI与MISO的信号只在NSS为低电平的时候才有效,在SCK的每个时钟周期MOSI和MISO传输一位数据。

二、OLED

2.1定义

OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesence Display, OELD)。OLED由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优异之特性,被认为是下一代的平面显示器新兴应用技术。

2.2优势

OLED显示技术具有自发光的特性,采用非常薄的有机材料涂层和玻璃基板,当有电流通过时,这些有机材料就会发光,而且OLED显示屏幕可视角度大,并且能够节省电能,从2003年开始这种显示设备在MP3播放器上得到了应用。

**LCD都需要背光,而OLED不需要,因为它是自发光的。**这样同样的显示,OLED效果要来得好一些。以目前的技术,OLED的尺寸还难以大型化,但是分辨率确可以做到很高。

2.3模块工作模式选择

4种模式通过模块的BS1/BS2设置(通过硬件来设置),BS1/BS2的设置与模块接口模式的关系如表所示:

在这里插入图片描述

实物图:

在这里插入图片描述
ALIENTEK OLED模块默认设置是BS0接GND,BS1和BS2接VCC(8080模式),即使用8080并口方式,如果想要设置成其他的模式,则需要在OLED的背面,用烙铁修改BS0-BS2的设置。

2.4模块特点

ALINETEK的OLED显示模块特点:

  1. 模块有单色和双色两种可选,单色为纯蓝色,而双色则为黄蓝双色。
  2. 尺寸小,显示尺寸为 0.96 寸,而模块的尺寸仅为 27mmx26mm 大小。
  3. 高分辨率,该模块的分辨率为128x64。
  4. 多种接口方式,该模块提供了总共 5 种接口包括:6800、8080 两种并行接口方式、3线或 4 线的穿行 SPI 接口方式、IIC 接口方式(只需要 2 根线就可以控制 OLED 了)。
  5. 不需要高压,直接接 3.3V 就可以工作。

注:勿将模块与5.5V接口相连,否则可能烧坏模块。

在这里插入图片描述
该模块采用 8*2 的 2.54 排针与外部连接,总共有 16 个管脚,在 16 条线中,我们只用了 15条,有一个是悬空的。15 条线中,电源和地线占了 2 条,还剩下 13 条信号线。在不同模式下,我们需要的信号线数量是不同的,在 8080 模式下,需要全部 13 条,而在 IIC 模式下,仅需要2条线就够了!这其中有一条是共同的,那就是复位线 RST(RES),RST 上的低电平,将导致 OLED 复位,在每次初始化之前,都应该复位一下 OLED 模块。

在这里插入图片描述

三、实验过程

3.1实验准备

硬件:stm32开发板,OLED显示屏模块

软件:keil5,串口调试助手

3.2硬件连接

在这里插入图片描述

  • OLED 接入完毕,下面接串口。

3.3程序烧录

3.3.1下载源码

  • 这里使用到的程序是运行厂家给出的 Demo 程序,请下载:
    链接:https://pan.baidu.com/s/1HS33ftk3Pb7nWJRhBTLqUw
    提取码:57x8
  • 解压缩后,在 1-Demo 下选择相应的项目
  • 双击打开 PROJECT 下的工程 OLED.uvprojx 即可。

3.3.2keil设置

  • 由于 Demo 程序是用 J-Link 烧录的,而我是用 ST-Link 烧录,所以需要设置下,其它的烧录方式自行设置。
  • 设置烧录方式

在这里插入图片描述

  • 然后点击 ST-Link 右边的 Settings 设置,将端口设为 SW 。
    在这里插入图片描述
  • 按照下图流程操作。

在这里插入图片描述

3.3.3编译烧录

厂家效果图:
在这里插入图片描述

四、显示汉字

4.1汉字字模

要想在 OLED 上显示英文、数字,可以直接输出显示,但要是想显示中文,就必须要对中文进行编码成点阵。
字模软件下载链接:https://pan.baidu.com/s/1GRe2X3p2ETJJEFwXsnV1sw
提取码:fn8i
打开应用程序后,输入自己想要的汉字,即可。
这里需要将正向的文字左旋 90 °,然后再上下翻转,这样,OLED 上显示的文字才是正向的。
其它汉字可以再继续生成字模即可(注意翻转下)。
在这里插入图片描述

4.2代码编辑

主函数main.c:

#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "gui.h"
#include "test.h"
#include "bsp_i2c.h"
int main(void)
{
   	
	delay_init();	    	       //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	 //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	OLED_Init();			         //初始化OLED
	IIC_Init();                //初始化IIC
	OLED_Clear(0);             //清屏(全黑)
	while(1) 
	{
   
		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(0x01,OLED_CMD);        //终止页 1
		OLED_WR_Byte(0x00,OLED_CMD);        //虚拟字节
		OLED_WR_Byte(0xFF,OLED_CMD);        //虚拟字节
		TEST_ShowMyName();                  //显示文本
		read_AHT20_once();                  //读取温度并显示
		OLED_WR_Byte(0x2F,OLED_CMD);        //开启滚动		
		delay_ms(1500);
		delay_ms(1350);
		/*
		TEST_MainPage();         //主界面显示测试
		OLED_Clear(0); 
		Test_Color();            //刷屏测试
		OLED_Clear(0);
		Test_Rectangular();      //矩形绘制测试
		OLED_Clear(0); 
		Test_Circle();           //圆形绘制测试
		OLED_Clear(0); 
		Test_Triangle();         //三角形绘制测试
		OLED_Clear(0);  
		TEST_English();          //英文显示测试
		OLED_Clear(0); 
		TEST_Number_Character(); //数字和符号显示测试
		OLED_Clear(0); 
		TEST_Chinese();          //中文显示测试
		OLED_Clear(0); 
		TEST_BMP();              //BMP单色图片显示测试
		OLED_Clear(0); 
		TEST_Menu1();            //菜单1显示测试
		OLED_Clear(0); 
		TEST_Menu2();            //菜单2显示测试
		OLED_Clear(0); 
		*/
		
	}
}

温湿度显示AHT20_sys.c函数->bsp_i2.c函数

#include "bsp_i2c.h"
#include "delay.h"
#include "string.h"
#include "gui.h"
#include <stdio.h>

uint8_t   ack_status=0;
uint8_t   readByte[6];

uint32_t  H1=0;  //Humility
uint32_t  T1=0;  //Temperature
uint8_t  t;
u8 *strTemp1;
u8 *strTemp2;
u8 *strTemp3;
u8 *strHumi1;
u8 *strHumi2;
u8 *strHumi3;

uint8_t  AHT20_OutData[4];

/****************
 *初始化 I2C 函数
 ****************/
void IIC_Init(void)
{
   
	GPIO_InitTypeDef GPIO_InitStructure;
	//启用高速 APB (APB2) 外围时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE );	
	
	//GPIO 定义
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	//初始化 SCL(Pin6)高电平
	IIC_SCL=1;
	//初始化 SDA(Pin7)高电平
	IIC_SDA=1;
}

/*********************
 *AHT20 数据操作总函数
 *********************/
void read_AHT20_once(void)
{
   

	//延时 10 微妙
	delay_ms(10);
	
  //传输数据前进行启动传感器和软复位
	reset_AHT20();
	delay_ms(10);
  
	//查看使能位
	init_AHT20();
	delay_ms(10);
	
  //触发测量
	startMeasure_AHT20();
	delay_ms(80);
  
	//读数据
	read_AHT20();
	delay_ms(5);
}


void reset_AHT20(void)
{
   
	//数据传输开始信号
	I2C_Start();
	
	//发送数据
	I2C_WriteByte(0x70);
	//接收 ACK 信号
	ack_status = Receive_ACK();

	//发送软复位命令(重启传感器系统)
	I2C_WriteByte(0xBA);
	//接收 ACK 信号
	ack_status = Receive_ACK();

	//停止 I2C 协议
	I2C_Stop();
}

//0x70 —> 0111 0000 前七位表示 I2C 地址,第八位为0,表示 write
//0xE1 —> 看状态字的校准使能位Bit[3]是否为 1
//0x08 0x00 —> 0xBE 命令的两个参数,详见 AHT20 参考手册
void init_AHT20(void)
{
   
	//传输开始
	I2C_Start();

	//写入 0x70 数据
	I2C_WriteByte(0x70);
	//接收 ACK 信号
	ack_status = Receive_ACK();

	//写入 0xE1 数据
	I2C_WriteByte(0xE1);
	ack_status = Receive_ACK();

	//写入 0x08 数据
	I2C_WriteByte(0x08);
	ack_status = Receive_ACK();

	//写入 0x00 数据
	I2C_WriteByte(0x00);
	ack_status = Receive_ACK();

	//停止 I2C 协议
	I2C_Stop();
}

//0x70 —> 0111 0000 前七位表示 I2C 地址,第八位为0,表示 write
//0xAC —> 触发测量
//0x33 0x00 —> 0xAC 命令的两个参数,详见 AHT20 参考手册
void startMeasure_AHT20(void)
{
   
	//启动 I2C 协议
	I2C_Start();
	
	I2C_WriteByte(0x70);
	ack_status = Receive_ACK();

	I2C_WriteByte(0xAC);
	ack_status = Receive_ACK();

	I2C_WriteByte(0x33);
	ack_status = Receive_ACK();

	I2C_WriteByte(0x00);
	ack_status = Receive_ACK();

	I2C_Stop();
}


void read_AHT20(void)
{
   
	uint8_t i;

	//初始化 readByte 数组
	for(i=0; i<6; i++)
	{
   
		readByte[i]=0;
	}

	I2C_Start();

	//通过发送 0x71 可以获取一个字节的状态字
	I2C_WriteByte(0x71);
	ack_status = Receive_ACK();
	
	//接收 6 个 8 bit的数据
	readByte[0]= I2C_ReadByte();
	//发送 ACK 信号
	Send_ACK();

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

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

	readByte[3]= I2C_ReadByte(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值