STM32入门HAL库-硬件I2C与0.96寸OLED

目录

1. 目标

2. 背景知识

2.1 I2C基础

2.2 0.96寸OLED基础

3 代码移植

3.1 改写为HAL库

3.2 改写为硬件I2C

4 流程略解

附录


1. 目标

通过HAL库操作I2C,点亮OLED屏

2. 背景知识

2.1 I2C基础

I2C是一种同步半双工总线协议,具有主机和从机的区分

典型的I2C需要:数据线SDA,时钟线SCL

起始:SCL为高,SDA有下降沿。停止:SCL为高,SDA有上升沿

逻辑0:SCL为高,SDA稳定低电平。逻辑1:SCL为高,SDA稳定高电平

写数据流程:

起始   -   设备地址(7位)+操作(1位)+应答(1位)   -   寄存器地址(8位)+应答(1位)   -   数据(8位)+应答(1位)   -   停止

读数据流程:

起始   -   设备地址(7位)+操作(1位)+应答(1位)   -   寄存器地址(8位)+应答(1位)   -   起始   -   设备地址(7位)+操作(1位)   -   数据(8位)+应答(1位)   -   停止

有趣的视频,光敏+纸带I2C

2.2 0.96寸OLED基础

可能有错,所以,这一小节可以跳过,只是出于文章的逻辑完整性放在这里,提供参考借鉴

0.96寸OLED屏幕像素为128x64

而实际控制OLED的是内部的SSD1315,SSD1315是一款集成了控制器的单芯片CMOS OLED/PLED驱动器,SSD1315可以直接从其内部的128 x 64位图形显示数据RAM(GDDRAM)显示数据

主机和OLED通过I2C交换数据,所以首先需要知道从机的地址,查阅手册,在写模式下,地址可以是 0111 1000 即0x78

对于操作,显然应该有命令和数据的区分,根据手册,区分应该是根据D/C#高低电平控制的,但实际上HAL库对这个字段的描述是MemAddress寄存器地址?不是很懂,这里

根据手册,结合代码,写入指令应该是0x00,数据应该是0x40 (D/C#高电平)

对于指令,可以参考代码注释和手册,这里就不过多叙述了

对于数据,需要知道数据如何存储(虽然也没有弄太清楚),结合手册,大概长这样。一共分为8页,每页可以划分为128SEG或8COM

在不重映射的前提下,大概是这样的,以第2页为例

然后写入数据大概有三种模式:页模式,水平模式和垂直模式

3 代码移植

显然,不是所有硬件都需要重复造轮子,所以,选用卖家代码为模板进行移植

代码为标准库+软件I2C,目标将其改写为HAL库+硬件I2C

3.1 改写为HAL库

先改写为HAL库检查代码是否能够正常运行

按照哪里标红改哪里的原则进行修改

先到oled.h中检查

删除sys.h后,先加入简单的宏定义

#include <stdio.h>
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t

再把标准库所写的宏定义进行改写 

#include "main.h"
// 标准库
#define OLED_SCL_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_0)
#define OLED_SCL_Set() GPIO_SetBits(GPIOA,GPIO_Pin_0)
// HAL库
#define OLED_SCL_Clr() HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET)
#define OLED_SCL_Set() HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET)

接着在oled.c中检查

在OLED_init()中,对GPIO口进行了初始化,改为CubeMX初始化,GPIO口设置为输出模式 

接下来把延时函数替换为HAL库

// 原始代码
delay_ms(200);
// HAL库
HAL_Delay(200);

最后回到main.c,添加测试代码进行测试 

OLED_Init ();
OLED_ColorTurn(0);
OLED_DisplayTurn(0);
OLED_ShowString(0, 0, "Hello World !", 8, 1);
OLED_Refresh ();

烧录代码,我们可以惊奇的发现,显示屏显示Hello World ! ,说明代码不存在问题

3.2 改写为硬件I2C

通过分析,可以发现实际上对I2C的操作基于以下部分代码

一部分是在oled.h中的宏定义,另一部分是在oled.c中的函数

#define OLED_SCL_Clr() HAL_GPIO_WritePin(GPIOF,GPIO_PIN_0,GPIO_PIN_RESET)//SCL
#define OLED_SCL_Set() HAL_GPIO_WritePin(GPIOF,GPIO_PIN_0,GPIO_PIN_SET)
#define OLED_SDA_Clr() HAL_GPIO_WritePin(GPIOF,GPIO_PIN_1,GPIO_PIN_RESET)//DIN
#define OLED_SDA_Set() HAL_GPIO_WritePin(GPIOF,GPIO_PIN_1,GPIO_PIN_SET)

//延时
void IIC_delay (void);
//起始信号
void I2C_Start (void);
//结束信号
void I2C_Stop (void);
//写入一个字节
void Send_Byte (u8 dat);
//发送一个字节
void OLED_WR_Byte (u8 dat, u8 mode);

根据2.1 I2C基础知识,我们可以将代码流程进行梳理,并进行改写

/**
 * @brief 通过I2C发送1字节数据
 * @param dat 数据
 * @param isData 是数据吗(数据/指令)
 */
void OLED_WR_Byte (u8 dat, u8 isData) {
    u8 oledAddress = 0x78;
    u8 dataAddress = 0x40;
    u8 commondAddress = 0x00;
    if (isData)
        HAL_I2C_Mem_Write(&hi2c2, oledAddress, dataAddress, I2C_MEMADD_SIZE_8BIT, &dat, 1, 20);
    else
        HAL_I2C_Mem_Write(&hi2c2, oledAddress, commondAddress, I2C_MEMADD_SIZE_8BIT, &dat, 1, 20);
}

将软件I2C宏定义和函数删除后, 根据标红地方,该删删,该改为OLED_WR_Byte就改

至此,改写为硬件I2C就结束了,可以在CubeMX中更改I2C速率以享受高帧率体验

另外,在OLED_Refresh函数中会传输一个128字节的数据,可以先把数据放在一个数组里面,再一次性发送数组(128字节),这样帧率又可以提高亿点

4 流程略解

所有画图操作,本质上都是操作这个数组 u8 OLED_GRAM[144][8]; 其实这里128应该就够了

数组共128行,代表128个SEG;每行8列,每列代表一页中的8个COM(1字节8位)

只有在OLED_Refresh (void)或者void OLED_Clear (void)时,会将该数组通过I2C发送到OLED实现刷新

写入时是按照页模式写入,需一页一页写,所以在OLED_Refresh (void)中会将数组做一次转换再进行写入

附录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值