基于SPI/IIC接口的OLED数据显示
目录
一、了解SPI
本部分转载自霁风AI博客:
SPI专题(一)——基础知识
1.什么是SPI
SPI(Serial Peripheral Interface)是串行外围设备接口。是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为 PCB 的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议。常用于单片机和 EEPROM、FLASH、实时时钟、数字信号处理器等器件的通信。
2.特点
① 连接方式
SPI 是一个环形总线结构,由设备选择线(CS)、时钟线(SCK)、串行输出数据线(MOSI)、串行输入数据线(MISO) 构成,其时序其实很简单,主要是在 sck 的控制下,两个双向移位寄存器进行数据交换。
(1)MOSI: 主器件数据输出,从器件数据输入
(2)MISO: 主器件数据输入,从器件数据输出
(3)SCLK : 时钟信号,由主器件产生
(4)SS(CS): 从器件使能信号,由主器件控制
② 模式
SPI 用主从模式(Master Slave)架构;支持多 slave 模式应用,一般仅支持单 Master。时钟由 Master 控制,在时钟移位脉冲下,数据按位传输,高位在前,低位在后(MSB first);SPI 接口有 2 根单向数据线,为全双工通信,目前应用中的数据速率可达几 Mbps 的水平。
主片在访问某一从片时,必须使该从片的片选信号有效;主片在 SCK 信号的同步下,通过 MOSI 线发出指令、地址信息;如需将数据输出,则接着写指令,由 SCK 同步在 MOSI 线上发出数据;如需读回数据,则接着读指令,由主片发出 SCK,从片根据 SCK 的节拍通过 MISO 发回数据。对具有 SPI 接口的从片器件来讲,SCK、MOSI 是输入信号,MISO 是输出信号。
3.内部结构
工作原理:主机和从机内部都有一个串行的移位寄存器,主机向其串行移位寄存器中写入一个字节发起传输,寄存器通过 MOSI 信号线将字节发送到从机,从机也将自己的移位寄存器一个字节发送给主机,从而实现两个移位寄存器的一个字节交换。
4.通信方式
① 通信协议
SPI 是单主设备通信协议,这意味着总线中只有一个中心设备能发起通信,当 SPI 主设备想读/写从设备时,它要 拉低从设备对应的 CS 线(CS 是低电平有效的)。主机和从机都有一个串行移位寄存器,主机通过向它的 SPI 串行寄存器写入一个字节来发起一次传输。寄存器通过 MOSI 信号线将字节传送给从机,实现“写”的功能。从机也将自己的移位寄存器中的内容通过 MISO 信号线返回给主机,实现“读”的功能。这样,两个移位寄存器中的内容就被交换,外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。
② 操作模式
SPI 有四种操作模式,模式0、模式1、模式2、模式3 其中使用的最为广泛的是 SPI0 和 SPI3 方式。它们的区别是定义了在时钟脉冲的哪条边沿转换输出信号,哪条边沿采样输入信号,还有时钟脉冲的稳定电平值(就是时钟信号无效时是高还是低)。每种模式都由参数时钟极性 CPOL 和时钟相位 CPHA 刻画。 时钟极性设置时钟空闲时的电平,时钟相位设置读取数据和发送数据的时钟沿。 通信双方必须是工作在同一模式下,CPOL 与 CPHA 的选项可实现时钟信号 180 度的相移以及半个时钟周期的数据延迟。(CPHA = 0 表示在每个周期的第一个时钟沿采样; CPHA = 1 表示在每个周期的第二个时钟沿采样。)
四种模式如下图:
如图:数据采集的电平
主机和从机的发送数据是同时完成的,两者的接收数据也是同时完成的。所以为了保证主从机正确通信,应使得它们的 SPI 具有相同的时钟极性和时钟相位。因为:其一,主设备 SPI 时钟和极性的配置应该由外设来决定;其二,二者的配置应该保持一致,即主设备的 SDO 同从设备的 SDO 配置一致,主设备的 SDI 同从设备的S DI 配置一致。因为主从设备是在 SCK 的控制下,同时发送和接收数据,并通过 2 个双向移位寄存器来交换数据。
③ 传输时序
四种模式的传输时序如下:
一个完整的传送周期是16位,即两个字节,因为,首先主机要发送命令过去,然后从机根据主机的命令准备数据,主机在下一个8位时钟周期才把数据读回来。如下图:
④ 优缺点
SPI 接口具有如下优点:
(1)支持全双工操作;
(2)操作简单;
(3) 数据传输速率较高。
缺点:
(1) 需要占用主机较多的口线(每个从机都需要一根片选线);
(2)只支持单个主机;
(3) 没有指定的流控制,没有应答机制确认是否接收到数据。
二、使用OLED显示屏显示数据
1.实验器材
软件:keil
硬件:PC机,STM32MINI,0.96寸OLED显示屏,ST_Link
2.硬件连接
3.代码实现
此工程基于STM32例程编写,以实现以下功能:
- 显示自己的学号
- 显示自己的姓名
- 添加汉字字模
使用软件PCtoLCD2002
软件设置如图所示
写入需要显示的字符
这里以:航神我的超人
为例
得到字模
航(0) 神(1) 我(2) 的(3) 超(4) 人(5)
DB 10H 40H 20H 20H 7CH 00H 45H FCH 64H 00H 54H 00H 54H F0H FCH 90H;
DB 44H 90H 64H 90H 54H 90H 54H 92H 44H 92H 45H 12H 55H 0EH 8AH 00H;"航",0
DB 20H 20H 10H 20H 10H 20H F9H FCH 09H 24H 11H 24H 11H 24H 39H FCH;
DB 55H 24H 95H 24H 11H 24H 11H FCH 11H 24H 10H 20H 10H 20H 10H 20H;"神",1
DB 04H 40H 0EH 50H 78H 48H 08H 48H 08H 40H FFH FEH 08H 40H 08H 44H;
DB 0AH 44H 0CH 48H 18H 30H 68H 22H 08H 52H 08H 8AH 2BH 06H 10H 02H;"我",2
DB 10H 40H 10H 40H 20H 40H 7EH 7CH 42H 84H 42H 84H 43H 04H 42H 44H;
DB 7EH 24H 42H 24H 42H 04H 42H 04H 42H 04H 7EH 04H 42H 28H 00H 10H;"的",3
DB 08H 00H 09H FCH 08H 44H 7EH 44H 08H 44H 08H 94H FFH 08H 08H FCH;
DB 28H 84H 28H 84H 2EH 84H 28H FCH 28H 00H 58H 00H 4FH FEH 80H 00H;"超",4
DB 01H 00H 01H 00H 01H 00H 01H 00H 01H 00H 01H 00H 02H 80H 02H 80H;
DB 04H 40H 04H 40H 08H 20H 08H 20H 10H 10H 20H 08H 40H 04H 80H 02H;"人",5
将得到的字模添加进函数
const typFNT_GB16 cfont16[] =
{
"航",0x00,0x00,0x08,0x60,0x10,0x20,0x3E,0x20,0x27,0xFE,0x36,0x00,0x2E,0x00,0x26,0xF8,
0x7E,0xC8,0x26,0xC8,0x36,0xC8,0x2E,0x88,0x26,0x88,0x46,0x8A,0x4D,0x0E,0x43,0x00,/*"航",0*/
"神",0x00,0x00,0x10,0x20,0x08,0x20,0x11,0xFE,0x7D,0x22,0x0D,0x22,0x09,0x22,0x19,0xFE,
0x3D,0x22,0x75,0x22,0x51,0xFE,0x11,0x22,0x11,0x20,0x10,0x20,0x10,0x20,0x10,0x20,/*"神",1*/
"我",0x00,0x00,0x00,0x40,0x07,0x50,0x7C,0x48,0x0C,0x44,0x0C,0x40,0x7F,0xFE,0x0C,0x48,
0x0C,0x44,0x0F,0x4C,0x7C,0x78,0x0C,0x30,0x0C,0x70,0x0C,0xDA,0x1B,0x0E,0x10,0x00,/*"我",3*/
"的",0x00,0x00,0x08,0x20,0x08,0x60,0x18,0x40,0x3E,0x7E,0x22,0xC6,0x22,0x86,0x23,0x86,
0x3E,0x66,0x22,0x24,0x22,0x34,0x22,0x04,0x22,0x04,0x3E,0x04,0x22,0x3C,0x00,0x00,/*"的",4*/
"超",0x00,0x00,0x08,0x00,0x08,0xFE,0x3E,0x26,0x08,0x24,0x08,0x44,0x7F,0xDC,0x0C,0x90,
0x0C,0x7C,0x2F,0x44,0x2D,0x44,0x2C,0x44,0x7C,0x7C,0x4C,0x00,0x47,0xFE,0x00,0x00,/*"超",5*/
"人",0x00,0x00,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,
0x03,0xC0,0x02,0x40,0x06,0x60,0x04,0x30,0x08,0x18,0x30,0x0C,0x60,0x06,0x00,0x00,/*"人",6*/
};
test.c
void TEST_MainPage(void)
{
TEST_Number_Character(); //数字和符号显示
TEST_Chinese();
}
void TEST_Number_Character(void)
{
GUI_ShowString(0,0,"631807030425",8,1); //输出学号
}
void TEST_Chinese(void)
{
GUI_ShowCHinese(16,20,16,"航神我的超人",1); //输出字符
delay_ms(1000);
}
main.c
#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "gui.h"
#include "test.h"
int main(void)
{
delay_init(); //延时函数初始化
NVIC_Configuration(); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
OLED_Init(); //初始化OLED
OLED_Clear(0); //清屏(全黑)
while(1)
{
TEST_MainPage(); //主界面显示测试
OLED_Clear(0);
}
}
4.演示
三、显示AHT20的温湿度
实验器材与硬件连接与前一步相仿
1.代码实现
将上一步中的test.c
代码修改为:
test.c
//温度显示函数
void show_tem(int hem,int tem)
{
GUI_ShowString(8,32,"温度: ",16,1);//显示湿度
GUI_ShowNum(48,32,hem,2,16,1);
GUI_ShowString(64,32,"%",16,1);
GUI_ShowString(8,48,"湿度: ",16,1);//显示温度
GUI_ShowNum(48,48,tem,2,16,1);
GUI_ShowCHinese(64,48,16,"¡æ",1);
}
main.c
#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 hem,tem;
delay_init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
IIC_Init();
AHT20.alive=!AHT20_Init();
OLED_Init();
OLED_Clear(0);
while(1)
{
show_my_num();
show_my_name();
if(AHT20.alive)
{
AHT20.flag = AHT20_ReadHT(AHT20.HT);
hem = AHT20.HT[0]*100*10/1024/1024/10;
tem = (AHT20.HT[1]*200*10/1024/1024-500)/10;
}
show_tem(hem,tem);
delay_ms(9000);
delay_ms(9000);
}
}
2.演示
四、显示滚动字符
1.代码实现
void Roll_display(void)
{
int i,j,m=112,n=0,k=0;
for(j=0;j<1;j++)
for(i=m;i>=-208;i-=1)
{
if(i<m&&i>n)
GUI_ShowFont16(i,22,"重",1);
if(i+16<m&&i+16>n)
GUI_ShowFont16(i+16,22,"庆",1);
if(i+32<m&&i+32>n)
GUI_ShowFont16(i+32,22,"交",1);
if(i+48<m&&i+48>n)
GUI_ShowFont16(i+48,22,"通",1);
if(i+64<m&&i+64>n)
GUI_ShowFont16(i+64,22,"大",1);
if(i+80<m&&i+80>n)
GUI_ShowFont16(i+80,22,"学",1);
if(i+96<m&&i+96>n)
GUI_ShowFont16(i+96,22,"欢",1);
if(i+112<m&&i+112>n)
GUI_ShowFont16(i+112,22,"迎",1);
if(i+128<m&&i+128>n)
GUI_ShowFont16(i+128,22,"您",1);
delay_ms(42);
k++;
if(k==16)
{
OLED_Clear(0);
k=0;
}
}
}
roll.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);
}
显示函数show_roll
void show_roll(void)
{
GUI_ShowCHinese(45,16,16,"重庆交通大学欢迎您",1);
}