I2C以及OLED屏幕的实际应用

一、I²C 协议的特点

  1. 仅需两根线

    • I²C 总线只需要两根线即可实现设备之间的通信,这两根线分别是串行数据线(SDA)和串行时钟线(SCL)。
    • SDA 用于传输数据,SCL 用于同步数据传输的时钟信号。
  2. 多主多从架构

    • I²C 协议支持多主多从架构,即多个主设备可以同时与多个从设备进行通信。
    • 在多主模式下,当多个主设备同时尝试控制总线时,通过仲裁机制来确定哪个主设备获得总线控制权。
  3. 7 位或 10 位地址寻址

    • 每个连接到 I²C 总线上的设备都有一个唯一的地址,用于在通信中被主设备寻址。
    • 地址可以是 7 位或 10 位,具体取决于设备的要求。
  4. 简单灵活

    • I²C 协议的实现相对简单,只需要少量的硬件资源即可实现。
    • 同时,该协议也非常灵活,可以根据不同的应用需求进行配置和扩展。

二、I²C 协议的工作原理

  1. 起始和停止条件

    • 起始条件:当 SCL 为高电平时,SDA 从高电平变为低电平,表示开始一次数据传输。
    • 停止条件:当 SCL 为高电平时,SDA 从低电平变为高电平,表示结束一次数据传输。
  2. 数据传输格式

    • I²C 协议的数据传输以字节为单位,每个字节为 8 位。
    • 在数据传输过程中,SCL 信号用于同步数据的传输,SDA 信号在 SCL 的每个时钟周期内传输一位数据。
    • 数据传输时,先传输最高位(MSB),最后传输最低位(LSB)。
  3. 应答信号

    • 在每个字节传输完成后,接收方需要发送一个应答信号(ACK)给发送方,表示数据已经成功接收。
    • 应答信号在 SCL 的第 9 个时钟周期由接收方将 SDA 拉低来表示。如果接收方没有发送应答信号,则表示数据传输出现错误。
  4. 地址传输

    • 在开始一次数据传输之前,主设备需要先发送一个起始条件,然后发送要访问的从设备地址。
    • 从设备地址由 7 位或 10 位组成,具体取决于设备的要求。
    • 地址传输完成后,从设备会发送一个应答信号给主设备,表示地址已经被成功接收。
  5. 数据传输方向

    • 在地址传输完成后,主设备可以选择数据传输的方向,即读操作或写操作。
    • 如果是写操作,主设备将数据发送给从设备;如果是读操作,主设备从从设备读取数据。

三、I²C 协议的应用场景

  1. 传感器连接

    • I²C 协议常用于连接各种传感器,如温度传感器、湿度传感器、压力传感器等。
    • 通过 I²C 总线,单片机可以方便地读取传感器的数据,并进行相应的处理。
  2. 存储器扩展

    • I²C 协议也可以用于扩展单片机的存储器,如 EEPROM、FRAM 等。
    • 通过 I²C 总线,单片机可以与外部存储器进行数据交换,实现数据的存储和读取。
  3. 显示设备控制

    • I²C 协议还可以用于控制各种显示设备,如液晶显示屏、OLED 显示屏等。
    • 通过 I²C 总线,单片机可以向显示设备发送控制命令和数据,实现显示内容的更新。

I2C通信协议层:

一、物理层

  1. 引脚定义
    • SDA(Serial Data Line):串行数据线,用于传输数据。这是一个双向引脚,数据在该线路上以串行方式逐位传输。
    • SCL(Serial Clock Line):串行时钟线,由主设备产生时钟信号,用于同步数据传输。
  2. 电气特性
    • 通常工作在低电压,如 3.3V 或 5V 逻辑电平。它是开漏输出结构,所以需要外接上拉电阻,一般上拉电阻的值在  1.8 - 10kΩ 之间。这一特性允许多个设备连接到同一总线上,实现多主多从的通信架构。

二、数据传输层

  1. 位传输
    • 在 I2C 总线上,数据传输以位为单位。在 SCL 时钟信号的高电平期间,SDA 线上的数据必须保持稳定,只有在 SCL 时钟信号为低电平时,SDA 线上的数据才允许发生变化。这一规则确保了数据传输的稳定性和准确性。
  2. 字节传输
    • 每次传输以字节(8 位)为单位。传输顺序是先高位(MSB)后低位(LSB)。在一个字节传输完成后,会有一个应答位(ACK)来确认接收是否成功。
  3. 应答机制
    • 发送器发送完一个字节后,会释放 SDA 线(将 SDA 线置为高电平),接收器在接收到这个字节后,会在第 9 个时钟周期将 SDA 线拉低(发送应答信号 ACK)表示接收成功;如果接收器不拉低 SDA 线(保持高电平),则表示非应答信号(NACK),通常表示传输出现错误或者接收器希望结束数据传输。

三、协议层的操作模式

  1. 主从模式
    • 主设备(Master)
      • 主设备负责发起数据传输、产生时钟信号(SCL)以及控制传输的开始和结束。在总线上可以有多个主设备,但同一时刻只能有一个主设备进行数据传输操作。
      • 主设备通过发送起始条件(Start Condition)来开始一次传输,起始条件是在 SCL 为高电平时,SDA 由高电平变为低电平。然后主设备发送从设备地址(7 位或 10 位地址),其中第 8 位(对于 7 位地址模式)或第 11 位(对于 10 位地址模式)用来表示数据传输方向(读或写)。
    • 从设备(Slave)
      • 从设备响应主设备的操作。从设备有唯一的地址,当从设备检测到主设备发送的地址与自己的地址匹配时,会根据数据传输方向进行相应操作。如果是写操作,从设备接收主设备发送的数据;如果是读操作,从设备向主设备发送数据。
  2. 多主模式
    • 多个主设备可以连接到 I2C 总线上。当多个主设备同时想要控制总线时,会通过仲裁机制来决定哪个主设备获得总线控制权。仲裁机制是基于在 SCL 时钟信号的高电平期间,比较各个主设备在 SDA 线上发送的数据位来实现的。如果一个主设备发送的电平与总线上检测到的电平不匹配,这个主设备就会失去仲裁,停止数据传输操作。

四、协议层的传输速度

  1. 标准模式(Standard - mode)
    • 传输速度可达 100 kbps。这种模式适用于对速度要求不是特别高的应用场景,如一些简单的传感器与微控制器之间的通信。
  2. 快速模式(Fast - mode)
    • 传输速度可以达到 400 kbps。能满足大多数中等速度要求的设备间通信需求,如一些小型的嵌入式系统内部模块之间的通信。
  3. 高速模式(High - speed mode)
    • 传输速度能够达到 3.4 Mbps。主要用于对数据传输速度要求较高的应用,如某些高速数据采集设备与处理器之间的通信。

软硬件I2C协议

软件模拟协议:使用CPU直接控制通讯引脚的电平,产生出符合通讯协议标准的逻辑。


硬件实现协议:由STM32的IIC片上外设专门负责实现IIC通讯协议,只要配置好该外设,它就会自动根据协议要求产生通讯信号,收发数据并缓存起来,CPU只要检测该外设的状态和访问数据寄存器,就能完成数据收发。这种由硬件外设处理IIC协议的方式减轻了CPU的工作,且使软件设计
更加简单。

I2C协议功能框图:                      

通过软件控制引脚电平实现I2C协议,也就是利用IO复用功能重映射

当 “12C1 REMAP = 0” 时,I2C1 的 SCL(串行时钟线)为 PB6,SDA(串行数据线)为 PB7

当 “12C1 REMAP = 1” 时,I2C1 的 SCL 变为 PB8,SDA 变为 PB9

其二者主要的区别就是引脚的不同

初始化I2C的结构体

typedef struct
{
  uint32_t I2C_ClockSpeed;  //设置时钟频率,必须低于400000        

  uint16_t I2C_Mode;         //指定工作模式,可选IIC模式及SMBUS模式       

  uint16_t I2C_DutyCycle;   //时钟占空比,可选LOW/HIGH,2:0或16:9

  uint16_t I2C_OwnAddress1;  //自身的IIC设备地址       

  uint16_t I2C_Ack;          //使能或关闭相应,一般为使能      

  uint16_t I2C_AcknowledgedAddress;  //指定地址长度,可为7或10
                                         
}I2C_InitTypeDef;
  1. uint32_t I2C_ClockSpeed
    • 描述:指定 I²C 总线的时钟频率。
    • 约束:这个参数的值必须设置为低于 400kHz。时钟频率决定了数据在 I²C 总线上的传输速度。较低的时钟频率可以在一些对噪声敏感或长距离通信的情况下提供更稳定的通信。
  2. uint16_t I2C_Mode
    • 描述:指定 I²C 的模式。
    • 可能取值:通常由一系列预定义的枚举值表示不同的模式,例如标准模式、快速模式等。不同的模式在传输速度、时钟占空比等方面可能有所不同。
  3. uint16_t I2C_DutyCycle
    • 描述:指定 I²C 在快速模式下的占空比。
    • 可能取值:通常由一系列预定义的枚举值表示不同的占空比选项。占空比影响时钟信号的高电平和低电平时间比例,对于一些特定的设备或应用场景可能需要调整占空比以获得最佳性能。
  4. uint16_t I2C_OwnAddress1
    • 描述:指定设备自己的第一个地址。
    • 取值范围:这个参数可以是 7 位或 10 位地址。I²C 总线上的每个设备都有一个唯一的地址,用于在通信中被主设备识别和寻址。
  5. uint16_t I2C_Ack
    • 描述:启用或禁用应答。
    • 可能取值:通常由一系列预定义的枚举值表示启用或禁用应答。在 I²C 通信中,接收方在接收到数据后可以发送一个应答信号表示成功接收,或者发送非应答信号表示接收出现问题或通信结束。
  6. uint16_t I2C_AcknowledgedAddress
    • 描述:指定是否应答 7 位或 10 位地址。
    • 可能取值:通常由一系列预定义的枚举值表示应答的地址位数。这个参数决定了设备在接收地址时是否对 7 位地址或 10 位地址进行应答。

一、OLED 屏幕概述

  1. 基本原理
    • OLED(Organic Light - Emitting Diode)即有机发光二极管。0.96 寸 OLED 屏幕是一种小型的显示设备。它基于有机材料在电场作用下发光的原理工作。当有电流通过有机发光层时,有机材料中的电子和空穴复合,释放能量并发光。
    • 与传统的液晶显示屏(LCD)相比,OLED 屏幕具有自发光、对比度高、视角广、响应速度快等优点。在单片机系统中,它能够提供清晰、鲜艳的显示效果。
    • 驱动芯片一般为SSD1306,SSD1306显存总共为128*64bit大小,SSD1306将这些显存分成了8页。每页包含了128字节。
  2. 物理特性
    • 0.96 寸的 OLED 屏幕通常具有较小的尺寸,其分辨率一般为 128×64 像素。这种分辨率在小型设备的显示需求中较为常见,能够显示简单的文字、图形和图标等信息。

    • 它的接口类型多样,常见的有 I2C 接口和 SPI 接口,这使得它能够方便地与单片机进行连接通信。

二、与单片机的连接

  1. I2C 接口连接
    • 硬件连接
      • 一般来说,OLED 屏幕的 SCL(串行时钟线)和 SDA(串行数据线)引脚需要连接到单片机对应的 I2C 引脚。例如,在一些单片机如 STM32 中,可以连接到具有 I2C 功能的 GPIO 引脚。同时,还需要连接电源(一般为 3.3V 或 5V,取决于屏幕规格)和地。
    • 通信原理
      • 在 I2C 通信中,单片机作为主设备,OLED 屏幕作为从设备。单片机通过发送起始信号、设备地址(OLED 屏幕有特定的 I2C 地址)、控制命令和数据等操作来控制 OLED 屏幕的显示内容。I2C 通信采用两线制,通过时钟信号同步数据传输,具有简单、占用引脚少等优点。
  2. SPI 接口连接
    • 硬件连接
      • SPI 接口连接时,需要连接 OLED 屏幕的 SPI 相关引脚,如 SCK(时钟线)、MOSI(主出从入线)、CS(片选线)等引脚到单片机对应的 SPI 功能引脚。同样也要连接电源和地。
    • 通信原理
      • SPI 是一种高速的串行通信接口。单片机通过 CS 引脚选择 OLED 屏幕设备,然后在 SCK 时钟信号的同步下,通过 MOSI 线向 OLED 屏幕发送数据。SPI 通信速度相对较快,但相比 I2C 需要更多的引脚资源。

三、单片机对 OLED 屏幕的控制

  1. 初始化操作
    • 在使用 OLED 屏幕之前,单片机需要对其进行初始化。初始化操作包括设置屏幕的显示模式、对比度、亮度等参数。例如,通过向 OLED 屏幕发送特定的命令序列来设置其为正常显示模式,设置合适的对比度值以确保清晰的显示效果。
    • 对于不同厂家生产的 0.96 寸 OLED 屏幕,初始化命令可能会略有不同,但一般都遵循相应的 OLED 显示规范。
  2. 显示内容操作
    • 文字显示
      • 单片机可以将待显示的文字转换为对应的字符编码(如 ASCII 码或 Unicode 码,取决于屏幕支持的编码方式),然后发送相应的显示命令和数据到 OLED 屏幕。例如,要在屏幕上显示 “Hello World”,需要将每个字符的编码按照屏幕的显示格式要求发送到屏幕。
    • 图形显示
      • 对于图形显示,单片机需要根据屏幕的分辨率构建图形数据。如果要显示一个简单的矩形,需要计算出矩形在屏幕上各个像素点的位置,并将对应的像素点亮或熄灭的数据发送到 OLED 屏幕。这通常涉及到对屏幕显存(显示缓冲区)的操作,将图形数据写入到显存中,然后通过刷新命令使屏幕显示出图形。

OLED屏幕常用指令

一、设置对比度指令(81)

  • 指令 HEX 值为 81。这个指令用于设置 OLED 屏幕的对比度。
  • D7 到 D0 的各位描述中,A 的范围从 0X00 到 0XFE。A 的值越大,屏幕越亮。通过改变这个值,可以调整屏幕的显示亮度,以适应不同的环境和需求。

二、设置显示开关指令(AE/AF)

  • 指令 HEX 值为 AE 表示关闭显示,AF 表示开启显示。
  • D7 到 D0 的各位描述中,X0 决定显示的开关状态。当 X0 = 0 时,关闭显示;当 X0 = 1 时,开启显示。这个指令可以方便地控制屏幕的显示状态,例如在需要省电或者不需要显示的时候可以关闭屏幕。

三、电荷泵设置指令(8D)

  • 指令 HEX 值为 8D。
  • D7 到 D0 的各位描述中,A2 决定电荷泵的状态。当 A2 = 0 时,关闭电荷泵;当 A2 = 1 时,开启电荷泵。电荷泵的作用是为 OLED 屏幕提供稳定的电源,开启电荷泵可以提高屏幕的显示稳定性和亮度。

四、设置页地址指令(B0 到 B7)

  • 指令 HEX 值范围为 B0 到 B7。这个指令用于设置 OLED 屏幕的页地址。
  • D7 到 D0 的各位描述中,X [2:0] 的值范围为 0 到 7,分别对应页 0 到 7。通过设置页地址,可以指定要显示内容的页面位置,方便进行多页显示的管理。

五、设置列地址指令(分为低四位和高四位)

  • 低四位指令 HEX 值为 00 到 0F。这个指令用于设置 OLED 屏幕起始列地址的低四位。
  • 高四位指令 HEX 值为 10 到 1F。这个指令用于设置 OLED 屏幕起始列地址的高四位。
  • D7 到 D0 的各位描述中,X3、X2、X1、X0 共同组成 8 位起始列地址。通过设置列地址,可以指定要显示内容在页面中的水平位置,从而实现精确的显示控制。

代码实现:
被调函数

以下是带有更详细注释的代码:

```c
#include "stm32f10x.h"
#include "oled.h"
#include "SysTick.h"

// 定义一个宽度为 8 像素,高度为 16 像素的字符集数组
const uint8_t OLED_F8x16[][16] =
{
    // 这里是每个字符的 16 个字节的点阵数据,每个字符占用连续的 16 个字节空间
    // 例如第一个字符:
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//   0
    // 第二个字符:
    0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00,//! 1
    0x00,0x16,0x0E,0x00,0x16,0x0E,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// " 2
	0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,
	0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00,// # 3
	0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,
	0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00,// $ 4
	0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,
	0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00,// % 5
	0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,
	0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10,// & 6
	0x00,0x00,0x00,0x16,0x0E,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// ' 7
	0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,
	0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00,// ( 8
	0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,
	0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00,// ) 9
	0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,
	0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00,// * 10
	0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,
	0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00,// + 11
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0xB0,0x70,0x00,0x00,0x00,0x00,0x00,// , 12
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,// - 13
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00,// . 14
	0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,
	0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00,// / 15
	0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,
	0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,// 0 16
	0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,
	0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,// 1 17
	0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,
	0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00,// 2 18
	0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,
	0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00,// 3 19
	0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,
	0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00,// 4 20
	0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,
	0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00,// 5 21
	0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,
	0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00,// 6 22
	0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,
	0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,// 7 23
	0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,
	0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00,// 8 24
	0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,
	0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00,// 9 25
	0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,
	0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,// : 26
	0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,
	0x00,0x00,0x80,0xB0,0x70,0x00,0x00,0x00,// ; 27
	0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,
	0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00,// < 28
	0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,
	0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00,// = 29
	0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,
	0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00,// > 30
	0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,
	0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00,// ? 31
	0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,
	0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00,// @ 32
	0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,
	0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20,// A 33
	0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,
	0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00,// B 34
	0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,
	0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00,// C 35
	0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,
	0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00,// D 36
	0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,
	0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00,// E 37
	0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,
	0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00,// F 38
	0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,
	0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00,// G 39
	0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,
	0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20,// H 40
	0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,
	0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,// I 41
	0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,
	0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00,// J 42
	0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,
	0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00,// K 43
	0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,
	0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00,// L 44
	0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,
	0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00,// M 45
	0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,
	0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00,// N 46
	0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,
	0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00,// O 47
	0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,
	0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00,// P 48
	0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,
	0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00,// Q 49
	0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,
	0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20,// R 50
	0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,
	0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00,// S 51
	0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,
	0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,// T 52
	0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,
	0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,// U 53
	0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,
	0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00,// V 54
	0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,
	0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00,// W 55
	0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,
	0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20,// X 56
	0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,
	0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,// Y 57
	0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,
	0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00,// Z 58
	0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,
	0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00,// [ 59
	0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00,// \ 60
	0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,
	0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00,// ] 61
	0x00,0x20,0x10,0x08,0x04,0x08,0x10,0x20,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// ^ 62
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,// _ 63
	0x00,0x02,0x04,0x08,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// ` 64
	0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
	0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20,// a 65
	0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,
	0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00,// b 66
	0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,
	0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00,// c 67
	0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,
	0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20,// d 68
	0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
	0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00,// e 69
	0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,
	0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,// f 70
	0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,
	0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00,// g 71
	0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,
	0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,// h 72
	0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,
	0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,// i 73
	0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,
	0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,// j 74
	0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,
	0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00,// k 75
	0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,
	0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,// l 76
	0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,
	0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F,// m 77
	0x00,0x80,0x80,0x00,0x80,0x80,0x00,0x00,
	0x00,0x20,0x3F,0x21,0x00,0x20,0x3F,0x20,// n 78
	0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
	0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,// o 79
	0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,
	0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00,// p 80
	0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,
	0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80,// q 81
	0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,
	0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00,// r 82
	0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,
	0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00,// s 83
	0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,
	0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00,// t 84
	0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,
	0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20,// u 85
	0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,
	0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00,// v 86
	0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,
	0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00,// w 87
	0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,
	0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00,// x 88
	0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,
	0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00,// y 89
	0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,
	0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00,// z 90
	0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,
	0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40,// { 91
	0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,// | 92
	0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,
	0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00,// } 93
	0x00,0x80,0x40,0x40,0x80,0x00,0x00,0x80,
	0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x00,// ~ 94
};

// 定义一个宽度为 6 像素,高度为 8 像素的字符集数组
const uint8_t OLED_F6x8[][6] = 
{
    // 这里是每个字符的 6 个字节的点阵数据,每个字符占用连续的 6 个字节空间
    // 例如第一个字符:
    0x00,0x00,0x00,0x00,0x00,0x00,//   0
    // 第二个字符:
    0x00,0x00,0x00,0x2F,0x00,0x00,//! 1
    0x00,0x00,0x07,0x00,0x07,0x00,// " 2
	0x00,0x14,0x7F,0x14,0x7F,0x14,// # 3
	0x00,0x24,0x2A,0x7F,0x2A,0x12,// $ 4
	0x00,0x23,0x13,0x08,0x64,0x62,// % 5
	0x00,0x36,0x49,0x55,0x22,0x50,// & 6
	0x00,0x00,0x00,0x07,0x00,0x00,// ' 7
	0x00,0x00,0x1C,0x22,0x41,0x00,// ( 8
	0x00,0x00,0x41,0x22,0x1C,0x00,// ) 9
	0x00,0x14,0x08,0x3E,0x08,0x14,// * 10
	0x00,0x08,0x08,0x3E,0x08,0x08,// + 11
	0x00,0x00,0x00,0xA0,0x60,0x00,// , 12
	0x00,0x08,0x08,0x08,0x08,0x08,// - 13
	0x00,0x00,0x60,0x60,0x00,0x00,// . 14
	0x00,0x20,0x10,0x08,0x04,0x02,// / 15
	0x00,0x3E,0x51,0x49,0x45,0x3E,// 0 16
	0x00,0x00,0x42,0x7F,0x40,0x00,// 1 17
	0x00,0x42,0x61,0x51,0x49,0x46,// 2 18
	0x00,0x21,0x41,0x45,0x4B,0x31,// 3 19
	0x00,0x18,0x14,0x12,0x7F,0x10,// 4 20
	0x00,0x27,0x45,0x45,0x45,0x39,// 5 21
	0x00,0x3C,0x4A,0x49,0x49,0x30,// 6 22
	0x00,0x01,0x71,0x09,0x05,0x03,// 7 23
	0x00,0x36,0x49,0x49,0x49,0x36,// 8 24
	0x00,0x06,0x49,0x49,0x29,0x1E,// 9 25
	0x00,0x00,0x36,0x36,0x00,0x00,// : 26
	0x00,0x00,0x56,0x36,0x00,0x00,// ; 27
	0x00,0x08,0x14,0x22,0x41,0x00,// < 28
	0x00,0x14,0x14,0x14,0x14,0x14,// = 29
	0x00,0x00,0x41,0x22,0x14,0x08,// > 30
	0x00,0x02,0x01,0x51,0x09,0x06,// ? 31
	0x00,0x3E,0x49,0x55,0x59,0x2E,// @ 32
	0x00,0x7C,0x12,0x11,0x12,0x7C,// A 33
	0x00,0x7F,0x49,0x49,0x49,0x36,// B 34
	0x00,0x3E,0x41,0x41,0x41,0x22,// C 35
	0x00,0x7F,0x41,0x41,0x22,0x1C,// D 36
	0x00,0x7F,0x49,0x49,0x49,0x41,// E 37
	0x00,0x7F,0x09,0x09,0x09,0x01,// F 38
	0x00,0x3E,0x41,0x49,0x49,0x7A,// G 39
	0x00,0x7F,0x08,0x08,0x08,0x7F,// H 40
	0x00,0x00,0x41,0x7F,0x41,0x00,// I 41
	0x00,0x20,0x40,0x41,0x3F,0x01,// J 42
	0x00,0x7F,0x08,0x14,0x22,0x41,// K 43
	0x00,0x7F,0x40,0x40,0x40,0x40,// L 44
	0x00,0x7F,0x02,0x0C,0x02,0x7F,// M 45
	0x00,0x7F,0x04,0x08,0x10,0x7F,// N 46
	0x00,0x3E,0x41,0x41,0x41,0x3E,// O 47
	0x00,0x7F,0x09,0x09,0x09,0x06,// P 48
	0x00,0x3E,0x41,0x51,0x21,0x5E,// Q 49
	0x00,0x7F,0x09,0x19,0x29,0x46,// R 50
	0x00,0x46,0x49,0x49,0x49,0x31,// S 51
	0x00,0x01,0x01,0x7F,0x01,0x01,// T 52
	0x00,0x3F,0x40,0x40,0x40,0x3F,// U 53
	0x00,0x1F,0x20,0x40,0x20,0x1F,// V 54
	0x00,0x3F,0x40,0x38,0x40,0x3F,// W 55
	0x00,0x63,0x14,0x08,0x14,0x63,// X 56
	0x00,0x07,0x08,0x70,0x08,0x07,// Y 57
	0x00,0x61,0x51,0x49,0x45,0x43,// Z 58
	0x00,0x00,0x7F,0x41,0x41,0x00,// [ 59
	0x00,0x02,0x04,0x08,0x10,0x20,// \ 60
	0x00,0x00,0x41,0x41,0x7F,0x00,// ] 61
	0x00,0x04,0x02,0x01,0x02,0x04,// ^ 62
	0x00,0x40,0x40,0x40,0x40,0x40,// _ 63
	0x00,0x00,0x01,0x02,0x04,0x00,// ` 64
	0x00,0x20,0x54,0x54,0x54,0x78,// a 65
	0x00,0x7F,0x48,0x44,0x44,0x38,// b 66
	0x00,0x38,0x44,0x44,0x44,0x20,// c 67
	0x00,0x38,0x44,0x44,0x48,0x7F,// d 68
	0x00,0x38,0x54,0x54,0x54,0x18,// e 69
	0x00,0x08,0x7E,0x09,0x01,0x02,// f 70
	0x00,0x18,0xA4,0xA4,0xA4,0x7C,// g 71
	0x00,0x7F,0x08,0x04,0x04,0x78,// h 72
	0x00,0x00,0x44,0x7D,0x40,0x00,// i 73
	0x00,0x40,0x80,0x84,0x7D,0x00,// j 74
	0x00,0x7F,0x10,0x28,0x44,0x00,// k 75
	0x00,0x00,0x41,0x7F,0x40,0x00,// l 76
	0x00,0x7C,0x04,0x18,0x04,0x78,// m 77
	0x00,0x7C,0x08,0x04,0x04,0x78,// n 78
	0x00,0x38,0x44,0x44,0x44,0x38,// o 79
	0x00,0xFC,0x24,0x24,0x24,0x18,// p 80
	0x00,0x18,0x24,0x24,0x18,0xFC,// q 81
	0x00,0x7C,0x08,0x04,0x04,0x08,// r 82
	0x00,0x48,0x54,0x54,0x54,0x20,// s 83
	0x00,0x04,0x3F,0x44,0x40,0x20,// t 84
	0x00,0x3C,0x40,0x40,0x20,0x7C,// u 85
	0x00,0x1C,0x20,0x40,0x20,0x1C,// v 86
	0x00,0x3C,0x40,0x30,0x40,0x3C,// w 87
	0x00,0x44,0x28,0x10,0x28,0x44,// x 88
	0x00,0x1C,0xA0,0xA0,0xA0,0x7C,// y 89
	0x00,0x44,0x64,0x54,0x4C,0x44,// z 90
	0x00,0x00,0x08,0x7F,0x41,0x00,// { 91
	0x00,0x00,0x00,0x7F,0x00,0x00,// | 92
	0x00,0x00,0x41,0x7F,0x08,0x00,// } 93
	0x00,0x08,0x04,0x08,0x10,0x08,// ~ 94
};

/*********************ASCII字模数据*/

// 配置 I2C 总线的函数
void I2C_configation(void)
{
    I2C_InitTypeDef I2C_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    // 开启 GPIOB 时钟,使能 GPIOB 端口,为配置 I2C 使用的 GPIO 引脚做准备
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    // 开启 I2C1 时钟,使能 I2C1 模块
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

    // 这里开始配置 GPIO 引脚,将用于 I2C 通信的 GPIOB 的 Pin6 和 Pin7 设置为复用开漏输出模式
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    // 指定要配置的引脚为 GPIOB 的 Pin6 和 Pin7
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    // 设置 GPIO 引脚的输出速度为 50MHz
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    // 将配置应用到 GPIOB 端口上
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    // 下面开始配置 I2C1 的参数
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    // 设置 I2C1 的时钟速度为 400kHz
    I2C_InitStructure.I2C_ClockSpeed = 400000;
    // 使能应答信号
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    // 设置为 7 位地址模式,并在接收到地址后应答
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    // 设置 I2C1 的占空比为 2(具体含义取决于芯片手册)
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    // 设置 I2C1 的自身地址为 0x30(一般在作为从设备时使用)
    I2C_InitStructure.I2C_OwnAddress1 = 0x30;

    // 初始化 I2C1,将配置写入 I2C1 寄存器
    I2C_Init(I2C1, &I2C_InitStructure);
    // 使能 I2C1,启动 I2C1 模块开始工作
    I2C_Cmd(I2C1, ENABLE);
}

// 发送一个字节数据的函数
void SendByte(uint8_t regaddr, uint8_t data)
{
    // 产生 I2C 起始信号,开始一次 I2C 通信
    I2C_GenerateSTART(I2C1, ENABLE);
    // 循环等待,直到主模式选择事件发生,表示 I2C 控制器已经进入主模式并准备发送地址
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
    // 发送 OLED 地址,并设置为发送模式,告诉 I2C 总线要与哪个设备通信以及通信方向为发送
    I2C_Send7bitAddress(I2C1, OLED_ADDRESS, I2C_Direction_Transmitter);
    // 循环等待,直到主发送器模式选择事件发生,表示设备已经应答并准备接收数据
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
    // 发送寄存器地址,告诉 OLED 接下来要写入的数据是存储在哪个寄存器中的
    I2C_SendData(I2C1, regaddr);
    // 循环等待,直到字节传输事件发生,表示寄存器地址已经成功发送出去
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING));
    // 发送数据,即将要写入 OLED 的具体数据内容
    I2C_SendData(I2C1, data);
    // 循环等待,直到字节传输完成事件发生,表示数据已经成功发送出去
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
    // 产生停止信号,结束本次 I2C 通信
    I2C_GenerateSTOP(I2C1, ENABLE);
}

// 写命令函数,将数据作为命令发送
void WriteCmd(uint8_t data)
{
    // 调用 SendByte 函数,将数据作为命令写入 OLED,命令寄存器地址为 0x00
    SendByte(0x00, data);
}

// 写数据函数,将数据发送到 OLED 数据寄存器
void WriteData(uint8_t data)
{
    // 调用 SendByte 函数,将数据作为数据写入 OLED,数据寄存器地址为 0x40
    SendByte(0x40, data);
}

// OLED 初始化函数
void OLED_Init(void)
{
    // 配置 I2C 总线,为与 OLED 通信做准备
    I2C_configation();

    // 延时一段时间等待稳定
    delay_ms(500);

    // 关闭显示,即写入命令 0xAE,让 OLED 进入关闭状态
    WriteCmd(0xAE);
    // 设置内存寻址模式,写入命令 0x20,这里设置为某种特定的寻址模式(具体取决于后续参数)
    WriteCmd(0x20);
    // 设置水平寻址模式,写入命令 0x10,表示选择水平寻址模式
    WriteCmd(0x10);
    // 设置页起始地址,写入命令 0xb0,这里设置为 0,表示从第一页开始
    WriteCmd(0xb0);
    // 设置 COM 输出扫描方向,写入命令 0xc8,具体方向由该命令决定
    WriteCmd(0xc8);
    // 设置低列地址,写入命令 0x00,表示从第一列开始
    WriteCmd(0x00);
    // 设置高列地址,写入命令 0x10,与低列地址一起确定列地址范围
    WriteCmd(0x10);
    // 设置起始行地址,写入命令 0x40,表示从第一行开始
    WriteCmd(0x40);
    // 设置对比度控制寄存器,写入命令 0x81,准备设置对比度
    WriteCmd(0x81);
    // 设置最大对比度,写入命令 0xff,让 OLED 显示最亮
    WriteCmd(0xff);
    // 设置段重映射,写入命令 0xa1,具体映射方式由该命令决定
    WriteCmd(0xa1);
    // 设置正常显示,写入命令 0xa6,开启正常显示模式
    WriteCmd(0xa6);
    // 设置复用比,写入命令 0xa8,准备设置复用比参数
    WriteCmd(0xa8);
    // 设置为 64 倍复用比,写入命令 0x3F
    WriteCmd(0x3F);
    // 输出跟随 RAM 内容,写入命令 0xa4,表示 OLED 的输出将跟随内存中的内容变化
    WriteCmd(0xa4);
    // 设置显示偏移,写入命令 0xd3,准备设置显示偏移参数
    WriteCmd(0xd3);
    // 不设置偏移,写入命令 0x00,表示没有偏移
    WriteCmd(0x00);
    // 设置显示时钟分频比和振荡器频率,写入命令 0xd5,准备设置相关参数
    WriteCmd(0xd5);
    // 设置分频比,写入命令 0xf0,具体分频比由该命令决定
    WriteCmd(0xf0);
    // 设置预充电周期,写入命令 0xd9,准备设置预充电周期参数
    WriteCmd(0xd9);
    // 设置预充电周期参数,写入命令 0x22
    WriteCmd(0x22);
    // 设置 COM 引脚硬件配置,写入命令 0xda,准备设置 COM 引脚的硬件配置参数
    WriteCmd(0xda);
    // 设置 COM 引脚硬件配置参数,写入命令 0x12
    WriteCmd(0x12);
    // 设置 VCOMH,写入命令 0xdb,准备设置 VCOMH 的电压参数
    WriteCmd(0xdb);
    // 设置 VCOMH 参数,写入命令 0x20,表示设置为特定的 VCOMH 电压值
    WriteCmd(0x20);
    // 开启 DC-DC 转换,写入命令 0x8d,准备开启 DC-DC 转换功能
    WriteCmd(0x8d);
    // 设置 DC-DC 转换参数,写入命令 0x14,表示开启 DC-DC 转换
    WriteCmd(0x14);
    // 开启 OLED 面板显示,写入命令 0xaf,让 OLED 开始显示内容
    WriteCmd(0xaf);

    // 再次延时一段时间等待稳定
    delay_ms(100);
}

// 设置显示位置的函数
void SetPos(uint8_t x, uint8_t y)
{
    // 设置页地址,通过或操作将传入的 y 值与命令 0xb0 组合,确定要显示的页
    WriteCmd(0xb0|y);
    // 设置高列地址,通过位运算和或操作将传入的 x 值的高四位处理后与命令 0x00 组合
    WriteCmd(((x&0xf0)>>4)|0x00);
    // 设置低列地址,通过位运算和或操作将传入的 x 值的低四位处理后与命令 0x00 组合
    WriteCmd((x&0x0f)|0x00);
}

// 填充 OLED 屏幕的函数
void OLED_Fill(uint8_t Fill_data)
{
    uint8_t i, j;
    // 循环遍历每一页(8 页)
    for (i = 0; i < 8; i++)
    {
        // 设置当前页地址
        WriteCmd(0xb0 + i);
        // 设置列地址起始值
        WriteCmd(0x00);
        WriteCmd(0x10);
        // 循环遍历每一列(128 列),写入填充数据
        for (j = 0; j < 128; j++)
        {
            WriteData(Fill_data);
        }
    }
}

// 清屏函数
void OLED_CLS(void)
{
    // 调用填充函数,用 0x00 填充屏幕实现清屏
    OLED_Fill(0x00);
}

// 开启 OLED 显示函数
void OLED_ON(void)
{
    WriteCmd(0x8d);
    WriteCmd(0x14);
    WriteCmd(0xaf);
}

// 关闭 OLED 显示函数
void OLED_OFF(void)
{
    WriteCmd(0x8d);
    WriteCmd(0x10);
    WriteCmd(0xAE);
}

// 在指定位置显示一个字符的函数
void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t ch, uint8_t FXNUM)
{
    // 如果选择 6x8 字体
    if (FXNUM == 6)
    {
        // 设置显示位置
        SetPos(x, y);
        // 循环写入 6 个字节的字符点阵数据
        for (uint8_t i = 0; i < 6; i++)
        {
            WriteData(OLED_F6x8[ch - 32][i]);
        }
    }
    // 如果选择 8x16 字体
    else if (FXNUM == 8)
    {
        // 设置显示位置
        SetPos(x, y);
        // 循环写入上半部分 8 个字节的字符点阵数据
        for (uint8_t i = 0; i < 8; i++)
        {
            WriteData(OLED_F8x16[ch - 32][i]);
        }
        // 设置显示位置到下一行
        SetPos(x, y + 1);
        // 循环写入下半部分 8 个字节的字符点阵数据
        for (uint8_t i = 0; i < 8; i++)
        {
            WriteData(OLED_F8x16[ch - 32][i + 8]);
        }
    }
}
```

主函数:
 

#include "stm32f10x.h"
#include "main.h"
#include "oled.h"
#include "SysTick.h"

int main(void)
{
	delay_ms(500);
	
	OLED_Init();
	delay_ms(100);
	OLED_Fill(0xff);
	delay_ms(100);
	OLED_CLS();
	delay_ms(100);
	OLED_ShowChar(0,0,'L',6);
	OLED_ShowChar(10,1,'b',6);
	OLED_ShowChar(20,2,'d',6);
//	OLED_ShowChar(60,3,'e',6);
//	OLED_ShowChar(100,1,'c',8);
	while(1);
}

成功实现OLED代码显示一个字符

显示汉字和图片内容只需使用各自的取模软件生成相应的数即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值