SSD1306的u8g2库源码解析

库源码对于入门者来说很生涩,层层嵌套,像迷宫一样,各种.c和.h文件很多,命名也很相似,网上也很少很详细的解析,我对u8g2这个库的一部分源码进行分析,希望有助于各位阅读,欢迎指点。

一、第一步

我的硬件是ESP32S3,用arduino框架,SSD1306是4线的SPI,MCU用硬件SPI通讯。

研究这种库我的习惯是先不要去看背后的.c和.h等文件,找一个最简单的.ino示例上手,示例会告诉我们初始化配置的先后顺序,沿着这个顺序才不会让人找不到路子。

u8g2库分为full buffer(整个画面一起更新)、page buffer(部分画面更新)、u8x8(针对文字字符的显示)三大类操作入口,针对不同显示内容,调用不同的操作入口。各个大类下都有类似hello world.ino这种示例,我阅读过后,选full buffer这个大类下的hello world.ino来解读,应该算是里面最简单的一个示例,代码就几行。这个示例就是在OLED屏幕上显示Helloworld字符。

在.ino示例里面我们只需要将红框的一行代码反注释。这句十分重要!是全局的第一层,选择屏幕硬件和通讯硬件的种类。

这句里面蓝色的u8g2看样子只是个实例,我们ctrl+鼠标左键点击红色U8G2_SSD1306_128x64,就看到这个类的定义。

二、类的继承

U8G2_SSD1306_128X64_NONAME_F_4W_HW_SPI在U8g2lib.h里面声明了(下图1),是一个子类,继承了U8G2这个大类,主要为了u8x8t这种结构体类型能通用。

花括号后第一段U8G2_SSD1306_128X64_NONAME_F_4W_HW_SPI,名称跟类名称一模一样,但是前面没有函数类型的,是这个类的构造函数,声明为public,作用是在.ino这个文件里接收程序员输入的函数参数如*rotataion, clock, data,cs,dc等引脚参数。这个比较简单,pass。

最重要的是这段u8g2_Setup_ssd1306_128x64_noname_f(),逐个参数研究。&u8g2先不用管,是这个库函数特色的u8x8类型结构体变量,内涵很多,不影响理解。

rotation其实就是定义屏幕是否转90度或180度,这个很简单,不再深入。

三、逐层代码解析

下图1

 u8x8_byte_arduino_hw_spi和u8x8_gpio_and_delay_arduino,我用红色箭头标注了比较重要。通过ctrl+鼠标左键点击setup这个函数名称,我们看到在u8g2_d_setup.c这个驱动文件里有其定义,从图2可以知道这两个参数类型是回调指针函数类型,类型名称是u8x8_msg_cb,该类型定义见图4

后面还有好多层函数嵌套,每层函数里byte_cb和gpio_and_delay_cb参数都是在这层赋值好了,其值分别为 u8x8_byte_arduino_hw_spi和u8x8_gpio_and_delay_arduino

U8g2lib.h这层的主要工作是选择好硬件通讯类型和MCU通讯引脚这两项硬件。下一层请看图2.

下图2

下图3

图2这两个变量是u8x8_msg_cb 类型,目前我们还不清楚它长什么样子,通过疯狂点击就能追溯到期最初的typedef,如图3,是个回调(callback)指针函数。名称后面带着_cb都是回调函数,有4个参数。理解好这个,库代码应该都会清晰了。

图2这一层在u8g2_d_setup.c这个文件定义,主要工作是在上一层硬件基础上,做初始化,包括:

(1)指令/数据通讯协议初始化。我们这个4线硬件spi是选择u8x8_cad_001这种,其他硬件会选择u8x8_cad_100、u8x8_cad_110等其他方式。c代表command,a代表argument,d代表data。其中argument的值是个简单的单位整数,一般是针对command语句进行选择,比如U8X8_MSG_DISPLAY_SET_FLIP_MODE这种command,若arg=1,则是屏幕底色翻转,若arg=0,则是不翻转。

(2)初始化相应的显存。buf数组做显存。我们屏幕是128*64,所以buf数组是1024大小,选择u8g2_m_16_8_f这种显存。其他屏幕尺寸的显存会选择u8g2_m_255_2_1、u8g2_m_9_5_1等u8g2->tile_buf记录了显存的初始化信息。这一步与什么芯片类型无关,仅把屏幕尺寸抽象出来。

(3)初始化芯片信息。u8x8->display_cb选择了u8x8_d_ssd1306_128x64_noname这个参数,然后按照ssd1306芯片厂家手册的初始化配置步骤发出一堆初始化指令,厂家手册的初始化配置指令在这一步完成的。像ssd1312等其他芯片这儿就会选择u8x8_d_ssd1312_128x64_noname参数进行初始化配置。

下图4

 函数指针的意思是,u8x8_msb_cb这个指针指向某个函数的入口地址,去运行该函数的功能。

下图5

图5的u8x8_Setup()函数可以溯源到图2的u8g2_SetupDisplay( )。byte_cb这个参数来自图2的u8x8_byte_arduino_hw_spi参数,u8x8->byte_cb指定的字节传输协议就采用u8x8_byte_arduino_hw_spi这个方式,其内容如下图6。

这一层主要完成了对u8x8这个核心结构体的赋值,无论什么屏幕硬件或spi通讯硬件,都在前面做了相应的选择,将选择的结果在这一层传导给u8x8结构体,后面的软件操作基本可以实现与硬件解耦,u8x8底层用了什么硬件类型,后面的操作不用关心了。

图6

图6的u8x8_byte_arduino_hw_spi就是一个函数,程序会先跑这个函数,这个函数就是脚踏实地去干SPI硬件配置的事情了,没有继续跳转调用了。这个函数虽然类型是uint8_t,但实际上只会返回0或1这种没什么意义的值,重要的是跑这个程序的过程完成的SPI硬件配置。

经过上述层层调用嵌套,完成了u8x8这个结构体初始化,后面想要画图、写字,就直接调用u8x8或者u8g2这个类就行,不用管硬件如何去实现了。

值得一提,typedef uint8_t (*u8x8_msg_cb)(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr),这个回调函数指针之所以用这4个参数,是程序员的能力总结出来的。u8x8_t *u8x8是贯穿全局的一个结构体,做各种操作目的都是为了让u8x8这个结构体逐渐增加记忆,逐渐变得具备ssd1306屏幕的所有特性;msg是某个操作方法的名字,比如U8X8_MSG_DISPLAY_SET_FLIP_MODE这种模式名字,或者u8x8->display_cb、u8x8->byte_cb这种中间变量的方法;arg_int就是某个简单个位整数,有时用不上。

图1一句话U8G2_SSD1306_128X64_NONAME_F_4W_HW_SPI完成u8x8结构体的赋值过程,然后可以去应用这个库,在主程序跑用户想要的程序了,比如回到最开始的helloworld.ino,下图7

图7是主程序, 全部都是u8g2这个类的方法来操作。

  • 16
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值