基于STM32_HAL库的IIC通信并驱动OLED屏幕
基于STM32_HAL库的I2C通信并驱动OLED屏幕
文章目录
- 基于STM32_HAL库的I2C通信并驱动OLED屏幕
- 一、I2C总线概述
- 1.1 串口通信的缺点
- 1.2 I2C总线介绍
- 1.3 I2C总线通信过程
- 起始位
- 寻址
- 数据传输
- 停止位
- 1.4 I2C数据传输实例
- 1.5 I2C总线速率模式
- 二、STM32_HAL库 I2C相关API函数
- 2.1 I2C 主机模式下向从设备发送数据函数`HAL_I2C_Master_Transmit()`原型及功能
- 2.2 I2C 主机模式下从从设备接收数据函数`HAL_I2C_Master_Receive()`原型及功能
- 2.3 向具有内部寄存器地址的 I2C 从设备写入数据函数`HAL_I2C_Mem_Write()`原型及功能
- 2.4 从具有内部寄存器地址的 I2C 从设备读取数据函数`HAL_I2C_Mem_Read()`原型及功能
- 三、STM32硬件I2C驱动OLED屏幕
- 3.1 硬件连接
- 3.2 CubeMX配置
- 3.3 代码实现
一、I2C总线概述
1.1 串口通信的缺点
串口可以实现一对一的数据传输,一个串口可以连接一个外部设备,我们的STM32F103C8T6单片机有3个串口,分别是:USART1、USART2、USART3,因此通过串口我们最多可以连接三个外部设备,但是想要连接更多的外部设备该怎么办呢?这个时候就要用到总线。
1.2 I2C总线介绍
I²C(Inter-Integrated Circuit)是一种串行、同步、半双工、主从式通信协议,由飞利浦公司提出,适用于板级通信。
- IIC就是一种总线结构,有了它我们就可以让单片机和大量的外部设备连接,单片机一般作为IIC总线的主机,其他设备作为从机。
- IIC由两条线组成 分别是:SCL和SDA。
- SCL是串行时钟线,负责传输时钟信号,也就是高低变化的方波信号,每一个时钟周期传输一位,所以时钟的频率越高传输的速率就越快。
- SDA是串行数据线,负责传输数据,高电压表示1,低电压表示0。
- 主机和从机都有SDA和SCL引脚,我们需要将他们分别连接在SCL和SDA总线上,另外IIC总线还需要两颗上拉电阻,阻值一般取4.7KΩ,分别接在SCL和SDA上对这两条线数据线进行上拉。
- 不管是主机还是从机它们的SCL和SDA引脚都应该使用开漏输出。
1.3 I2C总线通信过程
下面我们来介绍一下IIC总线的通信过程,我们把IIC数据通信的过程分为四个阶段:起始位、寻址、数据传输、停止位。首先说明一下由于IIC是总线结构所以主机和从机会公共SCL和SDA这两条线,为了加以区别后面在图中我们使用蓝色线代表主机 绿色线代表从机。
起始位
在数据传输开始之前,主机需要向总线上发送起始位,所谓起始位就是在SCL是高电压的时候向SDA发送一个下降沿,在数据传输开始之前总线上处于空闲状态,由于上拉电阻的存在,所以SCL和SDA都是高电压,此时主机只需要将SDA拉低就可以发送一个起始位。
寻址
在寻址阶段主机需要向总线上发送从机的地址,想要和谁通信就发送谁的地址,在IIC总线里每一个从机都有一个地址用来表示它们各自的身份,地址有7位的和10位的,但是如果使用10位地址的话寻址过程就会变得特别复杂,所以这里我们只使用7位地址,假设这些从机地址分别是:0x78、0x7a、0x22、0xf0,它们的寻址过程是什么?
在上一个阶段我们已经发送了起始位,现在开始寻址:首先发送从机的7位地址,比如说我们要寻址的地址是第一个从机0x78,把它的地址转换成二进制01111000,然后把最后以为0丢掉,现在遇到0就发送低电压 遇到1就发送高电压,最后还要发送一个R/W位,R/W用来填写数据传输的方向,其中R - Read代表读,W - Write代表写,#代表低电压有效,所以当R/W = 0的时候就代表向从机写数据,当R/W = 1的时候代表从从机读数据。
发送完从机的地址之后主机释放SDA线等待从机应答,从机通过把SDA拉低来发送一个应答信号,我们把这个应答信号称为ACK,从机发送ACK来告诉主机寻址成功,如果没有从机发送应答信号,我们把这种情况叫做NAK,在寻址阶段出现NAK的原因主要有三点:
- 地址填错,要寻址的地址不存在
- 要寻址的从机正忙,没来得及回复ACK
- 从机故障
但不管是哪种原因只要收到了NAK就表示寻址失败。
数据传输
寻址成功之后进入数据传输阶段,根据上一阶段填写的R/W值,数据传输有两种不同的方向:一种是写 另一种是读,如果是写的话:主机发送从机接收,如果是读的话:从机发送主机接收。
IIC以字节为单位传输数据,每次可以传输多个字节。
先来看写:主机首先向从机发送8bit,也就是一个字节的数据,然后释放掉SDA线等待从机确认接收,从机通过把SDA拉低来发送一个ACK表示数据收到,然后主机再发送第二个字节,以此类推,每次可以发送多个字节。
读数据跟写数据类似,但是方向相反:从机发送数据,主机回复ACK,每读一个字节回复一个ACK。
停止位
最后一个阶段是停止位,在数据传输结束之后,主机需要向总线上发送一个停止位,停止位的发送方式是:当SCL是高电压的时候,向SDA发送一个上升沿。
1.4 I2C数据传输实例
在这个例子里首先主机向从机0x78发送两个字节的数据0x5a和0x33,然后再从从机读取一个字节的数据。
数据传输开始之前总线上处于空闲状态,主机通过把SDA拉低来发送一个起始位,紧接着开始寻址:主机发送地址0x78,R/W位 = 0表示写数据,然后主机释放。SDA等待从机应答,从机0x78通过把SDA拉低来发送一个ACK,此时寻址成功,寻址成功之后主机发送第一个字节0x5a,然后释放SDA等待从机确认接收数据,从机通过把SDA拉低发送ACK,第一个字节传输完成,紧接着主机发送第二个字节0x33,然后释放SDA等待从机应答,从机发送ACK,第二个字节发送成功,两个字节都发送完了此时主机发送停止位,数据传输结束,总线重新进入空闲状态。
接下来主机从从机读取一个字节的数据:主机先发送起始位,然后发送地址0x78,R/W = 1表示方向是读,主机释放SDA,从机发送ACK表示寻址成功,主机从从机读取一个字节的数据,然后从机释放SDA等待主机回复,因为我们不想接收更多的数据,所以发送一个NAK,然后发送一个停止位,数据传输结束。
1.5 I2C总线速率模式
IIC以这种高低变化的电压来传输数据,每个时钟周期传输一位,我们把每秒钟传输位的数量叫做波特率,波特率越大通信速率越快,根据通信速率的快慢IIC总线可以分成多种模式,我们使用这颗单片机只支持:标准模式 和 快速模式,也就是最大通信速度不超过400kbps。
另外在快速模式下我们可以设置时钟信号的占空比,我们知道在IIC总线里主机可以通过SCL向外发送这种方波信号,这个就是时钟信号,在快速模式下它的占空比是可以设置的,分别是:2:1和16:9,2:1表示低电压占两份 高电压占一份,16:9表示低电压占16份 高电压占9份,如果没有特殊说明我们一般选择2:1的占空比。
二、STM32_HAL库 I2C相关API函数
2.1 I2C 主机模式下向从设备发送数据函数HAL_I2C_Master_Transmit()
原型及功能
✅ 功能说明:
该函数用于 I2C 主机模式下向从设备发送数据。发送过程是阻塞式的(即函数在数据传输完成或超时之前不会返回)。
🔣 函数原型:
HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c,uint16_t DevAddress,uint8_t *pData,uint16_t Size,uint32_t Timeout);
📌 参数详解:
参数 | 类型 | 含义 |
---|---|---|
hi2c | I2C_HandleTypeDef* | 指向 I2C 句柄的指针,指定使用哪个 I2C 外设(如 &hi2c1 ) |
DevAddress | uint16_t | 目标设备的地址,7位地址左移1位(如 0x50 → 0xA0) |
pData | uint8_t* | 指向要发送的数据缓冲区 |
Size | uint16_t | 要发送的数据字节数 |
Timeout | uint32_t | 超时时间(以毫秒为单位) |
🔁 返回值:
类型:HAL_StatusTypeDef
返回值 | 含义 |
---|---|
HAL_OK | 发送成功 |
HAL_ERROR | 发生错误 |
HAL_BUSY | I2C 正在忙,未能发送 |
HAL_TIMEOUT | 超时未完成发送 |
💡 使用示例:
uint8_t data[] = {0x01, 0x02}; // 要发送的两个字节
HAL_StatusTypeDef status;status = HAL_I2C_Master_Transmit(&hi2c1, 0x50 << 1, data, 2, 100);if (status == HAL_OK) {// 发送成功
} else {// 错误处理
}
2.2 I2C 主机模式下从从设备接收数据函数HAL_I2C_Master_Receive()
原型及功能
✅ 功能说明:
该函数用于 I2C 主机模式下从从设备接收数据。也是阻塞式的,函数会等待直到接收完成或超时。
🔣 函数原型:
HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c,uint16_t DevAddress,uint8_t *pData,uint16_t Size,uint32_t Timeout);
📌 参数详解:
参数 | 类型 | 含义 |
---|---|---|
hi2c | I2C_HandleTypeDef* | 指向 I2C 句柄的指针 |
DevAddress | uint16_t | 目标从设备的地址(7位左移1位) |
pData | uint8_t* | 用于接收数据的缓冲区 |
Size | uint16_t | 接收的字节数 |
Timeout | uint32_t | 超时时间(单位:毫秒) |
🔁 返回值:
返回值 | 含义 |
---|---|
HAL_OK | 接收成功 |
HAL_ERROR | 出错 |
HAL_BUSY | I2C 正忙 |
HAL_TIMEOUT | 超时 |
💡 使用示例:
uint8_t buffer[10]; // 接收缓冲区
HAL_StatusTypeDef status;status = HAL_I2C_Master_Receive(&hi2c1, 0x50 << 1, buffer, 10, 100);if (status == HAL_OK) {// 接收成功,数据在 buffer 中
} else {// 错误处理
}
2.3 向具有内部寄存器地址的 I2C 从设备写入数据函数HAL_I2C_Mem_Write()
原型及功能
✅ 功能说明:
该函数用于 向具有内部寄存器地址的 I2C 从设备写入数据。常用于写 EEPROM、RTC、温湿度传感器等,数据写入的步骤为:
主机先发送从设备地址 + 内部寄存器地址 + 数据字节。
🔣 函数原型:
HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c,uint16_t DevAddress,uint16_t MemAddress,uint16_t MemAddSize,uint8_t *pData,uint16_t Size,uint32_t Timeout);
📌 参数详解:
参数名 | 类型 | 说明 |
---|---|---|
hi2c | I2C_HandleTypeDef* | 指向 I2C 句柄结构体的指针,指定哪个 I2C 外设(如 &hi2c1 ) |
DevAddress | uint16_t | 从设备地址(7 位地址左移 1 位) |
MemAddress | uint16_t | 要写入的从设备内的寄存器地址 |
MemAddSize | uint16_t | 寄存器地址的字节长度(I2C_MEMADD_SIZE_8BIT 或 I2C_MEMADD_SIZE_16BIT ) |
pData | uint8_t* | 指向要写入的数据缓冲区 |
Size | uint16_t | 要写入的数据长度 |
Timeout | uint32_t | 超时时间(单位:毫秒) |
🔁 返回值(类型:HAL_StatusTypeDef
)
返回值 | 含义 |
---|---|
HAL_OK | 写入成功 |
HAL_ERROR | 写入失败 |
HAL_BUSY | I2C 总线繁忙 |
HAL_TIMEOUT | 超时未完成 |
2.4 从具有内部寄存器地址的 I2C 从设备读取数据函数HAL_I2C_Mem_Read()
原型及功能
✅ 功能说明:
HAL_I2C_Mem_Read()
用于 从具有内部寄存器地址的 I2C 从设备读取数据,常用于 EEPROM、RTC、温湿度传感器等的寄存器读取操作。
主设备会先发送从设备地址和寄存器地址,然后读回寄存器对应的数据。
🔣 函数原型:
HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c,uint16_t DevAddress,uint16_t MemAddress,uint16_t MemAddSize,uint8_t *pData,uint16_t Size,uint32_t Timeout);
📌 参数详解:
参数 | 类型 | 说明 |
---|---|---|
hi2c | I2C_HandleTypeDef* | 指向 I2C 句柄(结构体)的指针(如 &hi2c1 ) |
DevAddress | uint16_t | I2C 从设备的地址,注意需要 左移1位(如 0x50 → 0xA0) |
MemAddress | uint16_t | 要读取的从设备寄存器地址 |
MemAddSize | uint16_t | 寄存器地址长度,使用: I2C_MEMADD_SIZE_8BIT 或 I2C_MEMADD_SIZE_16BIT |
pData | uint8_t* | 指向接收数据的缓冲区 |
Size | uint16_t | 要读取的字节数 |
Timeout | uint32_t | 超时时间(毫秒) |
🔁 返回值(类型:HAL_StatusTypeDef
)
返回值 | 说明 |
---|---|
HAL_OK | 读取成功 |
HAL_ERROR | 错误发生 |
HAL_BUSY | I2C 正忙 |
HAL_TIMEOUT | 超时未完成读取 |
三、STM32硬件I2C驱动OLED屏幕
3.1 硬件连接
3.2 CubeMX配置
-
配置Debug
-
打开RCC配置时钟,将高速时钟配置成Crystal/Ceramic Resonator
-
点击配置时钟选项,把时钟配置成高速外部时钟HSE,并且通过PLLCLK倍频到72MHZ,按下回车之后就为芯片内部的功能分配好了对应的时钟频率。
-
打开左侧的I2C1选项,首先选择I2C,然后设置单片机的I2C为主机功能,并对I2C的速度、频率、还有时钟占空比进行配置。
-
接着对我们的工程进行配置,配置完成之后点击右上角的 GENERATE CODE 生成工程
3.3 代码实现
在实现我们的业务代码之前,我们首先先阅读一下使用CubeMX生成的代码:
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK){Error_Handler();}
}
上面这段代码是使用CubeMX生成的时钟相关的代码。
#include "i2c.h"/* USER CODE BEGIN 0 *//* USER CODE END 0 */I2C_HandleTypeDef hi2c1;/* I2C1 init function */
void MX_I2C1_Init(void)
{/* USER CODE BEGIN I2C1_Init 0 *//* USER CODE END I2C1_Init 0 *//* USER CODE BEGIN I2C1_Init 1 *//* USER CODE END I2C1_Init 1 */hi2c1.Instance = I2C1;hi2c1.Init.ClockSpeed = 400000;hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;hi2c1.Init.OwnAddress1 = 0;hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;hi2c1.Init.OwnAddress2 = 0;hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;if (HAL_I2C_Init(&hi2c1) != HAL_OK){Error_Handler();}/* USER CODE BEGIN I2C1_Init 2 *//* USER CODE END I2C1_Init 2 */}void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if(i2cHandle->Instance==I2C1){/* USER CODE BEGIN I2C1_MspInit 0 *//* USER CODE END I2C1_MspInit 0 */__HAL_RCC_GPIOB_CLK_ENABLE();/**I2C1 GPIO ConfigurationPB6 ------> I2C1_SCLPB7 ------> I2C1_SDA*/GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);/* I2C1 clock enable */__HAL_RCC_I2C1_CLK_ENABLE();/* USER CODE BEGIN I2C1_MspInit 1 *//* USER CODE END I2C1_MspInit 1 */}
}void HAL_I2C_MspDeInit(I2C_HandleTypeDef* i2cHandle)
{if(i2cHandle->Instance==I2C1){/* USER CODE BEGIN I2C1_MspDeInit 0 *//* USER CODE END I2C1_MspDeInit 0 *//* Peripheral clock disable */__HAL_RCC_I2C1_CLK_DISABLE();/**I2C1 GPIO ConfigurationPB6 ------> I2C1_SCLPB7 ------> I2C1_SDA*/HAL_GPIO_DeInit(GPIOB, GPIO_PIN_6);HAL_GPIO_DeInit(GPIOB, GPIO_PIN_7);/* USER CODE BEGIN I2C1_MspDeInit 1 *//* USER CODE END I2C1_MspDeInit 1 */}
}
上面这段代码是使用CubeMX生成的I2C1初始化相关的代码。
#include "main.h"
#include "i2c.h"
#include "gpio.h"/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* OLED屏幕写指令 */
void OLED_Write_Cmd(uint8_t dataCmd)
{// HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT, &dataCmd, sizeof(dataCmd), HAL_MAX_DELAY);
}/* OLED屏幕写数据 */
void OLED_Write_Data(uint8_t dataData)
{HAL_I2C_Mem_Write(&hi2c1, 0X78, 0X40, I2C_MEMADD_SIZE_8BIT, &dataData, sizeof(dataData), HAL_MAX_DELAY);
}/* OLED屏幕初始化 */
void OLED_Init()
{OLED_Write_Cmd(0xAE);OLED_Write_Cmd(0x00);OLED_Write_Cmd(0x10);OLED_Write_Cmd(0x40);OLED_Write_Cmd(0xB0);OLED_Write_Cmd(0x81);OLED_Write_Cmd(0xFF);OLED_Write_Cmd(0xA1);OLED_Write_Cmd(0xA6);OLED_Write_Cmd(0xA8);OLED_Write_Cmd(0x3F);OLED_Write_Cmd(0xC8);OLED_Write_Cmd(0xD3);OLED_Write_Cmd(0x00);OLED_Write_Cmd(0xD5);OLED_Write_Cmd(0x80);OLED_Write_Cmd(0xD8);OLED_Write_Cmd(0x05);OLED_Write_Cmd(0xD9);OLED_Write_Cmd(0xF1);OLED_Write_Cmd(0xDA);OLED_Write_Cmd(0x12);OLED_Write_Cmd(0xDB);OLED_Write_Cmd(0x30);OLED_Write_Cmd(0x8D);OLED_Write_Cmd(0x14);OLED_Write_Cmd(0xAF);
}/* OLED屏幕清屏 */
void OLED_Clear()
{unsigned char i,j; //-128 --- 127for(i=0;i<8;i++){OLED_Write_Cmd(0xB0 + i);//page0--page7//每个page从0列OLED_Write_Cmd(0x00);OLED_Write_Cmd(0x10);//0到127列,依次写入0,每写入数据,列地址自动偏移for(j = 0;j<128;j++){OLED_Write_Cmd(0);}}
}/* 图片像素 */
unsigned char bmpImager[] = {/*-- 宽度x高度=128x64 --128x8x8*/0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x08,0x0C,0x04,0x06,0x06,0x0C,0x04,0x0C,0xFC,0x1C,0x74,0xFC,0xF8,0xF0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x07,0x04,0x88,0xF8,0x08,0x08,0x0C,0x06,0x01,0x00,0x00,0x01,0x1F,0x7F,0xFF,0xDC,0xF8,0xE0,0xC0,0x40,0xC0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x10,0x18,0x08,0x0C,0x04,0x04,0x06,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x60,0xC0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x06,0x1C,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x07,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x08,0x88,0xE8,0x38,0x0E,0x09,0x08,0x08,0x88,0xE8,0x18,0x08,0x08,0x08,0x00,0x00,0xFF,0x89,0x89,0x89,0xFF,0x00,0xFF,0x89,0x89,0x89,0x89,0xFF,0x00,0x00,0x04,0x04,0x84,0x74,0x6F,0xA4,0x24,0x24,0x24,0x24,0xA4,0x64,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xF0,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x10,0x10,0x08,0x09,0x09,0x06,0x06,0x06,0x05,0x08,0x08,0x10,0x10,0x00,0x00,0x0C,0x03,0x10,0x10,0x10,0x1F,0x18,0x07,0x00,0x00,0x10,0x10,0x1F,0x00,0x10,0x08,0x06,0x11,0x10,0x08,0x09,0x0A,0x06,0x06,0x0B,0x08,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x1E,0x03,0x00,0x00,0xC0,0x60,0x30,0x0C,0x04,0x06,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x1E,0x60,0x78,0x0F,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};/* OLED显示图像 */
void OLED_Show_Image(unsigned char *image)
{unsigned char i; unsigned int j;for(i=0;i<8;i++){OLED_Write_Cmd(0xB0 + i);//page0--page7//每个page从0列OLED_Write_Cmd(0x00);OLED_Write_Cmd(0x10);//0到127列,依次写入0,每写入数据,列地址自动偏移for(j = 128 * i; j<(128 * (i+1));j++){OLED_Write_Data(image[j]);}}
}
/* USER CODE END 0 */int main(void)
{/* USER CODE BEGIN 1 */unsigned char i;/* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* Configure the system clock */SystemClock_Config();/* Initialize all configured peripherals */MX_GPIO_Init();MX_I2C1_Init();/* USER CODE BEGIN 2 */OLED_Init();// 选择一个位置// 确认页寻址模式OLED_Write_Cmd(0x20);OLED_Write_Cmd(0x02);OLED_Clear();OLED_Show_Image(bmpImager);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
上面这段代码就是我们要实现的业务代码了,分别封装了:OLED写指令,OLED写数据,OLED屏幕初始化,OLED清屏,main函数,完成了OLED屏幕显示了一张图像,还有字符、字符串、中文汉字的显示。