Zephyr OS驱动0.96OLED

Zephyr OS驱动0.96OLED


0.前言

本节在之前搭建的Zephyr环境下,实现合宙ESP32C3开发板驱动0.96寸OLED的功能,旨在熟悉zephyr的开发流程。

一、文件添加

按照官方推荐的目录结构,在zephyr同级目录下,创建app目录,用于顶层的应用开发。并创建相关的源文件、cmake文件、工程配置文件和设备树插件:

二、添加板级支持及编译逻辑

在CMakeLists.txt中,添加如下内容,将顶层源代码依赖于中间层的zyphyr os:

bash 复制代码
cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(ssd1306_oled_demo)

target_sources(app PRIVATE src/main.c)

修改 esp32c3_luatos_core.overlay 文件,对开发板上的管脚功能进行配置。

这里将 esp32c3 的 i2c0(GPIO4,GPIO5)进行使能,并在节点下添加 ssd1306 的子节点,compatible 属性用于和源码中的驱动进行匹配。

bash 复制代码
/ {
	chosen {
		zephyr,display = &ssd1306;
	};
};

&i2c0 {
	status = "okay";

	ssd1306: ssd1306@3c {
		compatible = "solomon,ssd1306fb";
		reg = <0x3c>;
		width = <128>;
		height = <64>;
		segment-offset = <0>;
		page-offset = <0>;
		display-offset = <0>;
		multiplex-ratio = <63>;
		segment-remap;
		com-invdir;
		prechargep = <0x22>;
	};
};

修改 prj.conf ,添加相关的依赖库支持包进行编译:

bash 复制代码
# 显示驱动配置
CONFIG_DISPLAY=y
CONFIG_SSD1306=y

# I2C 驱动配置
CONFIG_I2C=y

# 日志配置
CONFIG_LOG=y
CONFIG_LOG_MODE_IMMEDIATE=y
CONFIG_PRINTK=y
CONFIG_CONSOLE=y
CONFIG_SERIAL=y
CONFIG_UART_CONSOLE=y

# 显示驱动日志
CONFIG_DISPLAY_LOG_LEVEL_INF=y

也可以通过 west build -t menuconfig 命令到图形化界面中进行查找和添加。

三、业务代码

在main.c中,手动实现 ssd1306 的一些特定功能代码:

c 复制代码
/*
 * ESP32-C3 LuatOS Core SSD1306 OLED 示例程序
 *
 * 硬件连接:
 * - SSD1306 SDA -> GPIO4
 * - SSD1306 SCL -> GPIO5
 * - SSD1306 VCC -> 3.3V
 * - SSD1306 GND -> GND
 */

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/display.h>
#include <zephyr/logging/log.h>

LOG_MODULE_REGISTER(ssd1306_demo, LOG_LEVEL_INF);

/* 在屏幕上绘制一个填充矩形 */
static void draw_filled_rect(const struct device *dev, uint16_t x, uint16_t y,
			      uint16_t width, uint16_t height, bool fill)
{
	struct display_buffer_descriptor buf_desc;
	uint8_t *buf;
	size_t buf_size;

	/* SSD1306 使用 MONO01 像素格式,每个像素1位 */
	buf_size = (width * height + 7) / 8;
	buf = k_malloc(buf_size);
	if (!buf) {
		LOG_ERR("Failed to allocate buffer");
		return;
	}

	/* 填充缓冲区 */
	memset(buf, fill ? 0xFF : 0x00, buf_size);

	buf_desc.buf_size = buf_size;
	buf_desc.width = width;
	buf_desc.height = height;
	buf_desc.pitch = width;

	display_write(dev, x, y, &buf_desc, buf);
	k_free(buf);
}

/* 清空整个屏幕 */
static void clear_display(const struct device *dev,
			  struct display_capabilities *caps)
{
	draw_filled_rect(dev, 0, 0, caps->x_resolution, caps->y_resolution, false);
}

/* 绘制简单的图案 */
static void draw_pattern(const struct device *dev,
			 struct display_capabilities *caps)
{
	/* 绘制边框 */
	draw_filled_rect(dev, 0, 0, caps->x_resolution, 2, true);  /* 顶部 */
	draw_filled_rect(dev, 0, caps->y_resolution - 8,
			 caps->x_resolution, 2, true);  /* 底部 */
	draw_filled_rect(dev, 0, 0, 2, caps->y_resolution, true);  /* 左侧 */
	draw_filled_rect(dev, caps->x_resolution - 2, 0,
			 2, caps->y_resolution, true);  /* 右侧 */

	/* 在中心绘制一个小矩形 */
	uint16_t center_x = caps->x_resolution / 2 - 16;
	uint16_t center_y = caps->y_resolution / 2 - 8;
	draw_filled_rect(dev, center_x, center_y, 32, 16, true);

	/* 绘制四个角的小方块 */
	draw_filled_rect(dev, 8, 8, 8, 8, true);  /* 左上 */
	draw_filled_rect(dev, caps->x_resolution - 16, 8, 8, 8, true);  /* 右上 */
	draw_filled_rect(dev, 8, caps->y_resolution - 16, 8, 8, true);  /* 左下 */
	draw_filled_rect(dev, caps->x_resolution - 16,
			 caps->y_resolution - 16, 8, 8, true);  /* 右下 */
}

/* 绘制动画效果 - 移动的方块 */
static void draw_moving_box(const struct device *dev,
			    struct display_capabilities *caps,
			    uint16_t pos)
{
	const uint16_t box_size = 8;
	uint16_t x = pos % (caps->x_resolution - box_size);
	uint16_t y = 24;

	/* 清除上一帧的方块区域 */
	draw_filled_rect(dev, 0, y, caps->x_resolution, box_size, false);

	/* 绘制新位置的方块 */
	draw_filled_rect(dev, x, y, box_size, box_size, true);
}

int main(void)
{
	const struct device *display_dev;
	struct display_capabilities caps;
	uint16_t frame = 0;

	LOG_INF("ESP32-C3 LuatOS Core - SSD1306 OLED Demo");

	/* 获取显示设备 */
	display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
	if (!device_is_ready(display_dev)) {
		LOG_ERR("Display device not ready");
		return -1;
	}

	LOG_INF("Display device: %s", display_dev->name);

	/* 获取显示能力 */
	display_get_capabilities(display_dev, &caps);
	LOG_INF("Resolution: %dx%d", caps.x_resolution, caps.y_resolution);
	LOG_INF("Pixel format: %d", caps.current_pixel_format);

	/* 清空屏幕 */
	clear_display(display_dev, &caps);

	/* 关闭屏幕消隐(使能显示) */
	display_blanking_off(display_dev);

	/* 显示静态图案 */
	LOG_INF("Drawing pattern...");
	draw_pattern(display_dev, &caps);

	/* 等待3秒 */
	k_sleep(K_SECONDS(3));

	/* 清空屏幕准备动画 */
	clear_display(display_dev, &caps);

	/* 显示动画 - 移动的方块 */
	LOG_INF("Starting animation...");
	while (1) {
		draw_moving_box(display_dev, &caps, frame);
		frame += 2;

		/* 每50ms更新一次 */
		k_sleep(K_MSEC(50));

		/* 每隔一段时间重新绘制静态图案 */
		if (frame >= 500) {
			clear_display(display_dev, &caps);
			draw_pattern(display_dev, &caps);
			k_sleep(K_SECONDS(2));
			clear_display(display_dev, &caps);
			frame = 0;
		}
	}

	return 0;
}

这段代码中主要封装了一个填充矩形的代码,其他的代码以此为基础,进行矩形的平移、指定位置填充等操作。

显示节点的主要使用方法为:

在main函数中通过 display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display));函数,查找在设备树中指定的 display 显示节点,然后通过 display_write 接口,将缓冲区数据写入到这个显示节点中。

相关推荐
Amonter2 天前
Ubuntu下搭建zephyrOS开发环境
rtos·zephyr·esp32c3
2401_853448233 天前
STM32F103C8T6---OLED显示屏
stm32·单片机·oled
驱动探索者9 天前
Zephyr 获取 cpu 占用率异常bug分析
bug·rtos·zephyr
普中科技1 个月前
【普中Hi3861开发攻略--基于鸿蒙OS】-- 第 24 章 OLED液晶显示实验
单片机·嵌入式硬件·oled·liteos·hi3861·普中科技
jz-炸芯片的zero1 个月前
【Zephyr存储专题】16_内存泄露检测可视化脚本自动化
自动化·嵌入式·ai编程·zephyr
jz-炸芯片的zero1 个月前
【Zephyr电源与功耗专题】15_功耗优化测试工具与手段
嵌入式·zephyr·功耗
jz-炸芯片的zero3 个月前
【Zephyr电源与功耗专题】14_BMS电池管理算法(三重验证机制实现高精度电量估算)
单片机·物联网·算法·zephyr·bms电源管理算法
jz-炸芯片的zero3 个月前
【Zephyr炸裂知识系列】11_手撸内存泄露监测算法
驱动开发·算法·iot·rtos·内存泄露·zephyr
mftang3 个月前
Zephyr 中的 bt_le_per_adv_set_data 函数的介绍和应用方法
嵌入式硬件·nordic·zephyr