当前位置: 首页 > news >正文

0.96寸OLED显示屏详解

我们之前讲了 LCD1602,今天我们将它的进阶模块——OLED。它接线更少,性能更强,也能显示中文和图像了。

大家在学习单片机的时候是否会遇到调试的问题呢?例如 “这串代码我到底运行成功了没有” ,我相信很多刚开始学习单片机的小白一般都遇到过这样的问题。但是单片机不像电脑,是没有屏幕也没有输出结果窗口的。那么我们该如何得知调试的结果呢?有一个方法是通过串口打印在运行成功代码会向串口发送特定的字符到电脑上,但是这个方法的弊端是如果单片机被放置到一个实际的项目当中,就无法通过串口来得知结果了。还有一个方法就是将结果打印到屏幕上,当然我们的单片机是没有屏幕的,所以我们需要为单片机增添一个外置的屏幕模块。我们今天就来学习一下如何使用 OLED 屏幕模块。

OLED(Organic Light-Emitting Diode)具有高亮度、高对比度和快速响应的特点,能够提供生动逼真的图像和文字显示效果。由于不需要背光源,OLED 屏更薄、更轻、更省电。它在智能手机、可穿戴设备、汽车仪表盘等领域得到广泛应用。本文将通过介绍 OLED 的工作原理、特性和应用案例,带大家深入了解 OLED。

1. 源码下载及前置阅读

本文首发 良许嵌入式网 :https://www.lxlinux.net/e/ ,欢迎关注!

本文所涉及的源码及安装包如下(由于平台限制,请点击以下链接阅读原文下载):

https://www.lxlinux.net/e/stm32/oled-tutorial.html

如果你是嵌入式开发小白,那么建议你先读读下面几篇文章。

  • 嵌入式基本功,为后续学习打下坚实的基础:STM32串口接收不定长数据(接收中断+超时判断)
  • 手把手教你安装和使用STM32CubeMX:一文教你使用STM32CubeMX开发工具
  • 手把手让你掌握MDK的使用方式和技巧,助你更高效地进行开发:一文教你使用MDK开发工具

往期教程,有兴趣的小伙伴可以看看。

  • 如果想知道自己对嵌入式知识掌握程度:嵌入式自测题
  • 蓝牙模块详解,轻松掌握一种无线通信技术:手把手教你玩转蓝牙模块(原理+驱动)
  • 让你轻松掌握温湿度传感器:手把手教你玩转DHT11(原理+驱动)
作者简介
大家好,我是良许,博客里所有的文章皆为我的原创。
下面是我的一些个人介绍,欢迎交个朋友:
· 211工科硕士,国家奖学金获得者;
· 深耕嵌入式11年,前世界500强外企高级嵌入式工程师;
· 书籍《速学Linux作者》,机械工业出版社专家委员会成员;
· 全网60W粉丝,博客分享大量原创成体系文章,全网阅读量累计超4000万;
· 靠自媒体连续年入百万,靠自己买房买车。

我本科及硕士都是学机械,通过自学成功进入世界500强外企。我已经将自己的学习经验写成了一本电子书,超千人通过此书学习并转行成功。现在将这本电子书免费分享给大家,希望对你们有帮助:

电子书链接:https://www.lxlinux.net/1024.html

2. IIC通信

OLED 屏幕模块是通过 IIC 进行通信的,什么是 IIC 通信呢?

2.1 IIC通信介绍

IIC 通信通常也叫 I2C,I2C( Inter-Integrated Circuit ),中文应该叫集成电路总线,它是一种串行通信总线,使用多主从架构,是由飞利浦( Philips )公司在 1980 年代初设计的一种串行、半双工的通信,主要用于近距离、低速的芯片之间的通信。I2C 总线有两根双向的信号线,一根数据线 SDA 用于收发数据,一根时钟线 SCL 用于通信双方时钟的同步。I2C 总线硬件结构简单,简化了 PCB 布线,降低了系统成本,提高了系统可靠性,由于它的简单性,它被广泛用于微控制器与传感器阵列,显示器,IoT 设备,EEPROM 等之间的通信。

I2C 通信协议的特点:

  • 同步、半双工;

  • 只需要两条总线;

  • 所有组件之间都存在简单的主/从关系,连接到总线的每个设备均可通过唯一地址进行软件寻址;

  • I2C 是真正的多主设备总线,可提供仲裁和冲突检测;

  • 没有严格的波特率要求,例如使用 RS232 ,主设备生成总线时钟;

I2C 总线是一种多主机总线,连接在 I2C 总线上的器件分为主机和从机。主机每一次发起和结束一次通信,从机只能被动呼叫;当总线上有多个主机同时启用总线时,I2C 也具备冲突检测和仲裁的功能来防止错误产生。总线具有极低的电流消耗,抗噪声干扰能力强,增加总线驱动器可以使总线电容扩大10倍,传输距离达到 15m ;兼容不同电压等级的器件,工作温度范围宽。

I2C 总线可以通过外部连线进行在线检测,便于系统故障诊断和调试,故障可以立即被寻址,软件也有利于标准化和模块化,缩短开发时间。

每个连接到 I2C 总线上的器件都有一个唯一的地址(7bit),且每个器件都可以作为主机也可以作为从机(但是同一时间主机只能有一个),总线上的器件增加和删除不影响其他器件正常工作。I2C 总线在通信时总线上发送数据的器件为发送器,接收数据的器件为接收器。

I2C 总线上可挂接的设备数量受总线的最大电容 400pF 限制。串行的8位双向数据传输速率在标准模式下可达 100Kbit/s ,快速模式下可达 400Kbit/s ,高速模式下可达 3.4Mbit/s 。

2.2 IIC通信协议

  • 那么 I2C 是如何实现通信的呢?

    1. 主机发送起始信号然后启用总线;
    2. 主机发送一个字节数据指明从机地址和后续字节的传送方向;
    3. 被寻址的从机发送应答信号去回应主机;
    4. 发送器发送一个字节数据;
    5. 接收器发送应答信号回应发送器;
    6. 接下来会一直重复 4 和 5 的步骤直到通信完成后主机发送停止信号并释放总线。
  • 起始信号与停止信号

    当 SCL 为高电平时,SDA 产生下降沿,就是电平由高变低表示起始信号;

    当 SCL 为高电平时,SDA 产生上升沿,就是电平由低变高表示停止信号;

    当 SCL 与 SDA 同时为高电平时,表示当前为空闲状态。

    起始信号和停止信号都是由主机发出,起始信号产生后总线处于占用状态,停止信号产生后总线被释放,处于空闲状态。

  • 应答信号与非应答信号

    I2C 总线上的所有数据都是以 8bit 字节传输的,发送器每发送一个字节,就在第 9 个时钟开始时释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。对于反馈有效应答位 ACK 的要求是,接收器在第 9 个时钟脉冲之前的低电平期间将 SDA 线拉低,并且确保在该时钟的高电平期间为稳定的低电平。 如果接收器是主控器,则在它收到最后一个字节后,发送一个 NACK 信号,以通知被控发送器结束数据发送,并释放 SDA 线,以便主控接收器发送一个停止信号。

2.3 软件模拟IIC通信

STM32 的硬件 I2C 存在着一些 BUG ,有时候莫名会出现问题,所以大多数情况下都会使用软件模拟 I2C 。使用软件模拟 I2C 主要是方便程序的移植,在使用时只需要修改一下相应的 GPIO 口即可。而 oled 屏幕模块就是使用软件模拟 I2C 通信的,我们将会在下文学习软件模拟 I2C 通信。

3. OLED屏幕

OLED 屏幕模块型号多种多样,屏幕大小、引脚(4P 支持 I2C,7P 支持 I2C 和 SPI)、字体颜色不同。本次我们以 0.96 寸,4P,白色为例来讲解,一通百通。

3.1 0.96OLED屏幕介绍

0.96 寸 4P OLED 屏幕模块是一种显示屏模块,它包括一个 0.96 英寸的 OLED 显示屏和四个引脚。这种 OLED 屏幕模块通常用于嵌入式系统和小型电子设备中,可以显示文本、图像和其他类型的信息。由于其小尺寸和低功耗,也常用于智能手表、健康追踪器和其他便携式设备中。

下面这个是一款常见的 0.96 寸四针 OLED 屏幕模块,某宝价格在 7~15 左右。

0.96OLED 参数:

  • 驱动芯片:SSD1306
  • 分辨率:128 × 64
  • 显示尺寸:0.96英寸
  • 外形尺寸:27.5 × 27.8(mm)
  • 玻璃尺寸:26.7 × 19.26 × 1.4(mm)
  • 显示区域:21.74(W)× 10.864(mm)
  • 视角:全视角
  • 工作电压:3.3V ~ 5V
  • 工作温度:-20℃ ~ 70℃
  • 存储温度:-30℃ ~ 80℃
  • 使用寿命:>=16000 小时
  • 支持接口:I2C

参考接线如下:

OLEDSTM32备注
GNDG电源负极
VCC3.3/5V电源正极
SCLB8(SCL1)/B10(SCL2)时钟线
SDAB9(SDA1)/B11(SDA2)数据线

0.96OLED 屏幕模块的优点有:

  1. 高对比度和高亮度:OLED 屏幕模块可以提供深黑色背景和明亮的字色,因此可以实现高对比度和高亮度的显示效果;
  2. 自发光:OLED 屏幕模块不需要背光灯,因此可以实现更薄、更轻的设计;
  3. 响应速度快:OLED 屏幕模块的响应速度非常快,可以实现流畅的动画和视频效果;
  4. 视角宽:OLED 屏幕模块的视角非常宽,即使从不同角度观察,图像和文字也能保持清晰。

0.96OLED 屏幕模块的缺点有:

  1. 显示寿命短:与 LCD 显示屏相比,OLED 的显示寿命相对较短,因为它的有机材料会随着时间的推移而逐渐失效;
  2. 显示面积小:0.96 寸 OLED 屏幕模块的显示面积相对较小,因此不适合用于需要大屏幕的应用。

3.2 SSD1306介绍 

接下来我们介绍一下 OLED 屏模块的芯片——SSD1306。

SSD1306 是一款带控制器的用于 OLED 点阵图形显示系统的单片 CMOS OLED/PLED 驱动器。它由 128 个 SEG(列输出)和 64 个 COM(行输出)组成。该芯片专为共阴极 OLED 面板设计。 SSD1306 内置对比度控制器、显示 RAM(GDDRAM)和振荡器,以此减少了外部元件的数量和功耗。该芯片有 256 级亮度控制。数据或命令由通用微控制器通过硬件选择的 6800/8000 系通用并行接口、I2C 接口或串行外围接口发送。该芯片适用于许多小型便携式应用,如手机副显示屏、MP3 播放器和计算器等。

SSD1306 芯片结构图:

SSD1306 芯片引脚图:

SSD1306 芯片引脚说明:

  • NC:空引脚,不要短接;

  • VCC:显示面板驱动电源引脚;

  • VCOMH:用于 COM 的高电平电压输出的引脚,与 VSS 之间应该连接一个电容;

  • IREF:段输出电流参考脚,VSS 间应连接一个电阻,以将 IREF 电流保持在 12.5uA ;

  • D[7~0]:连接到 MCU 的 8 位双向数据总线,不同 MCU 总线接口有不同用法;

  • E (RD#):与 6800/8080 通用并行总线接口相关,使用串行接口时作拉高处理;

  • R/W#(WR#):与 6800/8080 通用并行总线接口相关,使用串行接口时作拉高处理;

  • D/C#:数据/命令控制脚,不同 MCU 总线接口有不同用法;

  • RES#:复位信号引脚,低电平有效;

  • CS#:芯片片选引脚,低电平有效;

  • B[2:0]:MCU 总线接口选择脚。通过配置 B0~B2 来选择不同的 MCU 总线接口;

  • VDD:芯片逻辑器件供电引脚;

  • VSS:接地引脚;

  • CL:外部时钟输入引脚。用外部时钟时,此引脚输入外部时钟信号。用内部时钟时应连接到 VSS ;

  • CLS:内部时钟启用引脚。拉高时内部时钟启用,拉低时内部时钟禁用;

  • FR:用于输出 RAM 写同步信号,不使用应当 NC pin 处理;

  • TR0~TR6:测试保留引脚,当做 NC pin 处理;

  • VLSS:模拟接地引脚,在外部连接到 VSS ;

  • SEG0~SEG127:列输出脚,OLED 关闭时,处于 VSS 状态;

  • COM0~COM63:行输出脚,OLED 关闭时,处于高阻抗状态。

SSD1306 的初始化过程:

当 RES# 输入低电平时

  1. 首先关闭显示(AEH)
  2. 然后进入 128x64 显示模式
  3. 接着恢复到默认的 SEG 和 COM 映射关系(A0H,D3H-00H)
  4. 清除串行接口中移位寄存器内的数据
  5. GDDRAM 显示开始行设为0(40H)
  6. 列地址计数器重置为 0
  7. 恢复到默认的 COM 扫描方向(C0H)
  8. 对比度寄存器初始化为 7FH(81H-7FH)
  9. 正常显示模式(A4H)

基础配置: IIC 总线包含从机地址位 SA0,数据信号线 SDA(SDAOUT/D2 输出和 SDAIN/D1 输入)和时钟信号线 SCL组成。SDA 和 SCL 线都必须接上拉电阻,RES#用来初始化芯片。 IIC设备在数据传输之前都必须识别从机地址。SSD1306 的从机地址有 0111100b 和 0111101b 两种,通过将 SA0(D/C#)脚上拉到高电平可以设置从机地址第七位为 1,将SA0(D/C#)脚下拉到低电平可以设置从机地址第七位为 0。通过SA0(D/C#)脚的上拉和下拉来设置从机地址,从而令总线上可以存在最多 2 个 SSD1306 驱动器。

B7B6B5B4B3B2B1B0
0111100R/W#

​ SDAOUT/D2和SDAIN/D1连接到一起作为SDA。SDAIN引脚必须连接到SDA,SDAOUT引脚可以不连接。当SDAOUT引脚不连接,应答信号将会被12C总线忽略。

写入时序:

  1. 主机先发起开始(START)信号,然后发送 1byte 首字节,包括从机地址(7位)和读写数据位(1 位,最低位,0 为写模式),驱动器识别从机地址为本机地址之后,将会发出应答信号(ACK)。

  2. 主机收到从机(驱动器)的应答信号之后,随后传输 1byte 控制字节。一个控制字节主要由CO 和 D/C# 位后面再加上六个 0 组成的。如果 Co 为 0 ,后面传输的信息就只包含数据字节。D/C# 位决定了下个数据字节是作为命令还是数据。D/C# 为 0 时,下一个数据被视为命令;DC# 为 1 时,下一个数据被视为显示数据,存储到 GDDRAM 中。

  3. 收到控制字节 ACK 信号之后,传输要写入的数据字节。

  4. 传输完毕之后主机发出结束(STOP)信号。

3.3 GDDRAM介绍

SSD1306 的 GDDRAM 用于存储将显示在 128×64 单色点阵屏上的图像,其中每个位对应屏幕上的一个像素。此 RAM 大小为 128×64 位,被分成八个页面,如下表格所示

Page 0
Page 1
Page 2
Page 3
Page 4
Page 5
Page 6
Page 7
  • 每个页面的分配情况为:

    PAGE0(COM0-COM7)

    PAGE1 -> COM8-COM15

    PAGE2 -> COM16-COM23

    PAGE3 -> COM24-COM31

    PAGE4 -> COM32-COM39

    PAGE5 -> COM40-COM47

    PAGE6 -> COM48-COM55

    PAGE7 -> COM56-COM63

  • 若行重映射(Row re-mapping),则为:

    PAGE0 -> COM56-COM63

    PAGE1 -> COM48-COM55

    PAGE2 -> COM40-COM47

    PAGE3 -> COM32-COM39

    PAGE4 -> COM24-COM31

    PAGE5 -> COM16-COM23

    PAGE6 -> COM8-COM15

    PAGE7 -> COM0-COM7

  • 段的写入顺序:

    SEG0 至 SEG127

  • 若列重映射(column re-mapping),则为:

    SEG127 至 SEG0

    当向 GDDRAM 写入一个字节时, 此字节将写入当前页的当前段(当前列, 即列地址指针(column address pointer)指向的列), 字节的 LSB 写入当前最低列, MSB 写入当前最高列。

3.4 GDDRAM寻址模式

GDDRAM 有三种寻址模式:

  1. 页寻址

    在页寻址模式下,读写显示 RAM 后,列地址指针自动增加 1。如果列地址指针到达列结束地址,则列地址指针将被重置为列起始地址,而页面地址指针将不会被更改。用户必须设置新的页面和列地址,才能访问下一页 RAM 内容。页面寻址模式的 page 和列地址点的移动顺序如图:

  2. 垂直寻址

    在水平寻址模式下,读写显示 RAM 后,列地址指针自动增加 1。如果列地址指针到达列结束地址,则将列地址指针重置为列起始地址,页地址指针增加 1。水平寻址模式下的页面和列地址点的移动顺序如图所示(起始页为 0,终止页为 7。起始列为 0,终止列为127)。当列和页面地址指针都到达结束地址时,指针将重置为列起始地址和页面起始地址(图中的虚线)。

  3. 水平寻址

    在垂直寻址模式下,读写显示 RAM 后,页面地址指针自动增加 1。如果页面地址指针到达页面结束地址,则页面地址指针重置为页面起始地址,列地址指针增加 1。垂直寻址模式的页面和列地址点的移动顺序如图所示(起始页为 0,终止页为 7。起始列为 0,终止列为127)。当列和页面地址指针都到达结束地址时,指针将重置为列起始地址和页面起始地址(图中的虚线)。

3.5 指令详解

3.5.1 基础指令

1.设置对比度 (指令为 “81H+A[7:0]“ )   这是一条双字节的指令,由第二条指令指定要设置的对比度级数。这条指令用于设置屏幕的对比度。   A[7:0] 从 00HFFH 分别指定对比度为 1256 级。SEG(段)输出的电流大小随对比度级数的增加而增加。

2.设置全屏全亮 (指令为 ”A4H / A5H“ )   这是一条单字节的指令,用于开关屏幕全亮模式。   A4H 将屏幕显示模式设置为正常模式,这时屏幕将会显示 GDDRAM 中输出的数据。   A5H 将屏幕显示模式设置为全亮模式,这时屏幕将会无视 GDDRAM 中的数据,并点亮全屏。   通过 A5H 设置全屏点亮之后可以通过 A4H 来回复正常显示。

3.设置正常/反转显示 (指令为 ”A6H / A7H“ )   这是一条单字节的指令,用于设置屏幕的显示模式。   A6H 设置显示模式为 1 亮 0 灭,而 A7H 设置显示模式为 0 亮 1 灭。

4.开关显示屏 (指令为 ”AEH / AFH” )   这是一条单字节的指令。   AEH 关闭屏幕,而 AFH 开启屏幕。   屏幕关闭时,所有 SEG 和 COM 的输出被分别置为 Vss 和高阻态。

3.5.2 硬件指令

1.设置GDDRAM起始行 (指令为 ”40H7FH“ )   这是一条单字节的指令。   高 2 位规定为 01b,由低 6 位的取值来决定起始行。整体指令从 40H7FH 分别设置起始行为 0~63。

2.设置SEG映射关系 (指令为 ”A0H / A1H“ )   这是一条单字节的指令。   A0H 设置 GDDRAM 的 COL0 映射到驱动器输出 SEG0 。   A1H 设置 COL127 映射到 SEG0 。

3.设置COM 扫描方向 (指令为 ”C0H / C8H“ )   这是一条单字节的指令。   C0H 设置 从 COM0 扫描到 COM[N-1] ,N 为复用率。   C1H 设置 从 COM[N-1] 扫描到 COM0 。

4.设置复用率 (指令为 ”A8H+A[5:0]“ )   这是一条双字节的指令,由 A[5:0] 指定要设置的复用率。   复用率(MUX ratio)即选通的 COM 行数,不能低于 16 ,通过 A[5:0] 来指定。   A[5:0] 高两位无规定视为 0 ,所以第二条指令从 0FH3FH 的取值设置复用率为 164(即A[5:0]+1)。A[5:0] 从 0 到 14 的取值都是无效的。

5.设置垂直显示偏移 (指令为 ”D3H+A[5:0]“ )   这是一条双字节的指令,由 A[5:0] 指定偏移量。   垂直显示偏移即整个屏幕向上移动的行数,最顶部的行会移到最底行。   A[5:0] 高两位无规定视为 0 ,所以第二条指令从 0FH3FH 的取值设置垂直偏移为 063 。

6.设置COM硬件配置 (指令为 ”DAH+A[5:4]“ )   这是一条双字节指令,由 A[5:4] 进行设置。   A[5] 位设置 COM 左右反置,A[4] 用来设置序列/备选引脚配置,其他位有规定,规定如下所示。

  SSD1306 的 COMn 引脚一共有左边 COM32COM63 和右边 COM0COM31 共 64 个(金手指面朝上方)。通过设置 A[5] 可以让左右 COM 引脚的输出互换。A[5]=0 时禁止左右反置,A[5]=1 时启用左右反置。   COM 引脚的排列有序列和奇偶间隔(备选)两种,通过 A[4] 进行设置。A[4]=0 时使用序列 COM 引脚配置,A[5]=1 时使用奇偶间隔(备选)COM 引脚配置。

3.5.3 地址指令

1.设置GDDRAM寻址模式 (指令为 “20H+A[1:0]” )  这是一条双字节的指令,由 A[1:0] 指定要设置的地址模式。   A[1:0]=00b 时为水平地址模式;

​ A[1:0]=01b 时为垂直地址模式;

​ A[1:0]=10b 时为页地址模式;

​ A[1:0]=11b 时为无效指令;  由于第二条指令前 6 位值无规定,所以直接用 0 替代,可以得到:00H- 水平;01H- 垂直;02H 页。

2.设置起始/终止列地址 (指令为 ”21H+A[6:0]+B[6:0]“ )   这是一条三字节的指令,由 A[6:0] 指定起始列地址,B[6:0] 指定终止列地址。   同样,由于前 1 位值无规定,所以:A[6:0] 和 B[6:0] 从 00H7FH 的取值指定起始/终止列地址为 0127。   这条指仅在水平/垂直模式下有效,用来设置水平/垂直模式的初始列和结束列

3.设置起始/终止页地址 (指令为 “22H+A[2:0]+B[2:0]“ )   这是一条三字节的指令,由 A[2:0] 指定起始也地址,B[2:0] 指定终止页地址。   由于前 5 位值无规定,所以:A[2:0]和B[2:0]从 00H07H 的取值指定起始/终止页地址为 07。   这条指仅在水平/垂直模式下有效,用来设置水平/垂直模式的初始页和结束页

4.设置起始列地址低位 (指令为 ”00H~0FH“ )   这是一条单字节的指令。   高 4 位恒定为 0H,低4位为要设置的起始列地址的低 4 位。这条指令仅用于页寻址模式。

5.设置起始列地址高位 (指令为 ”10H~1FH“ )   这是一条单字节的指令   高 4 位恒定为 1H ,低 4 位为要设置的起始列地址的高 4 位。这条指令仅用于页寻址模式。

6.设置页地址 (指令为 ”B0HB7H“ )   这是一条单字节的指令   高 4 位恒定为 BH,第 5 位规定为 0 ,低 3位用于设置页地址,从 B0HB7H 分别设置起始页为 0~7。这条指令仅用于页寻址模式下。

3.5.4 时序指令

1.设置显示时钟分频数和fosc (指令为 ”D5H+A[7:0]“ )

​ 这是一条双字节的指令,它用于设置显示时钟分频数和振荡器频率的。

​ A[3:0]用于指定分频数,分频数 = A[3:0]+1 。

​ A[7:4]用于指定振荡频率,振荡频率 = A[7:4] 。

2.设置预充电周期 (指令为 ”D9H+A[7:0]“ )

​ 这是一条双字节的指令,用于设置预充电周期的。

​ 在预充电期间的持续时间是以 DCLK 的数量计算的。复位值为 2 DCLK 。

3.设置VCOMH输出的高电平 (指令为 ”DBH+A[6:4]“ )

​ 这是一条双字节的指令,用于设置 Vcomh 输出的。

4.空操作 (指令为 ”E3H“ )

​ 这是一条单字节的指令,它没有任何操作。

3.5.5 滚动指令

1.禁止水平滚动(指令为 ”2EH“ )

  这是一条单字节的指令,它用于禁止水平滚动。

  停止由指令 26H/27H/29H/2AH 配置的滚动。(通过这条命令的停止滚动后需要重写 GDDRAM 中的内容)

2.启用水平滚动(指令为 ”2FH“ )

  这是一条单字节的指令,它用于启用水平滚动。

  启用由指令 26H/27H/29H/2AH 配置的滚动。(后一个配置会覆盖前面的配置。正确的命令序列应该是:配置命令 -> 启动命令,例如:2AH,2FH )。

3.连续水平滚动设置(指令为 ”26H/27H+A[7:0]+B[2:0]+C[2:0]+D[2:0]+E[7:0]+F[7:0]“ )

  这是一条七字节的指令,它用于设置连续水平滚动。

  26H 为水平右滚动,27H 为水平左滚动。(每次水平滚动的时候只滚动 1 列)。

  A[7:0]:为空比特,要设为 00H 。

  B[2:0]:设置滚动起始页地址 (起始页必须要小于结束页)。

输入指令 ( 00H~07H ):

指令00H01H02H03H04H05H06H07H
表示PAGE0PAGE1PAGE2PAGE3PAGE4PAGE5PAGE6PAGE7

  C[2:0]:设置滚动速度(多少帧滚动一次)。

输入指令( 00H~07H ):

指令00H01H02H03H04H05H06H07H
表示5帧64帧128帧256帧3帧4帧25帧2帧

  D[2:0]:设置滚动结束页地址(结束页必须要大于起始页)

输入指令 ( 00H~07H ):

指令00H01H02H03H04H05H06H07H
表示PAGE0PAGE1PAGE2PAGE3PAGE4PAGE5PAGE6PAGE7

  E[7:0]:为空比特,要设为 00H 。

  F[7:0]:为空比特,要设为 FFH 。

4.连续水平和垂直滚动设置(指令为 ”29H/2AH+A[7:0]+B[2:0]+C[2:0]+D[2:0]+E[5:0]“ )

  这是一条六字节的指令,它用于设置连续水平和垂直滚动。

​ A[7:0]:为空比特,要设为 00H 。

  B[2:0]:设置滚动起始页地址 (起始页必须要小于结束页)。

输入指令 ( 00H~07H ):

指令00H01H02H03H04H05H06H07H
表示PAGE0PAGE1PAGE2PAGE3PAGE4PAGE5PAGE6PAGE7

  C[2:0]:设置滚动速度(多少帧滚动一次)。

输入指令( 00H~07H ):

指令00H01H02H03H04H05H06H07H
表示5帧64帧128帧256帧3帧4帧25帧2帧

  D[2:0]:设置滚动结束页地址(结束页必须要大于起始页)

输入指令 ( 00H~07H ):

指令00H01H02H03H04H05H06H07H
表示PAGE0PAGE1PAGE2PAGE3PAGE4PAGE5PAGE6PAGE7

  E[5:0]:设置垂直偏移量,00H3FH 表示垂直偏移量为 063 行(为 0 时则无垂直滚动)。

5.设置垂直滚动区域 (指令为 ”A3H“ )

  这是一条单字节的指令,它用于设置垂直滚动的区域。

  A[5:0]:设置顶部固定区域行数,A[5:0]+B[6:0] < MUX ratio /复用率。

  B[6:0]:设置滚动区域行数,垂直滚动偏移必须要小于滚动区域行数。

4. 项目实战

4.1 硬件准备

本教程使用的硬件如下:

  • 单片机:STM32F103C8T6
  • 屏幕:0.96 OLED 模块
  • 烧录器:ST-Link V2

接线如下:

OLEDSTM32F103C8T6
GNDGND
VCC3.3
SCLB8
SDAB9

烧录的时候接线如下表,如果不会烧录的话可以看我之前的文章【STM32下载程序的五种方法】。

ST-Link V2STM32
SWCLKSWCLK
SWDIOSWDIO
GNDGND
3.3V3V3

接好如下图。开发板使用的是我们自绘的板子。大家也可以用自己的板子,只要是 STM32F103C8T6 主控芯片就行。

4.2 初始化OLED

首先我们需要为 OLED 屏幕模块做初始化才能够使用。0.96 寸 OLED 屏幕模块的驱动网上一大堆,随便下载一个移植即可,或者使用本文提供的源码 oled.c 。

首先需要创建一个 oled.c 和 oled.h 的文件。

打开 oled.h ,我们需要先配置引脚。本文将使用 B8 和 B9,oled.h 的代码如下:

#ifndef __OLED_H_
#define __OLED_H_#include "stm32f1xx_hal.h"#define OLED_ADDRESS    0x78     //oled地址(0x78/0x7A)#define OLED_I2C_SCL_CLK()        __HAL_RCC_GPIOB_CLK_ENABLE()
#define OLED_I2C_SCL_GPIO          GPIOB
#define OLED_I2C_SCL_PIN           GPIO_PIN_8#define OLED_I2C_SDA_CLK()       __HAL_RCC_GPIOB_CLK_ENABLE()
#define OLED_I2C_SDA_GPIO          GPIOB
#define OLED_I2C_SDA_PIN           GPIO_PIN_9#define OLED_SCL_RESET()         HAL_GPIO_WritePin(OLED_I2C_SCL_GPIO,OLED_I2C_SCL_PIN,GPIO_PIN_RESET)  //SCL
#define OLED_SCL_SET()             HAL_GPIO_WritePin(OLED_I2C_SCL_GPIO,OLED_I2C_SCL_PIN,GPIO_PIN_SET)#define OLED_SDA_RESET()         HAL_GPIO_WritePin(OLED_I2C_SDA_GPIO,OLED_I2C_SDA_PIN,GPIO_PIN_RESET)  //SDA
#define OLED_SDA_SET()             HAL_GPIO_WritePin(OLED_I2C_SDA_GPIO,OLED_I2C_SDA_PIN,GPIO_PIN_SET)void OLED_Display_On(void);                                                        //开启OLED显示
void OLED_Display_Off(void);                                                    //关闭OLED显示
void OLED_Init(void);                                                            //OLED初始化
void OLED_Fill(uint8_t fill_Data);                                                //填充全屏(清除屏幕)
void OLED_Show_Char(uint8_t x, uint8_t y, uint8_t num, uint8_t size);            //显示一个字符
void OLED_Show_ChineseFont(uint8_t x, uint8_t y, uint8_t N, uint8_t size);        //显示汉字
void OLED_DrawBMP(uint8_t x0,uint8_t y0,uint8_t x1,uint8_t y1,uint8_t *bmp);    //显示图片#endif

在 oled.c 中,我们需要初始化一下 GPIO 引脚,如下:

void OLED_IO_Init(void)         //初始化引脚
{GPIO_InitTypeDef GPIO_Initure;OLED_I2C_SCL_CLK();                                     //开启SCL时钟GPIO_Initure.Pin=OLED_I2C_SCL_PIN;                      //配置SCL引脚GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;                  //推挽输出GPIO_Initure.Pull=GPIO_PULLUP;                          //上拉GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;                //高速HAL_GPIO_Init(OLED_I2C_SCL_GPIO, &GPIO_Initure);OLED_I2C_SDA_CLK();                                     //开启SDA时钟GPIO_Initure.Pin=OLED_I2C_SDA_PIN;                      //配置SDA引脚HAL_GPIO_Init(OLED_I2C_SDA_GPIO, &GPIO_Initure);
}

初始化完成之后我们需要配置一个 I2C 通信协议用于为 OLED 屏幕模块的通信。

首先需要配置时序,如下:

void OLED_I2C_Start(void)       //发送起始信号
{OLED_SCL_SET();OLED_SDA_SET();OLED_SDA_RESET();OLED_SCL_RESET();
}void OLED_I2C_Stop(void)        //发送停止信号
{OLED_SCL_SET() ;OLED_SDA_RESET();OLED_SDA_SET();
}void OLED_I2C_Ack(void)         //应答信号
{OLED_SCL_SET();OLED_SCL_RESET();
}void OLED_I2C_WriteByte(uint8_t data)        //写入一个字节
{uint8_t i;uint8_t m,da;da = data;OLED_SCL_RESET();for(i = 0; i < 8; i++){m = da;m = m&0x80;if(m == 0x80)OLED_SDA_SET();else OLED_SDA_RESET();da = da << 1;OLED_SCL_SET();OLED_SCL_RESET();}
}void OLED_WriteCmd(uint8_t Cmd)            //写命令
{OLED_I2C_Start();OLED_I2C_WriteByte(0x78);OLED_I2C_Ack();OLED_I2C_WriteByte(0x00);OLED_I2C_Ack();OLED_I2C_WriteByte(Cmd); OLED_I2C_Ack();OLED_I2C_Stop();
}void OLED_WriteDat(uint8_t Data)        //写数据
{ OLED_I2C_Start();OLED_I2C_WriteByte(0x78);OLED_I2C_Ack();OLED_I2C_WriteByte(0x40);OLED_I2C_Ack();OLED_I2C_WriteByte(Data); OLED_I2C_Ack();OLED_I2C_Stop();
}

这样,OLED 屏幕模块的通信协议就配置好了。

接着我们需要配置 OLED 屏幕模块的初始化,从上文的 OLED 模块学习中我们得知,OLED 屏幕模块需要给 SSD1306 驱动模块配置完才能使用,配置代码如下:

void OLED_Display_On(void)//开启OLED显示
{OLED_WriteCmd(0x8D);  //设置电荷泵OLED_WriteCmd(0x14);  //开启电荷泵OLED_WriteCmd(0xAF);  //开启显示,唤醒
}void OLED_Display_Off(void)//关闭OLED显示
{OLED_WriteCmd(0x8D);  //设置电荷泵OLED_WriteCmd(0x10);  //关闭电荷泵OLED_WriteCmd(0xAE);  //关闭显示,休眠
}void OLED_Init(void)          //OLED初始化
{OLED_IO_Init();           //初始化IIC接口delay_ms(100);               //延时,很重要,保证oled准备就绪OLED_WriteCmd(0xAE);      //关闭显示OLED_WriteCmd(0x20);      //设置内存地址模式    OLED_WriteCmd(0x02);      //[1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认10;OLED_WriteCmd(0xB0);      //Set Page Start Address for Page Addressing Mode,0-7OLED_WriteCmd(0xC8);      //设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数OLED_WriteCmd(0x00);      //设置列低地址OLED_WriteCmd(0x10);      //设置列高地址OLED_WriteCmd(0x40);      //设定开始行地址OLED_WriteCmd(0x81);      //对比度设置OLED_WriteCmd(0xFF);      //亮度调节 0x00~0xff,越大越亮OLED_WriteCmd(0xA1);      //段重定义设置,bit0:0,0->0;1,0->127OLED_WriteCmd(0xA6);      //设置显示方式;bit0:1,反相显示;0,正常显示    OLED_WriteCmd(0xA8);      //--set multiplex ratio(1 to 64)OLED_WriteCmd(0x3F);      //默认0X3F(1/64) OLED_WriteCmd(0xA4);      //0xa4,Output follows RAM content;0xa5,Output ignores RAM contentOLED_WriteCmd(0xD3);      //设置显示偏移OLED_WriteCmd(0x00);      //-not offsetOLED_WriteCmd(0xD5);      //设置显示时钟分频因子,震荡频率OLED_WriteCmd(0xF0);      //--set divide ratioOLED_WriteCmd(0xD9);      //设置预充电周期OLED_WriteCmd(0x22);OLED_WriteCmd(0xDA);      //设置COM硬件引脚配置OLED_WriteCmd(0x12);OLED_WriteCmd(0xDB);      //设置VCOMH 电压倍率OLED_WriteCmd(0x20);      //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vccOLED_WriteCmd(0x8D);      //--set DC-DC enableOLED_WriteCmd(0x14);OLED_WriteCmd(0xAF);      //开启显示
}

这样,OLED 屏幕模块的初始化配置就完成了。

4.3 让OLED亮起来

接下来我们该如何使用 OLED 屏幕模块亮起来?

从上文 OLED 屏幕模块的学习中,我们可以知道 OLED 屏幕模块的显示原理,通过通信协议来操控指定的某些像素点来实现成像,那么我们代码可以这么写:

void OLED_Fill(uint8_t fill_Data)            //全屏填充(清除屏幕)
{uint8_t m,n;for(m = 0; m < 8; m++){OLED_WriteCmd(0xB0+m);        //设置页地址(0~7)OLED_WriteCmd(0x00);        //设置显示位置—列低地址OLED_WriteCmd(0x10);        //设置显示位置—列高地址   for(n = 0; n < 128; n++){OLED_WriteDat(fill_Data);}}
}

这样就可以了。

在 main.c 中代码如下:

int main(void)
{HAL_Init();                         /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */delay_init(72);                     /* 延时初始化 */OLED_Init();   //oled初始化OLED_Fill(0xFF);//全屏点亮delay_ms(2000);OLED_Fill(0x00);//全屏熄灭delay_ms(2000);while(1){ }        
}

编译下载。可以看到我们的 OLED 屏幕直接亮了起来,然后 2s 之后又灭掉了,说明我们的初始化很成功。

4.4 显示字符

那么我们该如何显示一个字符呢?这里我们需要用到字库,可以在网上找适配的字库用,也可以直接使用本文的字库文件(codetab.h)。

在 oled.c 中,我们需要定义一个函数用来显示字符。通过循环遍历待写入的字节,得到一个我们想要显示的数,这个数需要事先被定义在字库当中。

OLED_Show_Char 函数中我们需要填入 x(0127),y(07),显示的字符(需要用 ‘ ’ 包起来。在字库中定义),字符的大小(显示的字符大小也需要提前在字库中定义)。

void OLED_Show_Char(uint8_t x, uint8_t y, uint8_t num, uint8_t size)        //显示一个字符
{uint8_t i,j,page;num = num-' ';                  //得到偏移后的值page = size / 8;if(size % 8) page++;for(j = 0; j < page; j++){OLED_SetPos(x, y+j);for(i = size / 2 * j; i < size / 2*(j+1); i++){if(size == 12){OLED_WriteDat(Ascii_F6X12[num][i]);}else if(size == 16){OLED_WriteDat(Ascii_F8X16[num][i]);}else if(size == 24){OLED_WriteDat(Ascii_F24X24[num][i]);}}}
}

这里我们可以使用一下上面的函数打印一个字符 ”A” ,main.c 代码如下:

int main(void)
{HAL_Init();                         /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */delay_init(72);                     /* 延时初始化 */OLED_Init();    //oled初始化OLED_Fill(0x00);//清屏while(1){ OLED_Show_Char(64,4,'A',16);}
}

编译下载。我们可以在 OLED 屏幕上看到一个字符 ‘A’ ,这样我们就成功的在 OLED 屏幕上显示一个字符了。

4.5 显示汉字

我们该如何在 OLED 屏幕上显示汉字?首先我们需要一个取模软件,用于提取汉字。

本文使用的取模软件是 PCtoLCD2002 ,打开软件是这个样子的。

我们需要先配置一下软件,打开上面一排图标中的那个小齿轮

这样设置,如下图:

设置完成之后将模式改为字符模式并将字体大小改为16*16,如下图:

接着在输入框中输入汉字,然后点击生成字模就可以了。生成之后我们只需要复制下框中的代码即可,如图:

我们将复制好的代码粘贴到字库当中,在 Font_16x16 中,如图:

要在每个字的前面添加该字作为引索。

我们在 oled.c 中添加一个函数用于显示汉字,代码如下:

void OLED_Show_ChineseFont(uint8_t x, uint8_t y, uint8_t N, uint8_t size)        //显示汉字
{uint8_t i,j;for(j = 0; j < size/8; j++){OLED_SetPos(x , y+j);for(i = size * j; i < size*(j+1); i++){if(size == 16){OLED_WriteDat(Font_16x16[N][i]);}else if(size == 24){OLED_WriteDat(Font_24x24[N][i]);}}}
}

在 main.c 函数中,代码如下:

int main(void)
{HAL_Init();                         /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */delay_init(72);                     /* 延时初始化 */OLED_Init();   //oled初始化OLED_Fill(0x00);//全屏熄灭while(1){         OLED_Show_ChineseFont(32,3,1,16);  //显示Font_16x16字库中的第一个汉字OLED_Show_ChineseFont(64,3,3,16);  //显示Font_16x16字库中的第二个汉字        }
}

编译下载之后,可以看到 OLED 屏幕上显示出了汉字。这样我们就完成了 OLED 屏幕显示汉字了。

需要注意,在编译下载之前需要打开右上角的小扳手,将编码格式改为 Chinese GB2312 (Simplified),这是汉字的编码格式,有时候,你在移植别人代码的时候,会发现别人代码的注释都是乱码,可能就是你没有改这个。

4.6 显示图像

如何使用 OLED 屏幕来显示一张图片呢?

首先在 oled.c 中添加一个用于显示图片的函数,代码如下:

void OLED_DrawBMP(uint8_t x0,uint8_t y0,uint8_t x1,uint8_t y1,uint8_t *bmp)        //显示图片
{uint16_t j=0;uint8_t x,y;for(y = y0; y < y1; y++){OLED_SetPos(x0,y);for(x = x0; x < x1; x++){OLED_WriteDat(bmp[j++]);}}
}

OLED_DrawBMP 中 x0 ,y0 代表着 x 与 y 显示的起始位置,而 x1,y1 代表着 x 与 y 显示的终止位置。一般填图片的大小。而 bmp 则是填入图片的名称。

我们还是需要用到取模软件,打开取模软件 PCtoLCD2002 ,将模式调为图形模式,如下图:

这个取模软件目前只支持 BMP 的文件格式,我们可以通过 Photoshop 等软件,对图片进行处理,最后保存为 BMP 格式。

这里我们可以用 Photoshop 随便打开一张你想显示到 OLED 屏幕上的图片,打开图像/调整/阈值,对图片进行二极化(选择一个合适的阈值),然后再裁剪一下图片大小,选择像素,根据 OLED 屏幕的尺寸进行调整。我这里调的是 56*56 ,最后保存为 BMP 格式。如下图:

再通过取模软件打开,打开之后点击生成字模就可以将 BMP 格式的图片转换成字模。

图片可以使用本文提供的处理过的 BMP 图片。将图片放入取模软件,点击生成字模,如下图:

将代码粘贴到字库中,如下图:

在 main.c 中代码如下:

int main(void)
{HAL_Init();                         /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */delay_init(72);                     /* 延时初始化 */OLED_Init();    //oled初始化OLED_Fill(0x00);//清屏    while(1){             OLED_DrawBMP(0,0,56,7,(unsigned char *)BMP_Data);//显示BMP图片        }
}

编译下载,可以看到图片成功的显示在了 OLED 屏幕上。这样我们就完成了 OLED 屏幕显示图片了。

4.7 播放视频

播放视频的原理很简单,我们已经学会了显示图像,而视频就是一帧一帧的图像,我们不断显示一张接一张的图像就成了视频。由于 STM32F103C8T6 只有 64K 的FLASH,所以直接存储图像数据既浪费资源又无法播放长视频。那么我们怎么办呢?我在这里教大家利用 Python 和串口,将图片数据源源不断的传给 OLED。

  1. 安装 Python 和 PyCharm

首先我们需要安装 Python 和 PyCharm,网上资料很多,这里不多讲了。

  1. 配置国内源

安装好后,我们需要下载依赖包。当然,因为默认 pip 下载是基于国外源,很慢,甚至会超时,所以我们要配置成国内源,一劳永逸。打开运行工具(win+r),输入 cmd ,打开命令提示符,输入以下命令。

# 永久设置阿里云的国内源
pip config set global.trusted-host mirrors.aliyun.com
pip config set global.index-url https://mirrors.aliyun.com/simple# 永久设置清华大学的国内源
pip config set global.trusted-host pypi.tuna.tsinghua.edu.cn
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple/

效果如下:

  1. 安装依赖包

然后我们就很以超快的速度安装依赖包啦,同样是在命令提示符内,输入下面三个命令。

pip install numpy
pip install opencv-python
pip install pyserial

效果如下:

  1. 编写程序

代码我只讲重点,更多细节请看文头的源码,里面注释超全。

oled.c 内的显存数组更新代码,在 while 循环里不断调用该函数,即可实现不断显示一张接一张的图像:

void OLED_Update(void)
{uint8_t j;/*遍历每一页*/for (j = 0; j < 8; j++){/*设置光标位置为每一页的第一列*/OLED_SetCursor(j, 0);/*连续写入128个数据,将显存数组的数据写入到OLED硬件*/OLED_WriteData(OLED_DisplayBuf[j], 128);}
}

uart1.c 内的中断处理函数,每次被中断就接收一个字节的数据并存放到 OLED 显存数组。

void USART1_IRQHandler(void)
{static uint8_t p0 = 0, p1 = 0;if (__HAL_UART_GET_FLAG(&uart1_handle, UART_FLAG_RXNE) == SET){/*接收一个字节数据*/uint8_t RxData;HAL_UART_Receive(&uart1_handle, &RxData, 1, 1000);/*将一个字节放到OLED显存数组的指定位置*/OLED_DisplayBuf[p0][p1] = RxData;/*位置自增*/p1++;if (p1 >= 128){p1 = 0;p0++;if (p0 >= 8){p0 = 0;}}__HAL_UART_CLEAR_FLAG(&uart1_handle, UART_FLAG_RXNE);}
}
  1. 硬件连接

在之前接线的基础上需要增加一个 USB 转 TTL,接线如下:

USB 转 TTLSTM32F103C8T6
GNDGND
VCCVCC
RXDPA9
TXDPA10

接好如下图,这也是最终效果图,先给大家预告下哈哈。

  1. 烧录代码,运行 Python

将工程源码烧录到单片机上,运行。再打开 Python 文件并运行。注意 Python 文件内的串口号要和你们自己的对应,不然会在打开串口那报错。

效果如下:

5. 总结

0.96 寸 OLED 屏幕模块具有高亮度、快速响应、低功耗等优点,而且价格也不贵,同时它还适用于多种智能设备。我们不仅可以用它来打印一些调试的信息,也可以拿来显示图片播放视频什么的。玩好它不仅可以为我们的单片机调试带来极大的方便,同时它的能实现许多有趣的项目。让我们一起玩转 OLED 屏幕模块吧!

另外,想进大厂的同学,一定要好好学算法,这是面试必备的。这里准备了一份 BAT 大佬总结的 LeetCode 刷题宝典,很多人靠它们进了大厂。

刷题 | LeetCode算法刷题神器,看完 BAT 随你挑!

有收获?希望老铁们来个三连击,给更多的人看到这篇文章

推荐阅读:

  • 程序员必备编程资料大全
  • 程序员必备软件资源

欢迎关注我的博客:良许嵌入式教程网,满满都是干货!

相关文章:

0.96寸OLED显示屏详解

我们之前讲了 LCD1602&#xff0c;今天我们将它的进阶模块——OLED。它接线更少&#xff0c;性能更强&#xff0c;也能显示中文和图像了。 大家在学习单片机的时候是否会遇到调试的问题呢&#xff1f;例如 “这串代码我到底运行成功了没有” &#xff0c;我相信很多刚开始学习…...

【Python使用】嘿马python高级进阶全体系教程第10篇:静态Web服务器-返回固定页面数据,1. 开发自己的静态Web服务器【附代码文档】

本教程的知识点为&#xff1a;操作系统 1. 常见的操作系统 4. 小结 ls命令选项 2. 小结 mkdir和rm命令选项 1. mkdir命令选项 压缩和解压缩命令 1. 压缩格式的介绍 2. tar命令及选项的使用 3. zip和unzip命令及选项的使用 4. 小结 编辑器 vim 1. vim 的介绍 2. vim 的工作模式 …...

H3C AC_AP基本配置流程

前置摘要 AP管理地址:10.115.8.0/ AP业务地址:10.115.10.0 SSID qdtest passwd 123chery map.txt system-view vlan 1010 quit interface GigabitEthernet 1/0/1 port link-type trunk port trunk permit vlan 1010AC配置 vlan 1008 des Ap_manager quit int vlan 1008 ip ad…...

准备写一个内网穿透的工具

准备写一个内网穿透的工具&#xff0c;目前只实现了HTTP内网穿透的GET方式&#xff0c;看能不能坚持写下去 git地址&#xff1a; xuejiazhi/PortRelay...

模型高效微调方式

除了LoRA&#xff08;Low-Rank Adaptation&#xff09;外&#xff0c;还有其他一些快速且效果好的模型微调方法。这些方法可以在保持模型性能的同时&#xff0c;减少计算和存储需求。以下是几种常见的方法&#xff1a; 1. 参数高效微调&#xff08;Parameter-Efficient Fine-T…...

Linux系统编程——理解系统内核中的信号捕获

目录 一、sigaction() 使用 信号捕捉技巧 二、可重入函数 三、volatile关键字 四、SIGCHLD信号 在信号这一篇中我们已经学习到了一种信号捕捉的调用接口&#xff1a;signal(),为了深入理解操作系统内核中的信号捕获机制&#xff0c;我们今天再来看一个接口&#xff1a;si…...

ISP算法之BNR降噪(Bayer域)

概述 BNR&#xff08;Bayer Noise Reduction&#xff09;即Bayer域降噪算法。对于噪声的分类如下表所示&#xff1a; 高斯噪声&#xff08; Gaussian&#xff09; 高斯噪声也被称为热噪声&#xff0c;通常是由于电路系统中自由电子的热运动&#xff0c;这种噪声幅度分布服从高…...

HBuilder快捷键大全

目录 一、最常用快捷键 二、文件操作快捷键 三、选择操作快捷键 四、插入操作快捷键 五、编辑操作快捷键 六、删除操作快捷键 七、查找操作快捷键 八、标签规范快捷键 八、运行操作快捷键 九、转换操作快捷键 十、跳转操作快捷键 十一、附加功能快捷键 十二、快捷键…...

Diffusion_Policy项目测试报错记录

1、项目连接 paper&#xff1a;2303.04137 (arxiv.org) github: real-stanford/diffusion_policy: [RSS 2023] Diffusion Policy Visuomotor Policy Learning via Action Diffusion (github.com) 2、问题与解决办法 1&#xff09; 运行 python train.py --..... 显示无法Err…...

Linux内核学习资源

老版本内核源码&#xff1a; https://mirrors.edge.kernel.org/pub/linux/kernel/Historic/old-versions/ 内核源码分析开源项目&#xff1a; https://github.com/ultraji/linux-0.12/tree/master/srchttps://gitee.com/wslyx/linux-0.12https://github.com/0voice/linux_ke…...

NGINX的安装和配置(Linux环境)

目录 NGINX 安装方式1、 离线编译安装2、 在线仓库安装 NGINX 常用命令1、进程管理命令2、信息查看命令 NGINX 配置文件1、进程使用的配置2、配置文件格式3、配置文件层级 NGINX 全局配置1、全局配置常用指令2、连接相关配置 NGINX 配置MIME1、MIME 标准2、types 配置块3、defa…...

Leetcode打卡:考场就坐

执行结果&#xff1a;通过 题目&#xff1a; 855 考场就坐 在考场里&#xff0c;有 n 个座位排成一行&#xff0c;编号为 0 到 n - 1。 当学生进入考场后&#xff0c;他必须坐在离最近的人最远的座位上。如果有多个这样的座位&#xff0c;他会坐在编号最小的座位上。(另外&am…...

STM32——“SPI Flash”

引入 在给单片机写程序的时候&#xff0c;有时会用到显示屏&#xff0c;就拿市面上的0.96寸单色显示器来说&#xff0c;一张全屏的图片就占用8x1281024个字节&#xff0c;即1kb的空间&#xff0c;这对于单片机来说确实有点奢侈&#xff0c;于是我买了一个8Mb的SPI Flash&#x…...

Zookeeper基本命令解析

ZooKeeper -server host:port -client-configuration properties-file cmd args addWatch [-m mode] path # optional mode is one of [PERSISTENT, PERSISTENT_RECURSIVE] - default is PERSISTENT_RECURSIVE addauth scheme auth 一、整体命令格式 ZooKeeper -serve…...

React-更新state的数据,dom没有重新渲染

{entourageList.map((person) > (<Row key{person.id}> {/* 使用唯一的 person.id */}<Col span{16}><Row gutter{16}><Col lg{9} md{18} sm{24}><FormItem label{姓名}>{getFieldDecorator(name-${person.id}, {initialValue: person.name,…...

消息队列技术的发展历史

消息队列技术的演进历程宛如一幅波澜壮阔的科技画卷&#xff0c;历经多个标志性阶段&#xff0c;各阶段紧密贴合不同的技术需求与市场风向&#xff0c;下面为您详细道来。 第一阶段&#xff1a;消息中间件的起源&#xff08;1970 年代末期 - 1980 年代中期&#xff09; 在计算…...

20.抽卡只有金,带保底(WPF) C#

这是一个界面应用化后的抽卡&#xff0c;目前只能抽金&#xff0c;只有基础概率加保底概率 适合界面化应用初学者。 这是展示图&#xff1a; 使用的是WPF不是winform&#xff0c;我也是第一次做WPF内容&#xff0c;就可以试一试&#xff0c;代码都在这里了&#xff0c;简单来…...

DALFox-一款XSS自动化扫描工具

声明&#xff01;本文章所有的工具分享仅仅只是供大家学习交流为主&#xff0c;切勿用于非法用途&#xff0c;如有任何触犯法律的行为&#xff0c;均与本人及团队无关&#xff01;&#xff01;&#xff01; 目录标题 一、介绍及使用启动及使用1. 单个扫描2. 多个扫描3. 文件扫描…...

软件测试之压力测试【详解】

压力测试 压力测试是一种软件测试&#xff0c;用于验证软件应用程序的稳定性和可靠性。压力测试的目标是在极其沉重的负载条件下测量软件的健壮性和错误处理能力&#xff0c;并确保软件在危急情况下不会崩溃。它甚至可以测试超出正常工作点的测试&#xff0c;并评估软件在极端…...

电视大全 1.3.8|汇聚多频道资源,秒切换流畅播放

电视大全TV版是一款功能丰富的TV端直播软件&#xff0c;由悠兔电视的同一开发者打造。最新版本更新了更多频道&#xff0c;包括央视、卫视和地方频道等&#xff0c;提供了多线路流畅播放体验&#xff0c;并支持节目回看、线路选择、开机自启等功能。该应用免登录且无购物频道&a…...

MaxKB基于大语言模型和 RAG的开源知识库问答系统的快速部署教程

1 部署要求 1.1 服务器配置 部署服务器要求&#xff1a; 操作系统&#xff1a;Ubuntu 22.04 / CentOS 7.6 64 位系统CPU/内存&#xff1a;4C/8GB 以上磁盘空间&#xff1a;100GB 1.2 端口要求 在线部署MaxKB需要开通的访问端口说明如下&#xff1a; 端口作用说明22SSH安装…...

大恒相机开发(3)—大恒相机工业检测的实际案例

大恒相机工业检测的实际案例 工业检测的实际案例图像采集性能优化技巧工业环境下的稳定性 工业检测的实际案例 以下是一些使用大恒相机进行工业检测的实际案例&#xff1a; 多特征光学成像系统&#xff1a; 在这个案例中&#xff0c;使用大恒相机构建了一个全方位、多特征的图…...

《Java 与 OpenAI 协同:开启智能编程新范式》

在当今科技飞速发展的时代&#xff0c;人工智能已成为推动各领域创新变革的核心力量。OpenAI 作为人工智能领域的领军者&#xff0c;其开发的一系列强大模型&#xff0c;如 GPT 系列&#xff0c;为自然语言处理等诸多任务带来了前所未有的突破。而 Java&#xff0c;作为一种广泛…...

HarmonyOS NEXT 技术实践-基于基础视觉服务的多目标识别

在智能手机、平板和其他智能设备日益智能化的今天&#xff0c;视觉识别技术成为提升用户体验和智能交互的重要手段。HarmonyOS NEXT通过基础视觉服务&#xff08;HMS Core Vision&#xff09;提供了一套强大的视觉识别功能&#xff0c;其中多目标识别作为其关键技术之一&#x…...

MySQL体系架构

MySQL Server架构自顶向下大致可以分网络连接层、服务层、存储引擎层和系统文件层。 网络连接层 客户端连接器&#xff08;Connectors&#xff09;&#xff1a;提供与MySQL服务器建立的支持。目前几乎支持所有主 流的服务端编程技术&#xff0c;例如常见的 Java、C、Python、.…...

如何学习Trustzone

阅读官方文档 ARM 官方文档是学习 Trustzone 最权威的资料来源。例如&#xff0c;ARM Architecture Reference Manual 中详细介绍了 Trustzone 的架构原理、寄存器定义和操作模式等内容。这些文档虽然比较复杂&#xff0c;但能够提供最准确的技术细节&#xff0c;适合在学习过…...

【视觉SLAM:Panoptic FPN全景分割网络】

Panoptic FPN是一种全景分割&#xff08;Panoptic Segmentation&#xff09;的经典网络&#xff0c;最早由 Kirillov 等人在 2019 年提出。全景分割是一种统一的视觉任务&#xff0c;结合了实例分割&#xff08;Instance Segmentation&#xff09;和语义分割&#xff08;Semant…...

【Linux】数据呈现

一、数据的输入与输出 1、标准文件描述符 Linux系统会将每个对象都当做文件来处理&#xff0c;包括输入和输出。它用文件描述符来标识每个文件对象。 文件描述符是一个非负整数&#xff0c;唯一会标识的是会话中打开的文件。每个进程一次最多可以打开9个文件描述符。bash sh…...

编译原理期末复习--伪代码部分

1.词法分析 token token nexttoken()c getchar();switch(c):case <:c getchar();switch(c):case : return RE;case >: return NE;default: rollback(); return LT;case :return EQ;case >:c getchar();switch(c):case : return GE;default: rollback(); return GT…...

云原生相关的 Go 语言工程师技术路线(含博客网址导航)

要成为一名云原生相关的 Go 语言工程师&#xff0c;需要在 Go 语言、云原生技术栈以及相关的开发和运维工具上建立扎实的基础。下面是一个前字节员工总结的技术路线规划&#xff1a; 1. 掌握 Go 语言基础 深入理解 Go 语言&#xff1a;你需要熟练掌握 Go 的语法、数据结构、并…...

基于Springboot + vue实现的汽车资讯网站

&#x1f942;(❁◡❁)您的点赞&#x1f44d;➕评论&#x1f4dd;➕收藏⭐是作者创作的最大动力&#x1f91e; &#x1f496;&#x1f4d5;&#x1f389;&#x1f525; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4dd;欢迎留言讨论 &#x1f525;&#x1f525;&…...

【YashanDB知识库】ycm-YashanDB列表有数据库显示故障排除步骤

本文内容来自YashanDB官网&#xff0c;原文内容请见 https://www.yashandb.com/newsinfo/7802959.html?templateId1718516 数据库状态 正常 异常 1、查看告警列表 例如&#xff1a;告警显示实例无法连接&#xff0c;一般是数据库实例服务掉了&#xff0c;需要尽快联系系统…...

如何使用 FastAPI 框架创建 RESTful API ?

FastAPI是一个现代、快速&#xff08;高性能&#xff09;的Web框架&#xff0c;用于构建API&#xff0c;它基于Python 3.6的类型提示功能&#xff0c;可以自动生成交互式API文档&#xff08;使用Swagger UI或ReDoc&#xff09;&#xff0c;并且支持异步编程。 二、创建第一个F…...

Java 继承

继承是所有 OOP 语言和 Java 语言不可缺少的组成部分。 继承是 Java 面向对象编程技术的一块基石&#xff0c;是面向对象的三大特征之一&#xff0c;也是实现软件复用的重要手段&#xff0c;继承可以理解为一个对象从另一个对象获取属性的过程。 如果类 A 是类 B 的父类&…...

嵌入式Linux QT+OpenCV基于人脸识别的考勤系统 项目

此项目是基于人脸识别的考勤系统开发&#xff0c;包括如下模块&#xff1a; 1、人脸识别考勤系统GUI界面设计&#xff0c;包括&#xff1a; &#xff08;1&#xff09;Qt环境(window环境/linux环境) &#xff1b; &#xff08;2&#xff09;Qt工程创建分析&#xff1b; &am…...

门户系统需要压测吗?以及门户系统如何压力测试?

一、门户系统为什么要进行压力测试&#xff1f; 首先一点要明确一下&#xff0c;统一门户上线以后&#xff0c;将是所有应用系统的入口&#xff0c;对应门户稳定性要求较高&#xff0c;门户实现了统一入口和统一认证&#xff0c;系统宕机将影响其他系统使用。一般部署架构要求…...

Linux扩展——shell编程

前置&#xff1a;Linux基础及命令复习 目录 shell概述Shell脚本入门案例 sh bash ./ . source 变量系统预定义变量 $HOME $PWD $SHELL等自定义变量 unset readonly补充&#xff1a;开启子Shell进程的常见方法 (...) $(...) ... <(...) >(...) 特殊变量 $n $# $* $ $&…...

golang, go sum文件保证下载的依赖模块是一致的

在 Go 编程语言中&#xff0c;go.sum 文件是 Go 模块管理的一部分&#xff0c;主要用于记录模块的校验信息&#xff08;模块版本的校验和&#xff09;。它的设计目标是确保模块的完整性和安全性&#xff0c;解决以下关键问题&#xff1a; 1. 确保模块版本的一致性 go.sum 文件…...

TANGO与LabVIEW控制系统集成

TANGO 是一个开源的设备控制和数据采集框架&#xff0c;主要用于管理实验室设备、自动化系统和工业设备。它为不同类型的硬件提供统一的控制接口&#xff0c;并支持设备之间的通信&#xff0c;广泛应用于粒子加速器、同步辐射光源、实验室自动化和工业控制等领域。 1. TANGO的核…...

【day14】异常处理与Object类深入解析

【day13】回顾 在深入探讨异常处理与Object类之前&#xff0c;让我们回顾一下【day13】中的关键内容&#xff1a; 权限修饰符&#xff1a; public&#xff1a;最广的访问范围&#xff0c;任何地方都可以访问。protected&#xff1a;在同包和子类中可以访问。默认&#xff08;无…...

云技术基础知识(二):虚拟化与容器技术

内容预览 ≧∀≦ゞ 虚拟化与容器技术虚拟化技术一、虚拟化的核心概念二、虚拟化的主要类型1. 服务器虚拟化2. 操作系统虚拟化&#xff08;容器化&#xff09;3. 网络虚拟化4. 存储虚拟化 三、虚拟化的实现方法和工具1. 服务器虚拟化实现2. 操作系统虚拟化&#xff08;容器化&am…...

【Java基础面试题033】Java泛型的作用是什么?

Java的基础语法可以看尚硅谷的这个PDF&#xff1a;尚硅谷JavaSE基础/《Java从入门到精通(JDK17版)》_尚硅谷电子书.pdf Autism_Btkrsr/Blog_md_to_pdf - 码云 - 开源中国 (gitee.com) 回答重点 Java泛型的作用是通过在编译时检查类型安全&#xff0c;允许程序员编写更通用和…...

Linux 基本使用和程序部署

1. Linux 环境搭建 1.1 环境搭建方式 主要有 4 种&#xff1a; 直接安装在物理机上。但是Linux桌面使用起来非常不友好&#xff0c;所以不建议。[不推荐]。使用虚拟机软件&#xff0c;将Linux搭建在虚拟机上。但是由于当前的虚拟机软件(如VMWare之类的)存在一些bug&#xff…...

react中使用ResizeObserver来观察元素的size变化

在 React 中使用 ResizeObserver 来观察元素的大小变化&#xff0c;可以通过创建一个自定义 Hook 来封装 ResizeObserver 的逻辑&#xff0c;并在组件中使用这个 Hook。以下是一个完整的示例&#xff0c;展示了如何在 React 中使用 ResizeObserver 来观察元素的大小变化。 自定…...

常见数据结构

1.数组 vector 2.链表 list 双向链表&#xff0c;不能通过下标找元素 3.栈 stack 4.队列 queue 优先队列priority_queue. 默认队头为最大值&#xff0c;可以用来任务调度&#xff0c;图算法&#xff0c;霍夫曼树 5.双端队列 deque 6.集合 set multiset s.begin() //返回…...

【服务器】linux服务器管理员查看用户使用内存情况

【服务器】linux服务器管理员查看用户使用硬盘内存情况 1、查看所有硬盘内存使用情况 df -h2、查看硬盘挂载目录下所有用户内存使用情况 du -sh /public/*3、查看某个用户所有文件夹占用硬盘内存情况 du -sh /public/zhangsan/*...

Java-冒泡排序、选择排序、二分查找算法

1. 冒泡排序 (1) 冒泡排序&#xff1a;每次从数组中找出最大值放在数值的后面去 public static void main(String[] args) {//冒泡排序int[] arr {5, 6 ,4, 9, 3, 1};for (int i 0; i < arr.length - 1; i) {for (int j 0; j < arr.length - i - 1; j) {if (arr[j] &…...

leetcode之hot100---240搜索二维矩阵II(C++)

思路一&#xff1a;通过遍历主对角线上元素判断查找方向 主对角线遍历&#xff1a; 遍历主对角线上的每个元素&#xff08;matrix[i][i]&#xff09;&#xff0c;其中 i 的范围是 [0, min(m, n) - 1]。如果目标值小于当前主对角线元素&#xff0c;说明目标值可能在当前元素的左…...

牛客--求小球落地5次后所经历的路程和第5次反弹的高度,称砝码

求小球落地5次后所经历的路程和第5次反弹的高度 描述 假设有一个小球从 hh 米高度自由落下&#xff0c;我们不考虑真实的物理模型&#xff0c;而是简洁的假定&#xff0c;该小球每次落地后会反弹回原高度的一半&#xff1b;再落下&#xff0c;再反弹&#xff1b;……。 求小球…...

EasyPoi 使用$fe:模板语法生成Word动态行

1 Maven 依赖 <dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-spring-boot-starter</artifactId><version>4.0.0</version> </dependency> 2 application.yml spring:main:allow-bean-definition-over…...