【奶茶Beta专项】【LVGL9.4源码分析】04-OS抽象层

【奶茶Beta专项】【LVGL9.4源码分析】04-OS抽象层

  • [1. 概述](#1. 概述)
    • [1.1 文档目的](#1.1 文档目的)
    • [1.2 代码版本与范围](#1.2 代码版本与范围)
  • [2. 设计意图与总体定位](#2. 设计意图与总体定位)
    • [2.1 问题背景](#2.1 问题背景)
    • [2.2 OS 抽象层的角色](#2.2 OS 抽象层的角色)
    • [2.3 设计目标](#2.3 设计目标)
  • [3. 抽象接口分类与功能说明](#3. 抽象接口分类与功能说明)
    • [3.1 全局锁与线程安全封装(lv_lock / lv_unlock)](#3.1 全局锁与线程安全封装(lv_lock / lv_unlock))
      • [3.1.1 接口定义](#3.1.1 接口定义)
      • [3.1.2 用法与语义](#3.1.2 用法与语义)
    • [3.2 线程抽象:lv_thread_t 与 `lv_thread_*`](#3.2 线程抽象:lv_thread_t 与 lv_thread_*)
      • [3.2.1 抽象类型与优先级](#3.2.1 抽象类型与优先级)
      • [3.2.2 抽象接口](#3.2.2 抽象接口)
    • [3.3 互斥锁抽象:lv_mutex_t 与 `lv_mutex_*`](#3.3 互斥锁抽象:lv_mutex_t 与 lv_mutex_*)
      • [3.3.1 类型定义](#3.3.1 类型定义)
      • [3.3.2 抽象接口](#3.3.2 抽象接口)
    • [3.4 线程同步抽象:lv_thread_sync_t 与条件变量语义](#3.4 线程同步抽象:lv_thread_sync_t 与条件变量语义)
      • [3.4.1 类型与目的](#3.4.1 类型与目的)
      • [3.4.2 抽象接口](#3.4.2 抽象接口)
    • [3.5 无 OS 场景优化:LV_OS_NONE](#3.5 无 OS 场景优化:LV_OS_NONE)
    • [3.6 idle 统计与性能监控接口](#3.6 idle 统计与性能监控接口)
    • [3.7 各 OS 后端实现概览](#3.7 各 OS 后端实现概览)
    • [3.8 sleep 接口:lv_sleep_ms](#3.8 sleep 接口:lv_sleep_ms)
  • [4. 使用方法(移植与应用层)](#4. 使用方法(移植与应用层))
    • [4.1 配置 OS 类型](#4.1 配置 OS 类型)
    • [4.2 在多线程应用中的推荐用法](#4.2 在多线程应用中的推荐用法)
    • [4.3 自定义 OS 后端的实现步骤](#4.3 自定义 OS 后端的实现步骤)
  • [5. 设计优势与可能的局限](#5. 设计优势与可能的局限)
    • [5.1 优势](#5.1 优势)
    • [5.2 局限与注意点](#5.2 局限与注意点)
    • [5.3 与通用 OS 抽象层的差异](#5.3 与通用 OS 抽象层的差异)
    • [5.4 OS 抽象层 API 速查表](#5.4 OS 抽象层 API 速查表)
  • [6. 小结](#6. 小结)
    • [7. 附录](#7. 附录)
      • [A. 参考文档(外部)](#A. 参考文档(外部))
      • [B. 相关资源(外部)](#B. 相关资源(外部))
      • [C. 本文相关代码改动 Patch(OS 抽象层信号量扩展)](#C. 本文相关代码改动 Patch(OS 抽象层信号量扩展))

文档版本 : 1.0
更新日期 : 2025年11月
适用对象: LVGL9.4 在多 OS/多平台下移植与调优的工程师

1. 概述

1.1 文档目的

本篇面向需要在多种操作系统上移植和调优 LVGL 的工程师,帮助读者:

  • 理解 library/lvgl/src/osal 目录中 OS 抽象层在 LVGL 架构里的角色,以及它解决了什么问题;
  • 掌握在不同 OS(如 FreeRTOS、pthread、RT-Thread 等)下配置与使用 OS 抽象层的常见做法;
  • 通过接口分类的方式快速了解常用 API 的含义和典型用法;
  • 在项目选型和性能调优时,能够判断这种抽象方式的适用场景和可能带来的权衡。

1.2 代码版本与范围

  • 仓库路径:https://github.com/lvgl/lvgl.git
  • 版本:v9.4.0
  • commit: c016f72d4c125098287be5e83c0f1abed4706ee5
  • 重点文件:
    • src/osal/lv_os.h / lv_os.c / lv_os_private.h
    • 各后端实现:lv_freertos.*lv_pthread.*lv_rtthread.*lv_cmsis_rtos2.*lv_linux.*lv_windows.*lv_sdl2.*lv_mqx.*lv_os_none.*

2. 设计意图与总体定位

2.1 问题背景

LVGL 需要在两端都工作良好:

  • 一端是无 OS 的 MCU 场景 :单线程轮询 lv_timer_handler() 即可,不希望引入复杂的线程/锁开销;
  • 另一端是多任务 OS 场景:Linux/pthread、FreeRTOS、RT-Thread、Windows 等,多线程/多核并发访问 LVGL 成为常态。

如果直接在内核里散布各 OS 的 API 调用,既难以维护,也会让无 OS 场景背负多余成本。

2.2 OS 抽象层的角色

OSAL 的定位可以概括为:

  • 向上 :提供一套与具体 OS 解耦的抽象接口:
    • 线程:lv_thread_*
    • 互斥锁:lv_mutex_*
    • 线程同步对象:lv_thread_sync_*
    • 全局锁与 sleep:lv_lock / lv_unlock / lv_sleep_ms
    • idle 统计接口:lv_os_get_idle_percent() 等。
  • 向下 :针对不同 OS 提供一组实现文件:
    • FreeRTOS → lv_freertos.*
    • pthread/Linux → lv_pthread.* / lv_linux.*
    • RT-Thread → lv_rtthread.*
    • CMSIS-RTOS2 → lv_cmsis_rtos2.*
    • Windows → lv_windows.*
    • SDL2/桌面模拟 → lv_sdl2.* 等。

LV_USE_OS / LV_OS_* 配置在编译期选择具体后端。

2.3 设计目标

  • 保证 LVGL 在多 OS 环境下有统一的并发模型(全局锁 + 线程抽象);
  • 对于 LV_OS_NONE 场景,尽可能做到"零额外开销":
    • 线程/互斥/同步函数全部 inline 空实现;
    • 不额外引入调度和上下文切换。

3. 抽象接口分类与功能说明

本节按接口功能维度对 OSAL API 做分类说明,而不是逐文件罗列。

3.1 全局锁与线程安全封装(lv_lock / lv_unlock)

3.1.1 接口定义

lv_os.h / lv_os.c 中:

  • void lv_lock(void);
    • 加 LVGL 的"全局互斥锁"。
    • 典型实现:lv_mutex_lock(&lv_general_mutex)(在 LV_USE_OS != LV_OS_NONE 时)。
  • lv_result_t lv_lock_isr(void);
    • 中断上下文使用的加锁版本,对应 lv_mutex_lock_isr
  • void lv_unlock(void);
    • 解锁全局互斥。

3.1.2 用法与语义

  • lv_timer_handler 线程 中调用 LVGL API 前:
c 复制代码
lv_lock();
/* 调用任意 LVGL API */
lv_unlock();
  • 在事件回调中(由 lv_timer_handler 线程调用)则不需要显式加锁;
  • 这套机制保证了"同一时刻仅有一个线程在执行 LVGL 内核代码"。

3.2 线程抽象:lv_thread_t 与 lv_thread_*

3.2.1 抽象类型与优先级

  • lv_os_private.h 中定义统一的线程优先级枚举:
c 复制代码
typedef enum {
    LV_THREAD_PRIO_LOWEST,
    LV_THREAD_PRIO_LOW,
    LV_THREAD_PRIO_MID,
    LV_THREAD_PRIO_HIGH,
    LV_THREAD_PRIO_HIGHEST,
} lv_thread_prio_t;
  • 各后端头文件中定义 lv_thread_t 的具体结构体:
    • FreeRTOS:包含任务句柄 TaskHandle_t xTaskHandle 等;
    • pthread:一般会包含 pthread_t
    • 其他 OS 类似。

3.2.2 抽象接口

lv_os_private.h 中声明,由各后端实现:

  • lv_result_t lv_thread_init(lv_thread_t * thread, const char * name, lv_thread_prio_t prio, void (*callback)(void *), size_t stack_size, void * user_data);
  • lv_result_t lv_thread_delete(lv_thread_t * thread);

这让 LVGL 内部及上层适配代码能启动辅助线程,而不需要直接依赖某个 OS 的 API。

3.3 互斥锁抽象:lv_mutex_t 与 lv_mutex_*

3.3.1 类型定义

  • 各后端定义自己的 lv_mutex_t
    • FreeRTOS 后端:
      • BaseType_t xIsInitialized 标志位;
      • SemaphoreHandle_t xMutex 递归互斥;
    • pthread 后端:通常会包装 pthread_mutex_t

3.3.2 抽象接口

  • 初始化与销毁:
    • lv_result_t lv_mutex_init(lv_mutex_t * mutex);
    • lv_result_t lv_mutex_delete(lv_mutex_t * mutex);
  • 加锁与解锁:
    • lv_result_t lv_mutex_lock(lv_mutex_t * mutex);
    • lv_result_t lv_mutex_lock_isr(lv_mutex_t * mutex);
    • lv_result_t lv_mutex_unlock(lv_mutex_t * mutex);

全局锁 lv_lock / lv_unlock 就是对 lv_mutex_* 的一个具体使用。

3.4 线程同步抽象:lv_thread_sync_t 与条件变量语义

3.4.1 类型与目的

  • lv_thread_sync_t 代表一个"线程同步对象",语义上类似于"条件变量 + 事件";
  • 设计目的:
    • 在多线程环境中,让一个线程等待另一个线程发出"完成/唤醒"信号;
    • 支持 ISR 触发唤醒。

3.4.2 抽象接口

  • 初始化与销毁:
    • lv_thread_sync_init / lv_thread_sync_delete
  • 等待与唤醒:
    • lv_thread_sync_wait:阻塞等待信号;
    • lv_thread_sync_signal:从普通上下文发送信号;
    • lv_thread_sync_signal_isr:从中断上下文发送信号。

以 FreeRTOS 实现为例:

  • 可以使用 Task Notify 或自建 Semaphore + waitingThreads + syncMutex 组合,实现"等待者计数 + 唤醒"的行为。

3.5 无 OS 场景优化:LV_OS_NONE

LV_USE_OS == LV_OS_NONE 时:

  • lv_thread_* / lv_mutex_* / lv_thread_sync_*lv_os_private.h 中全部实现为 static inline 空函数,返回 LV_RESULT_INVALID 或直接无操作;
  • lv_lock / lv_unlock 退化为空操作;
  • lv_sleep_ms 退化为 lv_delay_ms(ms),仅依赖 LVGL 自己的 tick。

这样做的好处:

  • 编译器可以完全内联并优化掉这些函数调用,对无 OS MCU 几乎"零开销";
  • 即便应用代码误用线程/互斥 API,也不会导致崩溃,只是返回"不支持"。

3.6 idle 统计与性能监控接口

lv_os_private.h 中:

  • uint32_t lv_os_get_idle_percent(void);
    • 返回自上次调用以来 CPU 的 idle 百分比;
  • 若启用 LV_SYSMON_PROC_IDLE_AVAILABLE
    • uint32_t lv_os_get_proc_idle_percent(void);

这些接口主要为 lv_sysmon 性能监控模块服务,用来计算:

  • CPU = 100 - idle%
  • 以及必要时的进程级 CPU 占用。

各 OS 后端需要在适当位置(如 Idle Task、任务切换 hook)维护这些统计数据,LVGL 本身不直接依赖 OS 内核细节。

3.7 各 OS 后端实现概览

目录下的 .c/.h 文件可大致分组理解:

  • 通用入口

    • lv_os.h / lv_os.c:对上层公开的锁与 sleep 接口;
    • lv_os_private.h:根据配置选择后端,并声明抽象接口。
  • 无 OS 后端

    • lv_os_none.c / lv_os_none.h:提供空实现和 lv_sleep_ms 的 delay 版本。
  • RTOS/平台后端

    • lv_freertos.*:基于 FreeRTOS 任务、递归互斥、信号量、Task Notify 实现;
    • lv_pthread.*:基于 POSIX pthread;
    • lv_rtthread.*:RT-Thread;
    • lv_cmsis_rtos2.*:CMSIS-RTOS2;
    • lv_windows.*:Windows 下线程与同步抽象;
    • lv_sdl2.* / lv_linux.*:桌面模拟/SDL2 环境下的 OS 封装;
    • lv_mqx.*:特定 RTOS MQX 的适配。

所有后端共享同一组抽象函数签名,只在 .c 实现里使用各自 OS API。

3.8 sleep 接口:lv_sleep_ms

  • 在有 OS 场景下(LV_USE_OS != LV_OS_NONE),lv_sleep_ms 通常由后端提供合适实现(如 vTaskDelay / usleep 等);
  • 在无 OS 场景下,lv_os.c 中定义:
c 复制代码
#if LV_USE_OS == LV_OS_NONE
void lv_sleep_ms(uint32_t ms)
{
    lv_delay_ms(ms);
}
#endif

使应用层可以用统一的 lv_sleep_ms 进行延时,而不关心底层是否存在线程/调度器。

4. 使用方法(移植与应用层)

4.1 配置 OS 类型

lv_conf.h 或 Kconfig 中设置:

  • LV_USE_OS:选择 OS 模式:
    • LV_OS_NONE / LV_OS_FREERTOS / LV_OS_PTHREAD / LV_OS_RTTHREAD / LV_OS_CMSIS_RTOS2 等;
  • 若使用自定义 OS:
    • LV_USE_OS = LV_OS_CUSTOM
    • 定义 LV_OS_CUSTOM_INCLUDE 指向自定义后端头文件,例如 "lv_myos.h"

4.2 在多线程应用中的推荐用法

  1. 主线程/任务
    • 定期调用 lv_timer_handler() 驱动 LVGL 刷新;
  2. 其他线程/任务
    • 在调用 LVGL API 前后使用:
c 复制代码
lv_lock();
/* UI 更新、对象创建、样式修改等 */
lv_unlock();
  1. 中断上下文
    • 避免直接调用复杂 LVGL API,若确有需要(如发起少量无阻塞操作),用 lv_lock_isr / lv_thread_sync_signal_isr 等 ISR 版本接口。

4.3 自定义 OS 后端的实现步骤

  1. 创建 lv_myos.h / lv_myos.c
    • 定义 lv_thread_t / lv_mutex_t / lv_thread_sync_t
    • 实现 lv_thread_init/deletelv_mutex_*lv_thread_sync_*lv_os_get_idle_percent 等接口。
  2. 在配置中设置:
c 复制代码
#define LV_USE_OS           LV_OS_CUSTOM
#define LV_OS_CUSTOM_INCLUDE "lv_myos.h"
  1. 在工程构建脚本中把 lv_myos.c 编进 LVGL 库或项目。

5. 设计优势与可能的局限

5.1 优势

  • 统一抽象

    一套 API 支持多种 OS,应用和 LVGL 内部逻辑不再直接依赖具体 OS 的 API;

  • 对无 OS 场景友好

    无 OS 时所有 OS 接口 inline 空实现,几乎零成本;

  • 扩展性好

    自定义 OS 只需实现抽象接口,无需修改 LVGL 内核代码;

  • 与性能监控良好集成

    idle 统计、任务切换 hook 等通过 OSAL 预留接口,sysmon 模块可以跨平台展示 CPU/FPS 等指标。

5.2 局限与注意点

  • 粒度较粗的线程安全模型

    全局互斥锁简化了并发控制,但也意味着"LVGL 内部基本是串行"的,无法同时在多个线程中高并发执行复杂 UI 操作;

  • 移植者需要理解 OS 细节

    尤其是 lv_thread_sync_* 在 FreeRTOS/其他 OS 中的实现,需要仔细处理竞态、唤醒顺序,否则容易埋下死锁或"永不唤醒"的坑;

  • idle 统计精度依赖底层实现

    若 OS/移植层未正确实现 idle 统计,sysmon 显示的 CPU/FPS 可能会偏差,需要联动调试。

  • 没有统一的信号量抽象

    当前 OS 抽象层只公开互斥锁和具备"条件变量语义"的同步对象(lv_thread_sync_*),并没有单独的 lv_semaphore_* 一类计数信号量接口;在需要用到信号量语义(如资源计数、生产者-消费者限流)时,通常需要直接使用各自 OS 的原生信号量 API,或者在项目层基于现有 OSAL 设计一层 lv_semaphore_t + lv_semaphore_* 的扩展封装,由各后端仿照线程/互斥锁的方式去实现。

5.3 与通用 OS 抽象层的差异

在很多通用 OS 抽象层或平台抽象层(PAL)中,通常会把"系统能力"一股脑收进来,比如:

  • 线程与同步(互斥、条件变量、事件组、信号量等);
  • 软件定时器与时钟接口;
  • 消息队列、mailbox;
  • 文件系统、网络 socket、标准 IO;
  • 看门狗、电源管理、中断控制等。

与这些"全功能 OSAL"相比,LVGL 的 OS 抽象层刻意做了非常明显的取舍

  • 只抽象与 GUI 内核强相关的部分:线程、互斥、简单同步、sleep、idle 统计;
  • 不在 osal 里抽象文件系统/网络/定时器/内存等 ,而是分别交给:
    • lv_timerlv_tick 做统一软件定时;
    • lv_fs 抽象不同文件系统;
    • lv_mem / lv_mem_buf 管理 LVGL 内部的堆与临时缓冲;
    • 看门狗、电源管理、中断分发则完全留给 BSP / 应用层。

这种设计的直接结果是:

  • 对 GUI 使用者来说,OS 抽象层更轻、更聚焦,不强行接管系统里所有 OS 能力;
  • LVGL 可以在"完全无 OS"的芯片上保持极小体积,同时在复杂 OS 上又有统一的并发模型;
  • 若项目本身已经有一套更大的 OSAL/PAL,可以把 LVGL 的 OS 抽象层视作其中一个子模块,二者并存,而不是相互替代。

5.4 OS 抽象层 API 速查表

下表按功能模块归纳了 OS 抽象层中对外可见的主要接口,方便在移植或查阅时快速定位。

功能模块 主要 API 作用简述
全局锁与线程安全 lv_lock() / lv_unlock() 加/解 LVGL 全局互斥锁,保证同一时刻仅有一个线程在执行 LVGL 内核代码
lv_lock_isr() 中断上下文可用的全局锁版本,内部调用 lv_mutex_lock_isr
线程管理 lv_thread_init() 创建新线程/任务,接受线程名、优先级、栈大小、入口函数和用户数据等参数
lv_thread_delete() 删除已创建的线程/任务,释放其 OS 资源
互斥锁 lv_mutex_init() / lv_mutex_delete() 创建/销毁互斥锁对象
lv_mutex_lock() / lv_mutex_unlock() 在普通上下文加锁/解锁互斥锁
lv_mutex_lock_isr() 在中断上下文尝试加锁互斥锁,返回 lv_result_t 表示结果
线程同步(条件变量语义) lv_thread_sync_init() / lv_thread_sync_delete() 创建/销毁线程同步对象,用于一对一或一对多的"等待-唤醒"场景
lv_thread_sync_wait() 在同步对象上阻塞等待,直到被其他线程或中断唤醒
lv_thread_sync_signal() / lv_thread_sync_signal_isr() 从普通上下文或中断上下文向等待者发送唤醒信号
sleep 与时间相关 lv_sleep_ms() 以毫秒为单位休眠当前执行上下文;在无 OS 场景下退化为 lv_delay_ms
idle 统计与性能监控 lv_os_get_idle_percent() 返回自上次调用以来的 CPU idle 百分比,供 lv_sysmon 计算 CPU 占用
lv_os_get_proc_idle_percent()(可选) 若平台支持,返回当前进程级别的 idle 百分比

在具体 OS 后端中,还会实现若干内部使用的辅助函数和宏(如临界区、Task Notify 封装等),但上述表格覆盖了移植与应用层最常直接接触的一组接口。

6. 小结

library/lvgl/src/osal 为 LVGL9.4 提供了一层关键的 OS 抽象:

  • 用统一接口覆盖线程、互斥、同步、sleep、idle 统计等功能;
  • 通过编译期选择后端实现多 OS 适配;
  • 在无 OS 环境下保持最小开销。

在实际项目中,只要正确配置 LV_USE_OS,并在多线程场景中遵循 "所有 LVGL API 调用都包在全局锁内 " 的基本规则,就可以在大多数平台上安全、高效地运行 LVGL。

更高级的优化(如与 OS 定时器/事件系统更深度的融合)则可以在自定义 OS 后端中进一步探索。

7. 附录

A. 参考文档(外部)

B. 相关资源(外部)

C. 本文相关代码改动 Patch(OS 抽象层信号量扩展)

diff 复制代码
diff --git a/src/osal/lv_cmsis_rtos2.c b/src/osal/lv_cmsis_rtos2.c
index 7eb898315..7d2460ba9 100644
--- a/src/osal/lv_cmsis_rtos2.c
+++ b/src/osal/lv_cmsis_rtos2.c
@@ -191,6 +191,62 @@ lv_result_t lv_thread_sync_delete(lv_thread_sync_t * sync)
     return LV_RESULT_OK;
 }
 
+lv_result_t lv_semaphore_init(lv_semaphore_t * semaphore, uint32_t initial_count,
+                              uint32_t max_count)
+{
+    const osSemaphoreAttr_t attr = {
+        .name = "LVGLSemaphore",
+    };
+
+    *semaphore = osSemaphoreNew(max_count, initial_count, &attr);
+    if(*semaphore == NULL) {
+        LV_LOG_WARN("Error: failed to create cmsis-rtos2 semaphore");
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_take(lv_semaphore_t * semaphore, uint32_t timeout_ms)
+{
+    uint32_t timeout = (timeout_ms == 0U) ? 0U : timeout_ms;
+    osStatus_t status = osSemaphoreAcquire(*semaphore, timeout);
+    if(status != osOK) {
+        LV_LOG_WARN("Error: failed to acquire cmsis-rtos2 semaphore %d", (int)status);
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_give(lv_semaphore_t * semaphore)
+{
+    osStatus_t status = osSemaphoreRelease(*semaphore);
+    if(status != osOK) {
+        LV_LOG_WARN("Error: failed to release cmsis-rtos2 semaphore %d", (int)status);
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_give_isr(lv_semaphore_t * semaphore)
+{
+    /* CMSIS-RTOS2 不区分普通/ISR 版本的 Release,这里直接复用 */
+    return lv_semaphore_give(semaphore);
+}
+
+lv_result_t lv_semaphore_delete(lv_semaphore_t * semaphore)
+{
+    osStatus_t status = osSemaphoreDelete(*semaphore);
+    if(status != osOK) {
+        LV_LOG_WARN("Error: failed to delete cmsis-rtos2 semaphore %d", (int)status);
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
 uint32_t lv_os_get_idle_percent(void)
 {
     return lv_timer_get_idle();
diff --git a/src/osal/lv_cmsis_rtos2.h b/src/osal/lv_cmsis_rtos2.h
index ae4f94f0a..c91130459 100644
--- a/src/osal/lv_cmsis_rtos2.h
+++ b/src/osal/lv_cmsis_rtos2.h
@@ -36,6 +36,8 @@ typedef osMutexId_t lv_mutex_t;
 
 typedef osEventFlagsId_t lv_thread_sync_t;
 
+typedef osSemaphoreId_t lv_semaphore_t;
+
 /**********************
  * GLOBAL PROTOTYPES
  **********************/
diff --git a/src/osal/lv_freertos.c b/src/osal/lv_freertos.c
index a8545c100..b3c69d9cd 100644
--- a/src/osal/lv_freertos.c
+++ b/src/osal/lv_freertos.c
@@ -387,6 +387,79 @@ lv_result_t lv_thread_sync_signal_isr(lv_thread_sync_t * pxCond)
     return LV_RESULT_OK;
 }
 
+lv_result_t lv_semaphore_init(lv_semaphore_t * pxSemaphore, uint32_t initial_count,
+                              uint32_t max_count)
+{
+    if(pxSemaphore->xIsInitialized == pdTRUE) return LV_RESULT_OK;
+
+    pxSemaphore->xSemaphore = xSemaphoreCreateCounting(max_count, initial_count);
+    if(pxSemaphore->xSemaphore == NULL) {
+        LV_LOG_ERROR("xSemaphoreCreateCounting failed!");
+        return LV_RESULT_INVALID;
+    }
+
+    pxSemaphore->xIsInitialized = pdTRUE;
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_take(lv_semaphore_t * pxSemaphore, uint32_t timeout_ms)
+{
+    if(pxSemaphore->xIsInitialized == pdFALSE) {
+        LV_LOG_ERROR("semaphore not initialized");
+        return LV_RESULT_INVALID;
+    }
+
+    TickType_t ticks = (timeout_ms == 0U) ? 0U : pdMS_TO_TICKS(timeout_ms);
+    BaseType_t ret = xSemaphoreTake(pxSemaphore->xSemaphore, ticks);
+    if(ret != pdTRUE) {
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_give(lv_semaphore_t * pxSemaphore)
+{
+    if(pxSemaphore->xIsInitialized == pdFALSE) {
+        LV_LOG_ERROR("semaphore not initialized");
+        return LV_RESULT_INVALID;
+    }
+
+    BaseType_t ret = xSemaphoreGive(pxSemaphore->xSemaphore);
+    if(ret != pdTRUE) {
+        LV_LOG_ERROR("xSemaphoreGive failed!");
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_give_isr(lv_semaphore_t * pxSemaphore)
+{
+    if(pxSemaphore->xIsInitialized == pdFALSE) {
+        return LV_RESULT_INVALID;
+    }
+
+    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+    BaseType_t ret = xSemaphoreGiveFromISR(pxSemaphore->xSemaphore, &xHigherPriorityTaskWoken);
+    if(ret != pdTRUE) {
+        return LV_RESULT_INVALID;
+    }
+
+    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_delete(lv_semaphore_t * pxSemaphore)
+{
+    if(pxSemaphore->xIsInitialized == pdFALSE) return LV_RESULT_INVALID;
+
+    vSemaphoreDelete(pxSemaphore->xSemaphore);
+    pxSemaphore->xIsInitialized = pdFALSE;
+
+    return LV_RESULT_OK;
+}
+
 
 void lv_freertos_task_switch_in(const char * name)
 {
diff --git a/src/osal/lv_freertos.h b/src/osal/lv_freertos.h
index 96196b743..cba469509 100644
--- a/src/osal/lv_freertos.h
+++ b/src/osal/lv_freertos.h
@@ -65,6 +65,11 @@ typedef struct {
 #endif
 } lv_thread_sync_t;
 
+typedef struct {
+    BaseType_t xIsInitialized;            /**< Set to pdTRUE if this semaphore is initialized, pdFALSE otherwise. */
+    SemaphoreHandle_t xSemaphore;         /**< FreeRTOS counting/binary semaphore. */
+} lv_semaphore_t;
+
 /**********************
  * GLOBAL PROTOTYPES
  **********************/
diff --git a/src/osal/lv_mqx.c b/src/osal/lv_mqx.c
index 3b2442f4a..c82c9bf86 100644
--- a/src/osal/lv_mqx.c
+++ b/src/osal/lv_mqx.c
@@ -164,6 +164,68 @@ lv_result_t lv_thread_sync_signal_isr(lv_thread_sync_t * sync)
     return LV_RESULT_INVALID;
 }
 
+lv_result_t lv_semaphore_init(lv_semaphore_t * semaphore, uint32_t initial_count,
+                              uint32_t max_count)
+{
+    LV_UNUSED(max_count);
+    if(MQX_OK != _lwsem_create(semaphore, (_mqx_int)initial_count)) {
+        LV_LOG_WARN("create semaphore failed");
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_take(lv_semaphore_t * semaphore, uint32_t timeout_ms)
+{
+    _mqx_uint ret;
+
+    if(timeout_ms == 0U) {
+        ret = _lwsem_poll(semaphore);
+    }
+    else {
+        /* MQX 只提供阻塞等待接口,这里使用阻塞等待。
+         * 如需严格超时控制,可在项目层封装定时等待逻辑。 */
+        LV_UNUSED(timeout_ms);
+        ret = _lwsem_wait(semaphore);
+    }
+
+    if(ret != MQX_OK) {
+        LV_LOG_WARN("Error: %x", ret);
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_give(lv_semaphore_t * semaphore)
+{
+    _mqx_uint ret = _lwsem_post(semaphore);
+    if(ret != MQX_OK) {
+        LV_LOG_WARN("Error: %x", ret);
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_give_isr(lv_semaphore_t * semaphore)
+{
+    /* MQX 支持在中断中调用 _lwsem_post,这里复用普通 give */
+    return lv_semaphore_give(semaphore);
+}
+
+lv_result_t lv_semaphore_delete(lv_semaphore_t * semaphore)
+{
+    _mqx_uint ret = _lwsem_destroy(semaphore);
+    if(ret != MQX_OK) {
+        LV_LOG_WARN("Error: %x", ret);
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
 uint32_t lv_os_get_idle_percent(void)
 {
     return lv_timer_get_idle();
diff --git a/src/osal/lv_mqx.h b/src/osal/lv_mqx.h
index 1ccd0091d..39c905475 100644
--- a/src/osal/lv_mqx.h
+++ b/src/osal/lv_mqx.h
@@ -34,6 +34,8 @@ typedef MUTEX_STRUCT lv_mutex_t;
 
 typedef LWSEM_STRUCT lv_thread_sync_t;
 
+typedef LWSEM_STRUCT lv_semaphore_t;
+
 /**********************
  * GLOBAL PROTOTYPES
  **********************/
diff --git a/src/osal/lv_os_none.h b/src/osal/lv_os_none.h
index e08a0dc42..67e15fc4d 100644
--- a/src/osal/lv_os_none.h
+++ b/src/osal/lv_os_none.h
@@ -25,6 +25,7 @@ extern "C" {
 typedef int lv_mutex_t;
 typedef int lv_thread_t;
 typedef int lv_thread_sync_t;
+typedef int lv_semaphore_t;
 
 /**********************
  * GLOBAL PROTOTYPES
diff --git a/src/osal/lv_os_private.h b/src/osal/lv_os_private.h
index 029e90c25..b9814d218 100644
--- a/src/osal/lv_os_private.h
+++ b/src/osal/lv_os_private.h
@@ -81,7 +81,7 @@ uint32_t lv_os_get_idle_percent(void);
 uint32_t lv_os_get_proc_idle_percent(void);
 
 #endif
-
+ 
 #if LV_USE_OS != LV_OS_NONE
 
 /*----------------------------------------
@@ -180,6 +180,45 @@ lv_result_t lv_thread_sync_signal_isr(lv_thread_sync_t * sync);
  */
 lv_result_t lv_thread_sync_delete(lv_thread_sync_t * sync);
 
+/**
+ * Create a semaphore
+ * @param semaphore     a variable in which the semaphore will be stored
+ * @param initial_count initial count of the semaphore
+ * @param max_count     maximum count of the semaphore
+ * @return              LV_RESULT_OK: success; LV_RESULT_INVALID: failure
+ */
+lv_result_t lv_semaphore_init(lv_semaphore_t * semaphore, uint32_t initial_count,
+                              uint32_t max_count);
+
+/**
+ * Take (decrement) a semaphore
+ * @param semaphore     the semaphore to take
+ * @param timeout_ms    timeout in milliseconds to wait; 0 means no wait
+ * @return              LV_RESULT_OK: success; LV_RESULT_INVALID: failure or timeout
+ */
+lv_result_t lv_semaphore_take(lv_semaphore_t * semaphore, uint32_t timeout_ms);
+
+/**
+ * Give (increment) a semaphore
+ * @param semaphore     the semaphore to give
+ * @return              LV_RESULT_OK: success; LV_RESULT_INVALID: failure
+ */
+lv_result_t lv_semaphore_give(lv_semaphore_t * semaphore);
+
+/**
+ * Give (increment) a semaphore from interrupt context
+ * @param semaphore     the semaphore to give
+ * @return              LV_RESULT_OK: success; LV_RESULT_INVALID: failure
+ */
+lv_result_t lv_semaphore_give_isr(lv_semaphore_t * semaphore);
+
+/**
+ * Delete a semaphore
+ * @param semaphore     the semaphore to delete
+ * @return              LV_RESULT_OK: success; LV_RESULT_INVALID: failure
+ */
+lv_result_t lv_semaphore_delete(lv_semaphore_t * semaphore);
+
 #else
 
 /* Since compilation does not necessarily optimize cross-file empty functions well
@@ -266,6 +305,40 @@ static inline lv_result_t lv_thread_sync_delete(lv_thread_sync_t * sync)
     return LV_RESULT_INVALID;
 }
 
+static inline lv_result_t lv_semaphore_init(lv_semaphore_t * semaphore, uint32_t initial_count,
+                                            uint32_t max_count)
+{
+    LV_UNUSED(semaphore);
+    LV_UNUSED(initial_count);
+    LV_UNUSED(max_count);
+    return LV_RESULT_INVALID;
+}
+
+static inline lv_result_t lv_semaphore_take(lv_semaphore_t * semaphore, uint32_t timeout_ms)
+{
+    LV_UNUSED(semaphore);
+    LV_UNUSED(timeout_ms);
+    return LV_RESULT_INVALID;
+}
+
+static inline lv_result_t lv_semaphore_give(lv_semaphore_t * semaphore)
+{
+    LV_UNUSED(semaphore);
+    return LV_RESULT_INVALID;
+}
+
+static inline lv_result_t lv_semaphore_give_isr(lv_semaphore_t * semaphore)
+{
+    LV_UNUSED(semaphore);
+    return LV_RESULT_INVALID;
+}
+
+static inline lv_result_t lv_semaphore_delete(lv_semaphore_t * semaphore)
+{
+    LV_UNUSED(semaphore);
+    return LV_RESULT_INVALID;
+}
+
 #endif /*LV_USE_OS != LV_OS_NONE*/
 
 /**********************
diff --git a/src/osal/lv_pthread.c b/src/osal/lv_pthread.c
index 6bec2c77f..ba96f34d9 100644
--- a/src/osal/lv_pthread.c
+++ b/src/osal/lv_pthread.c
@@ -171,6 +171,69 @@ lv_result_t lv_thread_sync_signal_isr(lv_thread_sync_t * sync)
     return LV_RESULT_INVALID;
 }
 
+lv_result_t lv_semaphore_init(lv_semaphore_t * semaphore, uint32_t initial_count,
+                              uint32_t max_count)
+{
+    LV_UNUSED(max_count);
+    int ret = sem_init(semaphore, 0, (unsigned int)initial_count);
+    if(ret != 0) {
+        LV_LOG_WARN("sem_init error: %d", ret);
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_take(lv_semaphore_t * semaphore, uint32_t timeout_ms)
+{
+    int ret;
+
+    if(timeout_ms == 0U) {
+        ret = sem_trywait(semaphore);
+    }
+    else {
+        /* For simplicity, block indefinitely when timeout_ms > 0.
+         * If strict timeout is required, sem_timedwait can be used here. */
+        LV_UNUSED(timeout_ms);
+        ret = sem_wait(semaphore);
+    }
+
+    if(ret != 0) {
+        LV_LOG_WARN("sem_wait error: %d", ret);
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_give(lv_semaphore_t * semaphore)
+{
+    int ret = sem_post(semaphore);
+    if(ret != 0) {
+        LV_LOG_WARN("sem_post error: %d", ret);
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_give_isr(lv_semaphore_t * semaphore)
+{
+    /* There is no dedicated ISR context in pthread; reuse normal give. */
+    return lv_semaphore_give(semaphore);
+}
+
+lv_result_t lv_semaphore_delete(lv_semaphore_t * semaphore)
+{
+    int ret = sem_destroy(semaphore);
+    if(ret != 0) {
+        LV_LOG_WARN("sem_destroy error: %d", ret);
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
 #ifndef __linux__
 uint32_t lv_os_get_idle_percent(void)
 {
diff --git a/src/osal/lv_pthread.h b/src/osal/lv_pthread.h
index b0c921587..d6118bd69 100644
--- a/src/osal/lv_pthread.h
+++ b/src/osal/lv_pthread.h
@@ -41,6 +41,8 @@ typedef struct {
     bool v;
 } lv_thread_sync_t;
 
+typedef sem_t lv_semaphore_t;
+
 /**********************
  * GLOBAL PROTOTYPES
  **********************/
diff --git a/src/osal/lv_rtthread.c b/src/osal/lv_rtthread.c
index 9608ee24e..29e1dc5bd 100644
--- a/src/osal/lv_rtthread.c
+++ b/src/osal/lv_rtthread.c
@@ -185,6 +185,59 @@ lv_result_t lv_thread_sync_signal_isr(lv_thread_sync_t * sync)
     return LV_RESULT_INVALID;
 }
 
+lv_result_t lv_semaphore_init(lv_semaphore_t * semaphore, uint32_t initial_count,
+                              uint32_t max_count)
+{
+    LV_UNUSED(max_count);
+    semaphore->sem = rt_sem_create("lvsem", initial_count, RT_IPC_FLAG_PRIO);
+    if(semaphore->sem == RT_NULL) {
+        LV_LOG_WARN("create semaphore failed");
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_take(lv_semaphore_t * semaphore, uint32_t timeout_ms)
+{
+    rt_int32_t timeout = (timeout_ms == 0U) ? 0 : rt_tick_from_millisecond(timeout_ms);
+    rt_err_t ret = rt_sem_take(semaphore->sem, timeout);
+    if(ret != RT_EOK) {
+        LV_LOG_WARN("Error: %d", ret);
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_give(lv_semaphore_t * semaphore)
+{
+    rt_err_t ret = rt_sem_release(semaphore->sem);
+    if(ret != RT_EOK) {
+        LV_LOG_WARN("Error: %d", ret);
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_give_isr(lv_semaphore_t * semaphore)
+{
+    /* RT-Thread semaphore API 可以在中断中使用,与普通 release 相同 */
+    return lv_semaphore_give(semaphore);
+}
+
+lv_result_t lv_semaphore_delete(lv_semaphore_t * semaphore)
+{
+    rt_err_t ret = rt_sem_delete(semaphore->sem);
+    if(ret != RT_EOK) {
+        LV_LOG_WARN("Error: %d", ret);
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
 uint32_t lv_os_get_idle_percent(void)
 {
     return lv_timer_get_idle();
diff --git a/src/osal/lv_rtthread.h b/src/osal/lv_rtthread.h
index 255a47afe..9205ed981 100644
--- a/src/osal/lv_rtthread.h
+++ b/src/osal/lv_rtthread.h
@@ -37,6 +37,10 @@ typedef struct {
     rt_sem_t sem;
 } lv_thread_sync_t;
 
+typedef struct {
+    rt_sem_t sem;
+} lv_semaphore_t;
+
 /**********************
  * GLOBAL PROTOTYPES
  **********************/
diff --git a/src/osal/lv_sdl2.c b/src/osal/lv_sdl2.c
index a5ba0bedb..d95b9f0cd 100644
--- a/src/osal/lv_sdl2.c
+++ b/src/osal/lv_sdl2.c
@@ -173,6 +173,55 @@ lv_result_t lv_thread_sync_signal_isr(lv_thread_sync_t * sync)
     return LV_RESULT_INVALID;
 }
 
+lv_result_t lv_semaphore_init(lv_semaphore_t * semaphore, uint32_t initial_count,
+                              uint32_t max_count)
+{
+    LV_UNUSED(max_count);
+    *semaphore = SDL_CreateSemaphore(initial_count);
+    if(*semaphore == NULL) {
+        LV_LOG_ERROR("Error: %s", SDL_GetError());
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_take(lv_semaphore_t * semaphore, uint32_t timeout_ms)
+{
+    Uint32 timeout = (timeout_ms == 0U) ? 0U : timeout_ms;
+    int ret = SDL_SemWaitTimeout(*semaphore, timeout);
+    if(ret != 0) {
+        /* SDL returns SDL_MUTEX_TIMEDOUT or error; 都视为失败 */
+        LV_LOG_ERROR("Error: %d", ret);
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_give(lv_semaphore_t * semaphore)
+{
+    int ret = SDL_SemPost(*semaphore);
+    if(ret != 0) {
+        LV_LOG_ERROR("Error: %d", ret);
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_give_isr(lv_semaphore_t * semaphore)
+{
+    /* SDL 没有中断上下文概念,直接复用普通 give */
+    return lv_semaphore_give(semaphore);
+}
+
+lv_result_t lv_semaphore_delete(lv_semaphore_t * semaphore)
+{
+    SDL_DestroySemaphore(*semaphore);
+    return LV_RESULT_OK;
+}
+
 #ifndef __linux__
 uint32_t lv_os_get_idle_percent(void)
 {
diff --git a/src/osal/lv_sdl2.h b/src/osal/lv_sdl2.h
index 69eff5851..15c5cc22a 100644
--- a/src/osal/lv_sdl2.h
+++ b/src/osal/lv_sdl2.h
@@ -38,6 +38,8 @@ typedef struct {
     bool v;
 } lv_thread_sync_t;
 
+typedef SDL_sem * lv_semaphore_t;
+
 /**********************
  * GLOBAL PROTOTYPES
  **********************/
diff --git a/src/osal/lv_windows.c b/src/osal/lv_windows.c
index c28bc0797..694f68393 100644
--- a/src/osal/lv_windows.c
+++ b/src/osal/lv_windows.c
@@ -207,6 +207,53 @@ lv_result_t lv_thread_sync_signal_isr(lv_thread_sync_t * sync)
     return LV_RESULT_INVALID;
 }
 
+lv_result_t lv_semaphore_init(lv_semaphore_t * semaphore, uint32_t initial_count,
+                              uint32_t max_count)
+{
+    *semaphore = CreateSemaphore(NULL, (LONG)initial_count, (LONG)max_count, NULL);
+    if(!*semaphore) {
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_take(lv_semaphore_t * semaphore, uint32_t timeout_ms)
+{
+    DWORD timeout = (timeout_ms == 0U) ? 0U : timeout_ms;
+    DWORD res = WaitForSingleObject(*semaphore, timeout);
+
+    if(res == WAIT_OBJECT_0) {
+        return LV_RESULT_OK;
+    }
+
+    return LV_RESULT_INVALID;
+}
+
+lv_result_t lv_semaphore_give(lv_semaphore_t * semaphore)
+{
+    if(!ReleaseSemaphore(*semaphore, 1, NULL)) {
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
+lv_result_t lv_semaphore_give_isr(lv_semaphore_t * semaphore)
+{
+    /* Windows 没有严格意义上的 ISR,这里复用普通 give */
+    return lv_semaphore_give(semaphore);
+}
+
+lv_result_t lv_semaphore_delete(lv_semaphore_t * semaphore)
+{
+    if(!CloseHandle(*semaphore)) {
+        return LV_RESULT_INVALID;
+    }
+
+    return LV_RESULT_OK;
+}
+
 uint32_t lv_os_get_idle_percent(void)
 {
     return lv_timer_get_idle();
diff --git a/src/osal/lv_windows.h b/src/osal/lv_windows.h
index 9d864535d..edb071899 100644
--- a/src/osal/lv_windows.h
+++ b/src/osal/lv_windows.h
@@ -37,6 +37,8 @@ typedef struct {
     bool v;
 } lv_thread_sync_t;
 
+typedef HANDLE lv_semaphore_t;
+
 /**********************
  * GLOBAL PROTOTYPES
  **********************/
相关推荐
Bigan(安)1 小时前
【奶茶Beta专项】【LVGL9.4源码分析】06-tick时间管理
linux·c语言·mcu·arm·unix
2301_793069821 小时前
Linux Ubuntu/Windows 双系统 分区挂载指南
linux·windows·ubuntu
好风凭借力,送我上青云1 小时前
哈夫曼树和哈夫曼编码
c语言·开发语言·数据结构·c++·算法·霍夫曼树
道路与代码之旅1 小时前
Windows 10 中以 WSL 驱 Ubuntu 记
linux·windows·ubuntu
ULTRA??1 小时前
动态内存管理:C语言malloc极简封装方案(修正版,可申请二维数组)
c语言·开发语言
say_fall1 小时前
C++ 入门第一课:命名空间、IO 流、缺省参数与函数重载全解析
c语言·开发语言·c++
DeeplyMind1 小时前
第5章:并发与竞态条件-13:Fine- Versus Coarse-Grained Locking
linux·驱动开发·ldd
赖small强1 小时前
【Linux C/C++开发】C++多态特性深度解析:从原理到实践
linux·c语言·c++·多态·虚函数表
huangyuchi.1 小时前
【Linux 网络】基于TCP的Socket编程:通过协议定制,实现网络计算器
linux·网络·tcp/ip·linux网络·协议定制·josncpp库·序列与反序列化