Bootstrap

基于STM32F103的0.96寸OLED显示屏驱动程序IIC(注释详细_复制可用)

先介绍一下OLED

OLED被称为有机激光二极管显示,OLED采用有机材料涂层和玻璃基板,当有电流通过时有机材料就会发光,所以OLED具有自发光特性,不需要背光源的特点。显示的原理是将图像或文字分解为一组组小点,并通过控制每个点的亮度或颜色来显示信息。这些小点通常是很小的正方形,并且由有机发光二极管组成。
常用的点阵大小有12x12、14x14、16x16、8x16等,每个点用0或1表示,0代表没有点,1代表有点。这样,每个汉字就可以通过一个二进制数组来表示。

OLED屏像素分辨率
0.96寸OLED是128x64像素的分辨率,我们可以理解为:水平方向分布了128个像素点,垂直方向分布了64个像素点。而驱动芯片在点亮像素点的时候,是以8个像素点为单位的。我们在画点的时候坐标Y的取值为0-7,坐标X的取值为0-127.

上代码,在STM32F103C8T6上测试的,按照下方建好.c文件和.h文件,配置里面加上stm32f10x_i2c.c,直接复制

/*****************************​ main.c 文件 ​******************************/
#include "stm32f10x.h"
#include "oled.h"
#include "font.h"

int main(void) {
    // 系统时钟初始化(默认使用内部8MHz RC时钟)
    SystemInit();
    
    // 初始化I2C外设(使用I2C1,PB6-SCL,PB7-SDA)
    I2C_Config();
    
    // 初始化OLED显示屏
    OLED_Init();
    
    // 在屏幕上显示内容
    OLED_PrintString(0, 0, "Hello STM32!");
    OLED_PrintString(2, 3, "OLED TEST");
    
    // 刷新显示
    OLED_Refresh();
    
    while (1) {
        // 主循环
    }
}

/*****************************​ oled.h 文件 ​******************************/
#ifndef __OLED_H
#define __OLED_H

#include "stm32f10x.h"

// OLED参数定义
#define OLED_WIDTH   128
#define OLED_HEIGHT  64
#define OLED_PAGES   (OLED_HEIGHT/8)  // 8行每页

// 函数声明
void I2C_Config(void);
void OLED_Init(void);
void OLED_Clear(void);
void OLED_Refresh(void);
void OLED_PrintChar(uint8_t x, uint8_t y, char ch);
void OLED_PrintString(uint8_t x, uint8_t y, const char *str);

#endif

/*****************************​ oled.c 文件 ​******************************/
#include "oled.h"
#include "font.h"
#include <string.h>

// OLED I2C地址(左移1位后的地址)
#define OLED_ADDRESS    0x78  // 7位地址0x3C << 1

// 命令/数据标识
#define OLED_CMD        0x00
#define OLED_DATA       0x40

// 显示缓冲区(128x64像素,按页组织)
static uint8_t oled_buffer[OLED_WIDTH][OLED_PAGES];

/*----------------------------------------------------------
  I2C配置函数
  功能:配置I2C1外设,使用PB6(SCL)和PB7(SDA)
------------------------------------------------------------*/
void I2C_Config(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    I2C_InitTypeDef I2C_InitStructure;
    
    // 1. 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
    
    // 2. 配置GPIO
    // PB6 - I2C1_SCL
    // PB7 - I2C1_SDA
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;  // 复用开漏输出
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    // 3. 配置I2C参数
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;    // 50%占空比
    I2C_InitStructure.I2C_OwnAddress1 = 0x00;            // 主机地址不使用
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;           // 使能ACK
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStructure.I2C_ClockSpeed = 100000;            // 100kHz标准模式
    I2C_Init(I2C1, &I2C_InitStructure);
    
    // 4. 使能I2C
    I2C_Cmd(I2C1, ENABLE);
}

/*----------------------------------------------------------
  I2C写单个字节函数
  参数:type - 命令/数据标识
        data - 要发送的数据
------------------------------------------------------------*/
static void OLED_I2C_WriteByte(uint8_t type, uint8_t data) {
    // 等待总线空闲
    while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
    
    // 发送起始条件
    I2C_GenerateSTART(I2C1, ENABLE);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
    
    // 发送设备地址(写模式)
    I2C_Send7bitAddress(I2C1, OLED_ADDRESS, I2C_Direction_Transmitter);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
    
    // 发送控制字节(命令/数据标识)
    I2C_SendData(I2C1, type);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
    
    // 发送实际数据
    I2C_SendData(I2C1, data);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
    
    // 生成停止条件
    I2C_GenerateSTOP(I2C1, ENABLE);
}

/*----------------------------------------------------------
  发送命令函数
  参数:cmd - 要发送的命令字节
------------------------------------------------------------*/
void OLED_WriteCmd(uint8_t cmd) {
    OLED_I2C_WriteByte(OLED_CMD, cmd);
}

/*----------------------------------------------------------
  发送数据函数
  参数:data - 要发送的数据字节
------------------------------------------------------------*/
void OLED_WriteData(uint8_t data) {
    OLED_I2C_WriteByte(OLED_DATA, data);
}

/*----------------------------------------------------------
  OLED初始化函数
  功能:发送初始化序列,配置显示参数
------------------------------------------------------------*/
void OLED_Init(void) {
    // 关闭显示
    OLED_WriteCmd(0xAE);
    
    // 设置时钟分频和振荡频率
    OLED_WriteCmd(0xD5);
    OLED_WriteCmd(0x80);  // 建议值
    
    // 设置多路复用率
    OLED_WriteCmd(0xA8);
    OLED_WriteCmd(0x3F);  // 1/64 duty
    
    // 设置显示偏移
    OLED_WriteCmd(0xD3);
    OLED_WriteCmd(0x00);  // 无偏移
    
    // 设置起始行地址
    OLED_WriteCmd(0x40);
    
    // 启用内部电荷泵
    OLED_WriteCmd(0x8D);
    OLED_WriteCmd(0x14);  // 启用电荷泵
    
    // 设置内存地址模式
    OLED_WriteCmd(0x20);
    OLED_WriteCmd(0x00);  // 水平地址模式
    
    // 段重映射设置
    OLED_WriteCmd(0xA1);  // 列地址127映射到SEG0
    
    // COM输出扫描方向
    OLED_WriteCmd(0xC8);  // 从COM63扫描到COM0
    
    // COM引脚硬件配置
    OLED_WriteCmd(0xDA);
    OLED_WriteCmd(0x12);  // 备用COM引脚配置
    
    // 设置对比度
    OLED_WriteCmd(0x81);
    OLED_WriteCmd(0xCF);  // 对比度值
    
    // 预充电周期设置
    OLED_WriteCmd(0xD9);
    OLED_WriteCmd(0xF1);  // Phase1 = 1 DCLK, Phase2 = 15 DCLK
    
    // VCOMH电压设置
    OLED_WriteCmd(0xDB);
    OLED_WriteCmd(0x40);  // VCOMH = 0.83×VCC
    
    // 全部像素点亮(测试模式)
    OLED_WriteCmd(0xA4);  // 正常显示
    
    // 显示反转设置
    OLED_WriteCmd(0xA6);  // 正常显示(0正常,1反转)
    
    // 开启显示
    OLED_WriteCmd(0xAF);
    
    // 清屏并刷新
    OLED_Clear();
    OLED_Refresh();
}

/*----------------------------------------------------------
  清屏函数
  功能:将显示缓冲区全部清零
------------------------------------------------------------*/
void OLED_Clear(void) {
    memset(oled_buffer, 0, sizeof(oled_buffer));
}

/*----------------------------------------------------------
  刷新显示函数
  功能:将缓冲区内容发送到OLED显示
------------------------------------------------------------*/
void OLED_Refresh(void) {
    for (uint8_t page = 0; page < OLED_PAGES; page++) {
        // 设置页地址
        OLED_WriteCmd(0xB0 | page);
        
        // 设置列地址低位(0x00)
        OLED_WriteCmd(0x00);
        // 设置列地址高位(0x10)
        OLED_WriteCmd(0x10);
        
        // 发送该页全部128列的数据
        for (uint8_t col = 0; col < OLED_WIDTH; col++) {
            OLED_WriteData(oled_buffer[col][page]);
        }
    }
}

/*----------------------------------------------------------
  显示单个字符函数
  参数:x - 水平位置(0-15,每个字符占8像素宽)
        y - 垂直页位置(0-7)
        ch - 要显示的ASCII字符
------------------------------------------------------------*/
void OLED_PrintChar(uint8_t x, uint8_t y, char ch) {
    // 参数检查
    if (x >= OLED_WIDTH/8 || y >= OLED_PAGES) return;
    
    // 获取字符点阵数据(ASCII从32开始)
    const uint8_t *font_ptr = &font_8x8[(ch - 32) * 8];
    
    // 将点阵数据写入缓冲区
    for (uint8_t i = 0; i < 8; i++) {
        oled_buffer[x * 8 + i][y] = font_ptr[i];
    }
}

/*----------------------------------------------------------
  显示字符串函数
  参数:x - 起始水平位置(0-15)
        y - 起始垂直页位置(0-7)
        str - 要显示的字符串
------------------------------------------------------------*/
void OLED_PrintString(uint8_t x, uint8_t y, const char *str) {
    while (*str) {
        OLED_PrintChar(x, y, *str++);
        x++;
        // 换行处理
        if (x >= OLED_WIDTH/8) {
            x = 0;
            y++;
            if (y >= OLED_PAGES) y = OLED_PAGES - 1;
        }
    }
}

/*****************************​ font.h 文件 ​******************************/
#ifndef __FONT_H
#define __FONT_H

// 8x8 ASCII字库(仅包含部分字符)
const uint8_t font_8x8[95][8] = {
    // 空格 (ASCII 32)
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
    // '!' (33)
    {0x18,0x3C,0x3C,0x18,0x18,0x00,0x18,0x00},
    // 更多字符数据...
    // '0' (48)
    {0x3E,0x7F,0x63,0x63,0x63,0x63,0x7F,0x3E},
    // '1' (49)
    {0x18,0x38,0x58,0x18,0x18,0x18,0x18,0x7E},
    // 添加其他必要字符...
    // 'A' (65)
    {0x18,0x3C,0x66,0x66,0x7E,0x66,0x66,0x66},
    // 'B' (66)
    {0x7E,0x33,0x33,0x3E,0x33,0x33,0x33,0x7E},
    // 补充剩余字符...
};

#endif

代码结构说明:​

  1. main.c​ - 主程序入口

    • 初始化系统时钟
    • 配置I2C外设
    • 初始化OLED并显示测试内容
  2. oled.h​ - OLED驱动头文件

    • 包含参数定义和函数声明
    • 定义显示尺寸和内存组织方式
  3. oled.c​ - OLED驱动实现

    • 包含硬件驱动层函数和显示逻辑
    • 使用I2C协议与OLED通信
    • 维护显示缓冲区并实现刷新机制
  4. font.h​ - 字库文件

    • 包含ASCII字符的8x8点阵数据
    • 每个字符使用8字节表示(垂直列)

关键函数详细说明:​

  1. I2C_Config()

    • 配置I2C1的GPIO引脚(PB6/PB7)
    • 设置I2C时钟速度为100kHz
    • 使能I2C外设
  2. OLED_I2C_WriteByte()

    • 实现I2C通信基本操作
    • 包含完整的I2C时序控制:
      • 起始条件
      • 地址发送
      • 数据传输
      • 停止条件
  3. OLED_Init()

    • 发送SSD1306初始化命令序列
    • 配置显示参数:
      • 时钟分频
      • 多路复用率
      • 内存地址模式
      • 对比度设置等
  4. OLED_Refresh()

    • 将缓冲区内容按页刷新到OLED
    • 使用水平地址模式逐列更新
  5. OLED_PrintChar()

    • 将ASCII字符转换为点阵数据
    • 支持字符定位显示
    • 自动处理字库偏移(ASCII从32开始)

硬件连接说明:​

OLED引脚    STM32连接
------------------------
VCC       3.3V
GND       GND
SCL       PB6 (I2C1_SCL)
SDA       PB7 (I2C1_SDA)

使用注意事项:​

  1. 字库需要完整实现ASCII 32-126字符
  2. 实际显示坐标系统:
    • X轴:0-15(每个字符8像素宽)
    • Y轴:0-7(每页8行)
  3. 修改显示内容后必须调用OLED_Refresh()
  4. 可以通过修改oled_buffer直接操作像素
;