STM32 基于HAL库的硬件I2C驱动SSD1306

本文详细介绍了SSD1306 OLED驱动的I2C通信流程,包括地址设置、有效信息构成、寻址模式(页寻址、水平/垂直寻址)、初始化步骤和驱动实现。通过硬件I2C,掌握如何调整对比度、设置显示状态和优化通信效率。
摘要由CSDN通过智能技术生成


SSD1306 介绍

SSD1306 是一款 OLED 驱动芯片,可以驱动 64*128px 的OLED屏幕。允许主机使用 I2C、6800、8080、4线SPI、3线SPI 方式进行通信,为 BS[2:0] 设置不同的电平以实现通信方式的选择,本文介绍的是采用硬件 I2C 的通信方式,。


I2C 通信流程

I2C通信流程

上图所示为 I2C 通信,将连续的 Control byteData byte 称为一组有效信息,可以看到,发出地址字节后,主机必须发送至少1次的有效信息。

I2C 通信地址

SSD1306 的 I2C 地址中 [1:7] 位为 0111100 或 0111101,第1位代表进行读或写操作,由于 OLED 为一输出设备,故第1位应固定为0,所以SSD1306的两种 I2C 地址位 0x780x7A

有效信息

一个 Control byte 与一个 Data byte 构成一组有效信息,其中 Control byte 为:

76543210
CoD/C#000000
  • Co位被设置为逻辑“0”,表示其后会传输一位 Data byte

  • D/C位决定将后一位 Data byte 解释为命令或数据。如果D/C位是“0”,则将 Data byte 定义为命令。若为“1”,则将 Data byte 定义为数据。

使用硬件 I2C 通信

SSD1306 的一次有效通信中应至少包含一组有效信息,故可简化为,每次与 SSD1306 通信均只发送一组有效信息。则这次通信中共发送三个字节,分别是 地址Control byteData byte。故可以使用 HAL 库中提供的 HAL_I2C_Mem_Write 方法,将 Control byte 作为寄存器地址发送,并再发送一字节的数据。其中地址固定为 0x78Control byte0x400x00


SSD1306 结构

SSD1306 将 OLED 屏幕划分成了 8 页,每页中右 8 行、128列,如下图所示:

屏幕分区

寻址模式 (内容更新模式)

由于一次有效通信只能写入一字节的数据,只允许更新某一页中某一列共8位的显示内容。倘若在每次更新前指定更新位置,则存在一定的浪费,SSD1306 提供了三种寻址模式,可以连续更新显示内容。

页寻址

页寻址是默认的寻址模式,它具有以下特性:

  1. 列地址指针每次自动加1,如果列地址指针到达最后一个地址,将会自动复位到开始的地址;
  2. 页地址不会自动增加,必须手动设置新的页地址;

页寻址

在这个模式下,设置页地址需要通过以下一个字节实现

76543210作用
10110X2X1X0设置页地址

发送以上字节,则页地址被设置为 X[2:0],

在这个模式下,设置列地址需要通过以下两个字节实现

76543210作用
0000X3X2X1X0设置列地址低四位
0000X7X6X5X4设置列地址高四位

连续发送以上两个字节,则列地址被设置为 X[7:0]

水平寻址模式

在水平寻址模式下,列地址指针自动加1,如果列地址指针到达最后一个地址,将会自动复位到开始的地址,同时页地址指针也自动加1。

水平寻址模式

垂直寻址模式

在垂直寻址模式下,页地址指针自动加1,如果页地址指针到达最后一个地址,将会自动复位到开始的地址,同时列地址指针也自动加1。

垂直寻址模式

在水平和垂直寻址模式下,需要设置页的起始和结束位置、列的起始和结束位置,需发送以下字节:

76543210作用
00100001开始设置列地址
*A6A5A4A3A2A1A0设置起始列地址为 A[6:0],默认为0
*B6B5B4B3B2B1B0设置结束列地址为 B[6:0],默认为127
00100010开始设置页地址
*****A2A1A0设置起始页地址为 A[2:0],默认为0
*****B2B1B0设置结束页地址为 B[2:0],默认为7

* 表示此位置对结果不产生影响

设置寻址模式

设置寻址模式需要发送以下字节:

76543210作用
00100000开始设置寻址模式
******A1A000b 水平寻址模式;01b 垂直寻址模式;10b 页寻址模式

SSD1306 初始化

SSD1306 上电后应首先进行初始化,我将会选择初始化中几个重要步骤进行解释:

关闭显示器

上电第一步应关闭显示器,以避免初始化过程中屏幕上显示寄存器中存留的内容,需发送以下字节

76543210作用
1010111XX=0 显示关;X=1 显示开

设置对比度

使用以下字节设置对比度

76543210作用
10000001开始设置对比度
A7A6A5A4A3A2A1A0设置对比度为A[7:0],默认为0x7F

设置整体显示状态

使用以下字节设置显示状态

76543210作用
1010010XX=0 显示RAM中的内容;X=1 不显示RAM中的内容

其他命令需阅读datasheet
https://wenku.baidu.com/view/6d6497a53d1ec5da50e2524de518964bce84d233.html

详细流程

初始化包括很多环节的设置,由于不能确定 MCU 上电时 SSD1306 的状态,所以即使我们需要的状态时默认状态,还是建议重新进行设定。
详细代码如下,可以参照注释进行分析。

    OLED_Write_Byte(CmdReg, OLED_OFF);  //关闭显示器
    OLED_Write_Byte(CmdReg, 0x20);  //设置内存寻址模式Set Memory Addressing Mode
    // 00,水平寻址模式 01,垂直寻址模式 02,页面寻址模式(默认)
    OLED_Write_Byte(CmdReg, 0x01);
    OLED_Write_Byte(CmdReg, 0x81);  //设置对比度
    OLED_Write_Byte(CmdReg, 0xff);  //对比度,数值越大对比度越高
    OLED_Write_Byte(CmdReg, 0xc8);  //扫描方向 不上下翻转Com scan direction
    OLED_Write_Byte(CmdReg, 0xa1);  //设置段重新映射 不左右翻转set segment remap
    OLED_Write_Byte(CmdReg, 0xa8);  //设置多路复用比(1-64)
    OLED_Write_Byte(CmdReg, 0x3f);  //设定值1/32  1/32 duty
    OLED_Write_Byte(CmdReg, 0xd3);  //设置显示偏移 set display offset
    OLED_Write_Byte(CmdReg, 0x00);  //
    OLED_Write_Byte(CmdReg, 0xd5);  //设置osc分区 set osc division
    OLED_Write_Byte(CmdReg, 0x80);  //
    OLED_Write_Byte(CmdReg, 0xd8);  //关闭区域颜色模式 set area color mode off
    OLED_Write_Byte(CmdReg, 0x05);  //
    OLED_Write_Byte(CmdReg, 0xd9);  //设置预充电期 Set Pre-Charge Period
    OLED_Write_Byte(CmdReg, 0xf1);  //
    OLED_Write_Byte(CmdReg, 0xda);  //设置com引脚配置 set com pin configuartion
    OLED_Write_Byte(CmdReg, 0x12);  //
    OLED_Write_Byte(CmdReg, 0xdb);  //设置vcomh set Vcomh
    OLED_Write_Byte(CmdReg, 0x30);  //
    OLED_Write_Byte(CmdReg, 0x8d);  //设置电源泵启用 set charge pump enable
    OLED_Write_Byte(CmdReg, 0x14);  //
    OLED_Write_Byte(CmdReg, 0xa4);  //设置全局显示  bit0,1白,0黑
#ifdef OLED_INVERSE_COLOR
    OLED_Write_Byte(CmdReg, 0xa7);  //反相显示
#endif
#ifndef OLED_INVERSE_COLOR
    OLED_Write_Byte(CmdReg, 0xa6);  //正常显示
#endif
    OLED_Fill(0x00);                      //清屏
    OLED_Write_Byte(CmdReg, OLED_ON);  //打开oled面板 turn on oled panel

SSD1306 驱动

由于 SSD1306 在更新时会将同一页中本列的所有数据都进行更新,当需要显示的数据并没有占满整页的高度时,可能会擦除掉本页中其他行的图像。所以,建立一个缓冲区十分的有必要。

我编写了一个带有缓冲区的驱动,具有以下功能:

  • 绘制图象时不受 page 限制,可以于屏幕上的任何坐标绘制。
  • 图像布尔运算功能,实现图像的加减。
  • 数据暂存缓冲区,统一进行通信,实现局部刷新。
  • 计算通信较优方案,使得I2C通信次数较少。

https://gitee.com/snitro/stm32_hal_iic_ssd1306

  • 7
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用STM32HAL库驱动SSD1306 OLED显示屏的IIC接口的示例代码: ```c #include "stm32f4xx_hal.h" #include "ssd1306.h" I2C_HandleTypeDef hi2c1; #define SSD1306_I2C_ADDR 0x78 // SSD1306 I2C地址 void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_I2C1_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_I2C1_Init(); ssd1306_Init(&hi2c1); ssd1306_Fill(White); // 填充白色 ssd1306_UpdateScreen(&hi2c1); // 更新屏幕 HAL_Delay(1000); ssd1306_Fill(Black); // 填充黑色 ssd1306_UpdateScreen(&hi2c1); // 更新屏幕 while (1) { } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; RCC_OscInitStruct.PLL.PLLM = 8; RCC_OscInitStruct.PLL.PLLN = 336; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 7; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } 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_DIV4; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) { Error_Handler(); } } static void MX_I2C1_Init(void) { 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(); } } static void MX_GPIO_Init(void) { __HAL_RCC_GPIOC_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_8; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); } void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(i2cHandle->Instance==I2C1) { __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_I2C1_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } } void HAL_I2C_MspDeInit(I2C_HandleTypeDef* i2cHandle) { if(i2cHandle->Instance==I2C1) { __HAL_RCC_I2C1_CLK_DISABLE(); HAL_GPIO_DeInit(GPIOB, GPIO_PIN_6 | GPIO_PIN_7); } } ``` 上述代码中,我们通过HAL库初始化了I2C1接口,并在SSD1306 OLED显示屏上绘制了白色和黑色的屏幕。在使用此代码时,需要包含ssd1306.h和ssd1306.c文件,这些文件包含了SSD1306 OLED显示屏的驱动程序。同时需要注意的是,需要根据实际的硬件连接情况修改I2C引脚的定义。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值