目录
[(2)STM32CubeMX 软件配置](#(2)STM32CubeMX 软件配置)
[🟡️OLED_Write 函数代码实现](#🟡️OLED_Write 函数代码实现)
[🟡️OLED_Init 函数代码补全](#🟡️OLED_Init 函数代码补全)
[(1)OLED 显示问题](#(1)OLED 显示问题)
一、OLED
(1)资源介绍
🔅原理图
在新版蓝桥杯物联网竞赛实训平台中,有一块 0.91 寸的 OLED;
🟢️OLED原理图如图1所示:
图1 OLED电路原理图
通过电路图连接可知,引脚资源配置情况为:
OLED | MCU |
---|---|
SCL | PB6 |
SDA | PB7 |
[表1 引脚资源配置情况] |
🔅驱动原理
在原理图中,模块已板载 SCL 和 SDA 线的上拉电阻 。MCU 的引脚只需配置为开漏输出模式,即可接入模块。当有其他 I2C 从机接入同一 I2C 总线时,由于这里已经带有上拉电阻,所以无需另行外接 I2C 总线的上拉电阻,(在下一章的AT24C02中会提到)简化了系统的硬件连接复杂度。
模块作为 I2C 从机,其地址编码为:0111 10 [SA0] [RW] 。其中,SA0 属于硬件地址选择位,其电平状态直接决定地址编码中的 SA0 取值。当 SA0 引脚外接高电平时,地址中的 SA0 即为 1 ;反之,若 SA0 引脚外接低电平时,地址中的 SA0 则为 0 。模块出厂时,默认 SA0 引脚接低电平,用户如有需求,可自行通过调整模块背面的电阻连接,将 SA0 置为高电平。如此一来,基于地址编码的唯一性限制,++同一 I2C 总线最多能够同时挂接 2 个该型号的 OLED模块++,用户在进行系统扩展时需留意这一特性。
因此,使用 I2C 驱动 OLED时:
- **写地址:**0x78;
- **读地址:**0x79;
⚠️在赛点资源包中,官方提供了 OLED的底层驱动代码,这里不再赘述;但函数
**void OLED_Write(uint8_t ucType, uint8_t ucData)**需要自行补充编写;
驱动原理部分内容参考博客:
(2)STM32CubeMX 软件配置
🔅"工程建立、时钟树配置、Debug 串行线配置、代码生成配置" 在下文中有讲解,这里不再赘述❗️
1️⃣点击左侧 "Connectivity" → 选择 "I2C1" → 模式选择 "I2C" ( I2C总线模式);
⚠️此处模式参数默认,请根据题意或项目需求进行修改;
图2 I2C配置
2️⃣生成代码;
3️⃣将赛点资源数据包中的参考代码:"font.h" 、"oled.h" 、"oled.c" 、"i2c.h" 添加到工程;
(3)代码编写
🟢️main 函数
cpp
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "oled.h"
/* USER CODE END Includes */
/* USER CODE BEGIN PV */
uint8_t puc_oled[17]; // OLED 显示存储区
/* USER CODE END PV */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
/* USER CODE BEGIN 2 */
OLED_Init(); // OLED 初始化
OLED_Clear(); // OLED 清屏,去除初始噪点
sprintf((char*)puc_oled, "123456789ABCDEFG"); // 将需要显示的字符串格式化到显示存储区
OLED_ShowString(0, 0, puc_oled, 16); // 第0行显示
sprintf((char*)puc_oled, " Hello World! ");
OLED_ShowString(0, 2, puc_oled, 16); // 第2行显示
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
(4)实验现象
OLED 一共显示两行,第一行显示字符串 "123456789ABCDEFG" ,第二行显示 "Hello World!";
二、OLED接口函数封装
🟡️OLED_Write函数代码实现
cpp
void OLED_Write(uint8_t ucType, uint8_t ucData)
{
uint8_t pData[2]; // 定义数组存储传输的数据
pData[0] = ucType; // 数据类型:数据 或者 命令
pData[1] = ucData; // 数据本身
HAL_I2C_Master_Transmit(&hi2c1, 0x78, pData, 2, 10); // 调用库函数使用硬件 I2C 进行传输
}
🟡️OLED_Init函数代码补全
⚠️该函数需要添加一句代码,以稳定 OLED:
HAL_Delay(100);
如图3,在 OLED 的芯片手册上也有提到,在上电流程最后,推荐延迟100ms;
cpp
void OLED_Init(void)
{
//OLED_Write(TYPE_COMMAND, 0xA0); /* 段重映射, b0:0,0->0(复位值); */
OLED_Write(TYPE_COMMAND, 0xA1); /* 段重映射, b0:1,0->127 */
OLED_Write(TYPE_COMMAND, 0xA8); /* 复用比 */
OLED_Write(TYPE_COMMAND, 0x1F); /* 0F~3F,16MUX~64MUX(复位值) */
//OLED_Write(TYPE_COMMAND, 0xC0); /* COM扫描方向, b3:0,正常模式(复位值) */
OLED_Write(TYPE_COMMAND, 0xC8); /* COM扫描方向, b3:1,反转模式 */
OLED_Write(TYPE_COMMAND, 0xDA); /* COM引脚配置 */
OLED_Write(TYPE_COMMAND, 0x00); /* D4:0,顺序;1,交替(复位值) */
OLED_Write(TYPE_COMMAND, 0x8D); /* 充电泵 */
OLED_Write(TYPE_COMMAND, 0x14); /* D2:0,禁止(复位值);1,允许 */
OLED_Clear(); /* 清除屏幕 */
OLED_Write(TYPE_COMMAND, 0xAF); /* 开启显示 */
HAL_Delay(100); // 延迟100ms
}
图3 芯片手册
三、踩坑日记
(1)OLED显示问题
⚠️++该 OLED 像素点为128 × 32:++
- Y轴: 显示大小为 16 时,最多显示 2 排;显示大小为 8 时,最多显示 4排;
- X轴: ++最多显示 16个字符++ ,由于sprintf会在字符串后面添加 '\0' ,所以 OLED 显示数组 pucBuf 大小应该为 17;
(2)显示对齐问题
在蓝桥杯赛题上,会对 OLED显示的字符有相对位置要求,如下图所示:
图4 赛题
根据上文对 OLED 的显示分析,一行最多有 16 个字符。那么根据这一特性,我们可以先写入一排大小为 8 的字符串 "123456789ABCDEFH",可以根据此字符串进行居中显示等;
显示偶数个字符时,左右的空位刚好相等,能够满足完全居中;
显示奇数个字符时,左边空位数 = 右边空位数 - 1,满足相对居中;