文章目录
一、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显示模块特点:
- 模块有单色和双色两种可选,单色为纯蓝色,而双色则为黄蓝双色。
- 尺寸小,显示尺寸为 0.96 寸,而模块的尺寸仅为 27mmx26mm 大小。
- 高分辨率,该模块的分辨率为128x64。
- 多种接口方式,该模块提供了总共 5 种接口包括:6800、8080 两种并行接口方式、3线或 4 线的穿行 SPI 接口方式、IIC 接口方式(只需要 2 根线就可以控制 OLED 了)。
- 不需要高压,直接接 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(