位图技术在嵌入式任务管理中的应用

位图技术在嵌入式任务管理中的应用

介绍位图(Bitmap)在嵌入式任务管理中的原理、实现与典型应用模式。


一、背景与动机

嵌入式系统中常有多种「布尔标志」需求:

  • 待处理任务:事件到达时需标记「待执行」,周期任务检查后驱动
  • 功能开关/抑制:某些场景下需临时关闭部分功能
  • 错误状态:各类错误或异常标记
  • 运行状态:模块是否已初始化、是否就绪等
  • 调试开关:运行检查、调试输出等

若每种标志用 boolenum 单独变量,字段会增多、接口分散;若用数组或结构体,则占用大、操作不便。位图 将多个布尔标志 packed 进一个整数,每个 bit 表示一种状态:空间紧凑、接口统一、批量判断方便


二、位图原理

2.1 基本思想

用一个整数(如 uint32_t)的各个 bit 表示一组布尔值:bit 0 对应标志 0,bit 1 对应标志 1,以此类推。置位、清零、检查都通过位运算完成,O(1)。

2.2 基础实现

c 复制代码
typedef uint32_t bitmap_t;

#define bitmap_zero      0x0u
#define bitmap_v(bitpos) (0x1u << (bitpos))

#define bitmap_set(b, mask)  do { (b) |=   (mask); } while (0)
#define bitmap_clr(b, mask)  do { (b) &= (~(mask)); } while (0)
#define bitmap_chk(b, mask)  ((b) & (mask))
作用
bitmap_zero 空位图,所有位为 0
bitmap_v(n) 生成第 n 位的掩码(如 bitmap_v(0) = 0x01
bitmap_set 置位
bitmap_clr 清零
bitmap_chk 检查是否置位(非零即真)

2.3 容量

bitmap_tuint32_t 时,单个位图最多表示 32 个独立标志。用 uint64_t 可扩展到 64 个;用数组可扩展到任意位数。

2.4 组合掩码

多个标志可合并为一个掩码,一次操作多个位:

c 复制代码
#define JOB_A   bitmap_v(0)
#define JOB_B   bitmap_v(1)
#define JOB_AB  (JOB_A | JOB_B)   /* 组合掩码 */

bitmap_set(curJobs, JOB_AB);     /* 同时置位 A 和 B */
bitmap_clr(curJobs, JOB_AB);     /* 同时清零 A 和 B */
if (bitmap_chk(curJobs, JOB_A))   /* 检查 A 是否置位 */
    ...

三、典型应用场景

3.1 Job 调度位图

语义:表示当前有哪些 Job 待执行。多个 Job 可同时置位。

c 复制代码
#define JOB_NONE      bitmap_zero
#define JOB_SYNC      bitmap_v(0)
#define JOB_CMD       bitmap_v(1)
#define JOB_UPGRADE   bitmap_v(2)

bitmap_t curJobs;

void on_event(void) {
    bitmap_set(curJobs, JOB_SYNC);   /* 事件回调:置位 */
}

void main_proc(void) {
    if (bitmap_chk(curJobs, JOB_SYNC)) {
        if (do_sync_proc() == DONE)
            bitmap_clr(curJobs, JOB_SYNC);   /* 周期 Proc:完成后清零 */
    }
}

int can_shutdown(void) {
    return curJobs == JOB_NONE;   /* 批量检查:是否全部完成 */
}

3.2 功能抑制位图

语义 :当前被抑制的功能。业务逻辑通过 bitmap_chk 判断是否执行。

3.3 错误/运行/调试位图

语义:错误状态、运行标志、调试开关等,用法同上:Set/Clr/Chk。

3.4 与事件-周期分离的配合

位图天然适合「事件与周期分离」:事件回调只做 Set,周期任务里 Chk 并执行逻辑,完成后 Clr。这样回调快速返回,周期任务按固定顺序检查,逻辑集中、时序可控。

3.5 完整示例

c 复制代码
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

/* ============================================================================
 * 第一部分:位图基础操作宏定义
 * ============================================================================ */

typedef uint32_t bitmap_t;

#define BITMAP_ZERO      0x0u
#define BITMAP_V(n)     (0x1u << (n))

#define BITMAP_SET(b, m)      do { (b) |= (m); } while(0)
#define BITMAP_CLR(b, m)     do { (b) &= (~(m)); } while(0)
#define BITMAP_CHK(b, m)     ((b) & (m))
#define BITMAP_IS_ZERO(b)    ((b) == BITMAP_ZERO)

/* ============================================================================
 * 第二部分:业务相关位掩码定义
 * ============================================================================ */

/* 任务位掩码定义 */
#define TASK_NONE        BITMAP_ZERO
#define TASK_SYNC        BITMAP_V(0)   /* 同步任务 */
#define TASK_CMD         BITMAP_V(1)   /* 命令处理任务 */
#define TASK_UPGRADE     BITMAP_V(2)   /* 升级任务 */
#define TASK_DIAG        BITMAP_V(3)   /* 诊断任务 */
#define TASK_LOG         BITMAP_V(4)   /* 日志任务 */
#define TASK_CALIB       BITMAP_V(5)   /* 校准任务 */
#define TASK_ALL         (TASK_SYNC | TASK_CMD | TASK_UPGRADE | TASK_DIAG | \
                         TASK_LOG | TASK_CALIB)

/* 全局任务位图 */
static volatile bitmap_t g_pending_tasks = BITMAP_ZERO;

/* ============================================================================
 * 第三部分:业务层接口封装
 * ============================================================================ */

/* 任务状态查询 */
#define Task_Is_Pending(task)    BITMAP_CHK(g_pending_tasks, task)
#define Task_Is_None()           BITMAP_IS_ZERO(g_pending_tasks)

/* 任务激活(事件回调中使用) */
#define Task_Activate(task)      BITMAP_SET(g_pending_tasks, task)

/* 任务完成(任务处理完成后调用) */
#define Task_Complete(task)      BITMAP_CLR(g_pending_tasks, task)

/* ============================================================================
 * 第四部分:模拟业务函数
 * ============================================================================ */

static int do_sync_processing(void)
{
    printf("  [执行] 同步处理...\n");
    return 0;  /* 返回0表示完成 */
}

static int do_command_processing(void)
{
    printf("  [执行] 命令处理...\n");
    return 0;
}

static int do_upgrade_processing(void)
{
    printf("  [执行] 升级任务...\n");
    return 0;
}

static int do_diagnostics(void)
{
    printf("  [执行] 诊断任务...\n");
    return 0;
}

static int do_logging(void)
{
    printf("  [执行] 日志任务...\n");
    return 0;
}

static int do_calibration(void)
{
    printf("  [执行] 校准任务...\n");
    return 0;
}

/* ============================================================================
 * 第五部分:任务调度器
 * ============================================================================ */

void task_scheduler(void)
{
    printf("\n========== 任务调度器开始 ==========\n");

    /* 按优先级顺序检查并处理任务 */
    
    /* 1. 同步任务(最高优先级) */
    if (Task_Is_Pending(TASK_SYNC)) {
        printf("-> 处理同步任务\n");
        if (do_sync_processing() == 0) {
            Task_Complete(TASK_SYNC);
            printf("   同步任务完成,已清除\n");
        }
    }

    /* 2. 命令处理任务 */
    if (Task_Is_Pending(TASK_CMD)) {
        printf("-> 处理命令任务\n");
        if (do_command_processing() == 0) {
            Task_Complete(TASK_CMD);
            printf("   命令任务完成,已清除\n");
        }
    }

    /* 3. 升级任务 */
    if (Task_Is_Pending(TASK_UPGRADE)) {
        printf("-> 处理升级任务\n");
        if (do_upgrade_processing() == 0) {
            Task_Complete(TASK_UPGRADE);
            printf("   升级任务完成,已清除\n");
        }
    }

    /* 4. 诊断任务 */
    if (Task_Is_Pending(TASK_DIAG)) {
        printf("-> 处理诊断任务\n");
        if (do_diagnostics() == 0) {
            Task_Complete(TASK_DIAG);
            printf("   诊断任务完成,已清除\n");
        }
    }

    /* 5. 日志任务 */
    if (Task_Is_Pending(TASK_LOG)) {
        printf("-> 处理日志任务\n");
        if (do_logging() == 0) {
            Task_Complete(TASK_LOG);
            printf("   日志任务完成,已清除\n");
        }
    }

    /* 6. 校准任务(最低优先级) */
    if (Task_Is_Pending(TASK_CALIB)) {
        printf("-> 处理校准任务\n");
        if (do_calibration() == 0) {
            Task_Complete(TASK_CALIB);
            printf("   校准任务完成,已清除\n");
        }
    }

    /* 检查是否所有任务都完成 */
    if (Task_Is_None()) {
        printf("========== 所有任务处理完成 ==========\n");
    } else {
        printf("-------- 未处理的任务: 0x%08x --------\n", g_pending_tasks);
    }
}

/* ============================================================================
 * 第六部分:模拟事件回调(中断或事件驱动场景)
 * ============================================================================ */

void on_can_receive_event(void)
{
    /* 模拟CAN收到同步请求 */
    printf("[事件] CAN同步请求到达\n");
    Task_Activate(TASK_SYNC);
}

void on_command_received(void)
{
    /* 模拟收到应用命令 */
    printf("[事件] 收到命令\n");
    Task_Activate(TASK_CMD);
}

void on_upgrade_request(void)
{
    /* 模拟收到升级请求 */
    printf("[事件] 收到升级请求\n");
    Task_Activate(TASK_UPGRADE);
}

void on_diagnostic_trigger(void)
{
    /* 模拟触发诊断 */
    printf("[事件] 触发诊断\n");
    Task_Activate(TASK_DIAG);
}

void on_log_trigger(void)
{
    /* 模拟触发日志 */
    printf("[事件] 触发日志\n");
    Task_Activate(TASK_LOG);
}

void on_calibration_trigger(void)
{
    /* 模拟触发校准 */
    printf("[事件] 触发校准\n");
    Task_Activate(TASK_CALIB);
}

/* ============================================================================
 * 第七部分:主函数演示
 * ============================================================================ */

int main(void)
{
    printf("======================================\n");
    printf("   嵌入式位图任务管理系统演示\n");
    printf("======================================\n");

    /* 初始化:清除所有任务标志 */
    g_pending_tasks = BITMAP_ZERO;
    printf("初始化: 清除所有任务标志\n");
    printf("初始状态: 0x%08x\n", g_pending_tasks);

    /* 场景1:多个事件同时触发 */
    printf("\n----- 场景1: 多个事件同时触发 -----\n");
    on_can_receive_event();      /* 触发同步 */
    on_command_received();        /* 触发命令 */
    on_upgrade_request();        /* 触发升级 */
    printf("设置标志后: 0x%08x\n", g_pending_tasks);
    task_scheduler();

    /* 场景2:连续触发多个任务 */
    printf("\n----- 场景2: 连续触发多个任务 -----\n");
    on_diagnostic_trigger();      /* 触发诊断 */
    on_log_trigger();             /* 触发日志 */
    on_calibration_trigger();     /* 触发校准 */
    printf("设置标志后: 0x%08x\n", g_pending_tasks);
    task_scheduler();

    /* 场景3:检查特定任务状态 */
    printf("\n----- 场景3: 查询特定任务状态 -----\n");
    on_command_received();        /* 再次触发命令 */
    printf("命令任务挂起: %s\n", Task_Is_Pending(TASK_CMD) ? "是" : "否");
    printf("升级任务挂起: %s\n", Task_Is_Pending(TASK_UPGRADE) ? "是" : "否");
    printf("当前任务位图: 0x%08x\n", g_pending_tasks);
    task_scheduler();

    /* 场景4:判断是否可以进入低功耗 */
    printf("\n----- 场景4: 检查是否可以休眠 -----\n");
    on_can_receive_event();      /* 再触发一个任务 */
    printf("当前任务: 0x%08x\n", g_pending_tasks);
    printf("是否可以休眠: %s\n", Task_Is_None() ? "是" : "否");
    task_scheduler();

    printf("\n======================================\n");
    printf("           演示结束\n");
    printf("======================================\n");

    return 0;
}

四、封装模式

4.1 定义位掩码

为每个标志定义宏,避免魔法数字:

c 复制代码
#define JOB_NONE    bitmap_zero
#define JOB_A       bitmap_v(0)
#define JOB_B       bitmap_v(1)
#define JOB_C       bitmap_v(2)

4.2 封装操作宏

将底层 bitmap_set/clr/chk 和具体位图变量封装成业务接口:

c 复制代码
#define JobSet(job)   bitmap_set(gJobs, job)
#define JobClr(job)   bitmap_clr(gJobs, job)
#define JobChk(job)   bitmap_chk(gJobs, job)

业务代码只调用 JobSet/JobClr/JobChk,不直接操作 gJobs


五、与其它方案对比

方案 32 个标志的空间 批量检查(如「是否全部完成」)
32 个 bool 32 字节 需逐项判断
32 个 enum 32--128 字节 不适合同存多种状态
单个 uint32_t 位图 4 字节 bitmap == 0 即可

位图在空间批量判断上优势明显。


六、流程示意

资源释放检查
bitmap == 0
允许 Shutdown
周期 Proc



BitmapChk
置位?
执行逻辑
完成?
BitmapClr
跳过
事件回调
收到事件
BitmapSet


七、移植与扩展

  1. 扩展位数uint64_tuint32_t[N] 数组,需实现多字版本的 set/clr/chk 及「是否全零」判断。
  2. 并发 :多任务访问同一 bitmap_t 时,需加锁或使用原子操作(如 __sync_or_and_fetch)。
  3. 初始化 :启动时将所有位图置为 bitmap_zero
  4. 命名 :掩码可用 xxx_v(n),操作可用 xxx_Set/Clr/Chk 等,保持统一。

八、使用注意

  • 位号范围bitmap_v(n) 中 n 应在 [0, 31](uint32_t),否则未定义或需自己保证不溢出。
  • 掩码用常量 :建议用宏定义掩码,避免运行时变量做移位导致未定义行为(如 bitmap_v(x) 中 x ≥ 32)。
  • chk 的返回值bitmap_chk 返回的是与运算结果,非 0 即真;若需严格 0/1,可写 !!bitmap_chk(b, mask)
  • 多任务:多任务访问同一位图时须加锁或原子操作,避免竞态。

九、适用与不适用

适用 不适用
大量独立布尔标志、需批量判断 每个标志需多状态(如 0/1/2)
事件置位、周期检查并清零 需要 FIFO 或优先级队列
资源紧张、追求紧凑 标志数量很少且不扩展,用 bool 即可

位图思想在 Linux 内核(CPU 掩码、IRQ 掩码等)及各类嵌入式系统中广泛使用,是高效表示布尔集合的通用手段。

相关推荐
liu****2 天前
4.哈希扩展
c++·算法·哈希算法·位图·bitset
知识即是力量ol1 个月前
亿级数据下的基数统计:从 Set 到 HyperLogLog 的进阶实战
redis·set·uv·bitmap·hyperloglog·亿级数据
guigu20125 个月前
C++ STL 深度解析:容器、迭代器与算法的协同作战
c++·嵌入式编程
R-G-B7 个月前
【14】大恒相机SDK C#开发 ——Bitmap.UnlockBits()什么意思?有什么用?bmpData.Scan0;什么意思?有什么用?
c#·bitmap·大恒相机c#开发·unlockbits·bmpdata.scan0·bmpdata
Monkey-旭7 个月前
Android Bitmap 完全指南:从基础到高级优化
android·java·人工智能·计算机视觉·kotlin·位图·bitmap
sitelist9 个月前
NTFS0x90属性和0xa0属性和0xb0属性的一一对应关系是index_entry中的index_node中VCN和runlist和bitmap
bitmap·vcn·lcn·runlist
DARLING Zero two♡9 个月前
C++寻位映射的究极密码:哈希扩展
c++·面试·位图·布隆过滤器
磁悬浮青蛙呱呱呱1 年前
30个整数映射到1个字节,查表法实现小范围内常数时间素性判定
数据结构·matlab·c#·位图·素性测试
Hello Dam1 年前
场馆预定平台高并发时间段预定实现V1
java·springboot·位图·秒杀·场馆预定