模块来源
采购链接:
黄保凯中景园 1.3 寸 OLED 显示屏 12864 液晶屏 sh1106 12864 OLED 模块
资料下载链接:
https://pan.baidu.com/s/1FNGHM0u5MQ2li3QOrTkTuA
资料提取码:8888
规格参数
工作电压: 3.3V ~ 5V
工作电流: 20mA
模块尺寸: 33.5 x 35.4 MM
像素间距:0.23(H) x 0.23(V)
像素尺寸:0.21(H) x 0.21(V)
驱动芯片:SH1106
通信协议:SPI(可调 IIC)
移植过程
我们的目标是将例程移植至开发板上。按照以下步骤,即可完成移植。
- 将源码导入工程;
- 根据编译报错处进行粗改;
- 修改引脚配置;
- 修改时序配置;
- 移植验证。
查看资料
打开厂家资料例程
移植至工程
将厂家资料路径下的【OLED】文件夹,复制到自己的工程中。自己的工程至少需要有毫秒级延时函数。(工程可以参考入门手册空白工程下载)
打开自己的工程,将我们刚刚复制过来的文件导入.c 和.h 文件。
将 oled.h 文件下的 sys.h 改为 stm32f4xx.h
将 oled.c 文件下的 delay.h 改为 board.h
再编译发现还有错误,错误内容分别为标识符“u8”、“u16”、“u32”未定义和找不到 delay.h 这个文件。
分别在 oled.h 文件中定义三个宏,u32、u16 与 u8。
#ifndef u8
#define u8 uint8_t
#endif
#ifndef u16
#define u16 uint16_t
#endif
#ifndef u32
#define u32 uint32_t
#endif
2
3
4
5
6
7
8
9
10
11
再编译发现只剩下 LCD 引脚初始化的内容报错,接下来我们要进行引脚选择。
引脚选择
该屏幕需要设置 8 个接口,具体接口说明见
模块为 SPI 通信协议的从机,SCL 为 SPI 信号线(SCK),SDA 为 SPI 输出线(MOSI),CS 为 SPI 片选线(NSS)。
- 如果 MCU 的 GPIO 引脚不足,可以将屏幕的两个引脚接口不接入 MCU 的 GPIO。
- 将 RES 接入 MCU 的复位引脚,当 MCU 复位时,屏幕也跟着复位;
下面分为软件 SPI 移植与硬件 SPI 移植进行讲解。
软件 SPI 移植
当前厂家源码使用的是软件 SPI 接口,SPI 时序部分厂家已经完成,我们只需要将引脚和延时配置好即可。所以对应接入的屏幕引脚请按照你的需要。这里选择的引脚见
选择好引脚后,进入工程开始编写屏幕引脚初始化代码。
将 oled.c 源代码中的**void OLED_Init(void)**修改为如下代码。
//OLED的初始化
void OLED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PORTA时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|
GPIO_Pin_3|
GPIO_Pin_4|
GPIO_Pin_5|
GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
GPIO_SetBits(GPIOA, GPIO_Pin_2|
GPIO_Pin_3|
GPIO_Pin_4|
GPIO_Pin_5|
GPIO_Pin_7 );
OLED_RES_Clr();
delay_ms(200);
OLED_RES_Set();
OLED_WR_Byte(0xAE,OLED_CMD); /*display off*/
OLED_WR_Byte(0x02,OLED_CMD); /*set lower column address*/
OLED_WR_Byte(0x10,OLED_CMD); /*set higher column address*/
OLED_WR_Byte(0x40,OLED_CMD); /*set display start line*/
OLED_WR_Byte(0xB0,OLED_CMD); /*set page address*/
OLED_WR_Byte(0x81,OLED_CMD); /*contract control*/
OLED_WR_Byte(0xcf,OLED_CMD); /*128*/
OLED_WR_Byte(0xA1,OLED_CMD); /*set segment remap*/
OLED_WR_Byte(0xA6,OLED_CMD); /*normal / reverse*/
OLED_WR_Byte(0xA8,OLED_CMD); /*multiplex ratio*/
OLED_WR_Byte(0x3F,OLED_CMD); /*duty = 1/64*/
OLED_WR_Byte(0xad,OLED_CMD); /*set charge pump enable*/
OLED_WR_Byte(0x8b,OLED_CMD); /* 0x8B 内供 VCC */
OLED_WR_Byte(0x33,OLED_CMD); /*0X30---0X33 set VPP 9V */
OLED_WR_Byte(0xC8,OLED_CMD); /*Com scan direction*/
OLED_WR_Byte(0xD3,OLED_CMD); /*set display offset*/
OLED_WR_Byte(0x00,OLED_CMD); /* 0x20 */
OLED_WR_Byte(0xD5,OLED_CMD); /*set osc division*/
OLED_WR_Byte(0x80,OLED_CMD);
OLED_WR_Byte(0xD9,OLED_CMD); /*set pre-charge period*/
OLED_WR_Byte(0x1f,OLED_CMD); /*0x22*/
OLED_WR_Byte(0xDA,OLED_CMD); /*set COM pins*/
OLED_WR_Byte(0x12,OLED_CMD);
OLED_WR_Byte(0xdb,OLED_CMD); /*set vcomh*/
OLED_WR_Byte(0x40,OLED_CMD);
OLED_Clear();
OLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
将 lcd_init.h 中的 LCD 端口定义 宏,修改为
//-----------------OLED端口定义----------------
/*
SCL = PA5
SDA = PA7
RES = PA3
DC = PA2
CS = PA4
*/
#define OLED_SCL_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_5)//SCL
#define OLED_SCL_Set() GPIO_SetBits(GPIOA,GPIO_Pin_5)
#define OLED_SDA_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_7)//MOSI
#define OLED_SDA_Set() GPIO_SetBits(GPIOA,GPIO_Pin_7)
#define OLED_RES_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_3)//RES
#define OLED_RES_Set() GPIO_SetBits(GPIOA,GPIO_Pin_3)
#define OLED_DC_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_2)//DC
#define OLED_DC_Set() GPIO_SetBits(GPIOA,GPIO_Pin_2)
#define OLED_CS_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_4)//CS
#define OLED_CS_Set() GPIO_SetBits(GPIOA,GPIO_Pin_4)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
将 oled.c 文件中的 void OLED_WR_Byte(u8 dat,u8 cmd)函数修改为:
void OLED_WR_Byte(u8 dat,u8 cmd)
{
u8 i;
if(cmd)
OLED_DC_Set();
else
OLED_DC_Clr();
delay_us(1);
OLED_CS_Clr();
delay_us(1);
for(i=0;i<8;i++)
{
OLED_SCL_Clr();
delay_us(1);
if(dat&0x80)
OLED_SDA_Set();
else
OLED_SDA_Clr();
delay_us(1);
OLED_SCL_Set();
delay_us(1);
dat<<=1;
}
OLED_CS_Set();
OLED_DC_Set();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
到这里软件 SPI 就移植完成了,请移步到 3.4 节进行移植验证。
硬件 SPI 移植
硬件 SPI 与软件 SPI 相比,硬件 SPI 是靠硬件上面的 SPI 控制器,所有的时钟边缘采样,时钟发生,还有时序控制,都是由硬件完成的。它降低了 CPU 的使用率,提高了运行速度。软件 SPI 就是用代码控制 IO 输出高低电平,模拟 SPI 的时序,这种方法通信速度较慢,且不可靠。
想要使用硬件 SPI 驱动屏幕,需要确定使用的引脚是否有 SPI 外设功能。可以通过数据手册进行查看。
数据手册和用户手册都在百度网盘资料,网盘地址看入门手册。
当前使用的是硬件 SPI 接口,而屏幕我们只需要控制它,而不需要读取屏幕的数据,故使用的是 3 线的 SPI,只使用到了时钟线 SCK、主机输出从机输入线 MOSI 和软件控制的片选线 NSS。而 NSS 我们使用的是软件控制,所以除了 SCL(SCK)/SDA(MOSI)引脚需要使用硬件 SPI 功能的引脚外,其他引脚都可以使用开发板上其他的 GPIO。这里选择使用 PA5/PA7 的 SPI 复用功能。其他对应接入的屏幕引脚请按照你的需要。这里选择的引脚见硬件 SPI 接线
选择好引脚后,进入工程开始编写屏幕引脚初始化代码。
引脚初始化配置见如下代码。
//OLED的初始化
void OLED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure1;
GPIO_InitTypeDef GPIO_InitStructure2;
SPI_InitTypeDef SPI_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能PORTA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
/* 设置引脚复用 */
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
/* 配置SPI引脚引脚*/
GPIO_InitStructure1.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7;
GPIO_InitStructure1.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure1.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure1.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure1.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure1);
GPIO_InitStructure2.GPIO_Pin = GPIO_Pin_3|
GPIO_Pin_2|
GPIO_Pin_4;
GPIO_InitStructure2.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure2.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure2.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure2.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOA, &GPIO_InitStructure2);//初始化
GPIO_SetBits(GPIOA, GPIO_Pin_3|
GPIO_Pin_2|
GPIO_Pin_4 );
/* FLASH_SPI 模式配置 */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // 配置为主机
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 8位数据
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; // 极性相位
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // 软件cs
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; // SPI时钟预调因数为2
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位在前
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
/* 使能 SPI1 */
SPI_Cmd(SPI1, ENABLE);
OLED_RES_Clr();
delay_ms(200);
OLED_RES_Set();
OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
OLED_WR_Byte(0x40,OLED_CMD);//--set start line address Set Mapping RAM Display Start Line (0x00~0x3F)
OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register
OLED_WR_Byte(0xCF,OLED_CMD);// Set SEG Output Current Brightness
OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping 0xa0左右反置 0xa1正常
OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction 0xc0上下反置 0xc8正常
OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)
OLED_WR_Byte(0x00,OLED_CMD);//-not offset
OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
OLED_WR_Byte(0x12,OLED_CMD);
OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
OLED_WR_Byte(0x02,OLED_CMD);//
OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7)
OLED_Clear();
OLED_WR_Byte(0xAF,OLED_CMD);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
将 lcd_init.h 中的 LCD 端口定义 宏,修改为
//-----------------OLED端口定义----------------
/*
SCL = PA5
SDA = PA7
RES = PA3
DC = PA2
CS = PA4
*/
#define OLED_SCL_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_5)//SCL
#define OLED_SCL_Set() GPIO_SetBits(GPIOA,GPIO_Pin_5)
#define OLED_SDA_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_7)//MOSI
#define OLED_SDA_Set() GPIO_SetBits(GPIOA,GPIO_Pin_7)
#define OLED_RES_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_3)//RES
#define OLED_RES_Set() GPIO_SetBits(GPIOA,GPIO_Pin_3)
#define OLED_DC_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_2)//DC
#define OLED_DC_Set() GPIO_SetBits(GPIOA,GPIO_Pin_2)
#define OLED_CS_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_4)//CS
#define OLED_CS_Set() GPIO_SetBits(GPIOA,GPIO_Pin_4)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
初始化部分完,还需要修改发送数据部分。源代码中使用的是软件 SPI,时序是由厂家编写完成的。我们使用硬件 SPI 则需要对其进行修改。
在 oled.c 文件中,将源代码的 void OLED_WR_Byte(u8 dat,u8 cmd) 函数修改为
void OLED_WR_Byte(u8 dat,u8 cmd)
{
if(cmd)
OLED_DC_Set();
else
OLED_DC_Clr();
OLED_CS_Clr();
//等待发送缓冲区为空
while(RESET == SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) );
//通过SPI1发送一个字节数据
SPI_I2S_SendData(SPI1, dat);
//等待接收缓冲区不空标志
while(RESET == SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) );
SPI_I2S_ReceiveData(SPI1);
OLED_CS_Set();
OLED_DC_Set();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
到这里硬件 SPI 就移植完成了,请移步到最后一节进行移植验证。
移植验证
在 main.c 中输入代码如下
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:https://oshwhub.com/forum
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
* Change Logs:
* Date Author Notes
* 2024-04-03 LCKFB-LP first version
*/
#include "board.h"
#include "bsp_uart.h"
#include <stdio.h>
#include "oled.h"
int main(void)
{
board_init();
uart1_init(115200U);
OLED_Init(); //初始化OLED
OLED_Clear();
uint32_t t = 0;
while(1)
{
OLED_ShowString(0,0,(uint8_t *)"ABC",8,1);//6*8 “ABC”
OLED_ShowString(0,8,(uint8_t *)"ABC",12,1);//6*12 “ABC”
OLED_ShowString(0,20,(uint8_t *)"ABC",16,1);//8*16 “ABC”
OLED_ShowString(0,36,(uint8_t *)"ABC",24,1);//12*24 “ABC”
OLED_ShowString(50,8,(uint8_t *)"Num:",16,1);//12*24 “ABC”
OLED_ShowNum(90,8,t,3,16,1);
t += 1;
OLED_Refresh();
delay_ms(1000);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
上电效果:
代码下载
链接在开发板介绍
章节的离线资料下载!!