Zephyr RTOS 中k_work_init_delayable 函数功能

目录

概述

1 工作队列概念

1.1 基本概念介绍

1.2 工作队列特性

2 k_work_init_delayable函数介绍 

1.1  函数原型

2.2 工作流程

2.3 主要特点与用途

3 代码示例

4 重要注意事项


概述

k_work_init_delayable 函数的核心功能是:初始化一个“可延迟的工作项”(delayable work),将其设置为一个待执行的函数,但这个函数可以指定在未来的某个时间点执行,而不是立即提交到工作队列。它是 Zephyr 工作队列(Work Queue)机制中用于延迟/定时任务的关键组件。

1 工作队列概念

1.1 基本概念介绍

1) 工作队列 (Work Queue)

  • Zephyr 的工作队列是一种将任务(一个函数)的执行上下文从中断服务例程(ISR)或高优先级线程卸载到低优先级线程的机制。

  • 它由一个先入先出(FIFO)的队列和一个专门处理该队列的线程组成。

  • 你提交一个“工作项”(work item),工作队列线程会在其上下文中执行你指定的函数。

2) 可延迟工作项 (Delayable Work) vs 普通工作项 (Work)

  • 普通工作项 (struct k_work): 使用 k_work_init 初始化。提交后,会尽快被工作队列线程执行(无延迟)。

  • 可延迟工作项 (struct k_work_delayable): 使用 k_work_init_delayable 初始化。提交时,可以指定一个延迟时间。工作队列线程会等待这个延迟时间过后,才会执行该工作项。

1.2 工作队列特性

特性描述
功能初始化一个可以延迟指定时间后执行的任务。
本质将一个函数(任务)与一个内置定时器绑定,并交由工作队列线程管理。
优势实现延迟和周期性任务,节省系统资源,安全地从ISR卸载工作。
典型应用按键防抖、轮询传感器、延迟初始化、非精确定时任务。

2 k_work_init_delayable函数介绍 

1.1  函数原型

1) 函数原型代码

#include <zephyr/kernel.h>

void k_work_init_delayable(struct k_work_delayable *dwork,
                           k_work_handler_t handler);

2) 主要参数

  • 参数 dwork: 指向一个用户定义的 struct k_work_delayable 变量的指针。这个结构体包含了工作项信息和一个内置的定时器。

  • 参数 handler: 你的任务函数。其函数签名必须是:void handler(struct k_work *work)

    • 注意:即使在可延迟工作项中,传入处理函数的参数类型仍然是 struct k_work *。你通常需要使用 CONTAINER_OF 宏来获取外层结构体的地址,从而访问你的自定义数据。

2.2 工作流程

  1. 初始化: 使用 k_work_init_delayable 设置工作项和处理函数。此时工作项处于空闲状态。

  2. 提交( scheduling ): 使用专门的提交函数(如 k_work_reschedule_for_queuek_work_schedule_for_queue 等)将初始化好的可延迟工作项提交到某个工作队列。在提交时,必须指定延迟时间(例如 K_MSEC(100) 表示 100 毫秒后执行)。

  3. 等待与执行: 工作队列系统内部管理一个定时器。定时器到期后,工作项会被自动加入到指定工作队列的末尾,等待线程调度执行。

  4. 取消(可选): 在延迟期间或工作项尚未被执行前,可以调用 k_work_cancel_delayable 来取消它的执行。

2.3 主要特点与用途

  1. 实现非精确定时任务: 它不像硬件定时器那样精确,但非常适合执行那些不需要非常精确计时、且比较耗时的任务(例如,每隔几秒读取一次传感器数据)。

  2. 延迟执行/超时处理: 例如,在按键检测中实现软件防抖(debounce)。当按键中断到来时,提交一个延迟 50ms 的工作项。如果 50ms 内再次收到中断,就取消上次的工作项并重新提交。只有在 50ms 内没有新中断,工作项才会执行,从而确认是一次有效的按键。

  3. 将任务从 ISR 中卸载: 这是工作队列的核心目的。在 ISR 中,你不能执行冗长的操作。ISR 可以快速提交一个可延迟的工作项,让工作队列线程在之后去处理繁重的工作。

  4. 节省资源: 相比于为每个需要定时执行的任务都创建一个内核线程(k_thread)和一个定时器(k_timer),使用可延迟工作项更加节省内核资源(内存、线程控制块等)。多个延迟任务可以共享同一个工作队列线程。

3 代码示例

以下是一个简单的示例,演示如何初始化并提交一个在 1 秒后打印消息的可延迟工作项。

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

/* 定义系统的工作队列 */
#define MY_WORK_Q sys_work_q

/* 定义并声明一个可延迟工作项 */
static struct k_work_delayable my_dwork;

/* 工作项的处理函数 */
void my_work_handler(struct k_work *work)
{
    /* 注意:参数是 k_work, 我们需要取得其外层的 k_work_delayable */
    struct k_work_delayable *dwork = k_work_delayable_from_work(work);

    printk("Hello from delayed work! Executed after 1 second.\n");

    // 可以在这里再次提交它,以实现周期性执行
    // k_work_reschedule_for_queue(&MY_WORK_Q, dwork, K_SECONDS(1));
}

void main(void)
{
    printk("Work Delayable Example Started.\n");

    /* 1. 初始化可延迟工作项,并将其与处理函数绑定 */
    k_work_init_delayable(&my_dwork, my_work_handler);

    /* 2. 提交工作项到系统工作队列,并设置延迟时间为1秒 */
    int ret = k_work_reschedule_for_queue(&MY_WORK_Q, &my_dwork, K_SECONDS(1));

    /* 检查提交是否成功 (0 表示成功) */
    if (ret != 0) {
        printk("Failed to schedule work!\n");
        return;
    }

    /* 主线程可以去做其他事情... */
    while (1) {
        k_sleep(K_FOREVER);
    }
}

4 重要注意事项

  • 执行上下文: 处理函数 handler 是在工作队列线程的上下文中执行的,其优先级由工作队列线程的优先级决定(例如,系统工作队列 sys_work_q 的优先级通常是负的,即协作式优先级)。

  • 内存生命周期: 你传递给处理函数的数据(通常通过 CONTAINER_OF 访问)必须在工作项执行期间一直有效。通常使用全局变量或动态分配的内存(k_malloc)。

  • 取消与竞争: 取消一个已经到期但尚未开始执行的工作项是可能的,但取消一个正在执行的工作项是不可能的。k_work_cancel_delayable 会等待正在执行的工作项完成。

  • 提交函数: 除了 k_work_reschedule_for_queue,还有 k_work_schedule_for_queue 等函数,它们在不同情况下使用(例如是相对当前时间延迟还是绝对时间点)。

PWM_POLARITY_INVERTED该怎么定义呢,又如何在以下代码当中使用呢#include <zephyr.h> #include <drivers/pwm.h> #include <device.h> #include <sys/printk.h> /* 蜂鸣器配置 */ #define BEEP_PIN 7 #define BEEP_PWM_CHANNEL PWM0_3N // PWM0控制器的第3通道(负极性输出) #define BEEP_FREQUENCY 2000 // 蜂鸣器频率 2kHz #define BEEP_DUTY_CYCLE 50 // 占空比 50% /* 滴滴滴模式参数 */ #define BEEP_ON_DURATION 100 // 蜂鸣器开启时间 (ms) #define BEEP_OFF_DURATION 100 // 蜂鸣器关闭时间 (ms) #define BEEP_REPEAT_COUNT 3 // 重复次数 /* 蜂鸣器设备指针 */ static const struct device *pwm_dev; /* 蜂鸣器控制结构体 */ struct beep_control { struct k_work work; uint8_t repeat_count; }; static struct beep_control beep_ctrl; /* 蜂鸣器工作函数 */ static void beep_work_handler(struct k_work *work) { int ret; /* 发出声音 */ ret = pwm_pin_set_usec(pwm_dev, BEEP_PIN, 1000000/BEEP_FREQUENCY, (1000000/BEEP_FREQUENCY)*BEEP_DUTY_CYCLE/100, PWM_POLARITY_INVERTED); if (ret) { printk("Error setting PWM: %d\n", ret); return; } /* 等待开启时间 */ k_sleep(K_MSEC(BEEP_ON_DURATION)); /* 关闭蜂鸣器 */ pwm_pin_set_usec(pwm_dev, BEEP_PIN, 0, 0, 0); /* 等待关闭时间 */ k_sleep(K_MSEC(BEEP_OFF_DURATION)); /* 检查是否需要重复 */ if (beep_ctrl.repeat_count > 0) { beep_ctrl.repeat_count--; k_work_submit(&beep_ctrl.work); } } /* 初始化蜂鸣器 */ int beep_init(void) { pwm_dev = device_get_binding("PWM_0"); if (!pwm_dev) { printk("Cannot find PWM device\n"); return -1; } /* 初始化工作队列 */ k_work_init(&beep_ctrl.work, beep_work_handler); return 0; } /* 触发滴滴滴声音 */ void trigger_beep_didi(void) { beep_ctrl.repeat_count = BEEP_REPEAT_COUNT; k_work_submit(&beep_ctrl.work); } /* 测试函数 */ void test_beep(void) { if (beep_init() == 0) { printk("Beep initialized. Triggering di-di-di sound...\n"); trigger_beep_didi(); } }
06-24
#include <zephyr/kernel.h> #include <zephyr/device.h> #include <zephyr/devicetree.h> #include <zephyr/drivers/gpio.h> #include <zephyr/logging/log.h> #include <zephyr/drivers/uart.h> #include "GVL.h" #define LOG_MODULE_NAME rs485_thread LOG_MODULE_REGISTER(LOG_MODULE_NAME); // 获取设备树节点 #define ZEPHYR_USER_NODE DT_PATH(zephyr_user) // 声明设备 static const struct device *const rs485_uart = DEVICE_DT_GET(DT_ALIAS(myuart)); static const struct gpio_dt_spec direct_pin = GPIO_DT_SPEC_GET(ZEPHYR_USER_NODE, rs485_gpios); // 延时工作 static struct k_work_delayable uart_work; // RS485接收状态机 enum rx_state { STATE_IDLE, STATE_GOT_AA, STATE_RECEIVING }; static struct { enum rx_state state; uint8_t buffer[sizeof(rs485_raw_data_t) + 3]; // AA + BB + payload + CRC size_t index; } rx_state_machine; static struct k_spinlock data_spinlock; // CRC校验函数 static uint8_t calculate_crc(const uint8_t *data, size_t len) { uint8_t crc = 0; for (size_t i = 0; i < len; i++) { crc ^= data[i]; } return crc; } // 统一数据结构处理 static void process_rs485_packet(uint8_t *packet, size_t len) { // CRC校验 (最后1字节是CRC) if (calculate_crc(packet, len - 1) != packet[len - 1]) { LOG_WRN("Invalid RS485 packet CRC"); return; } // 解析原始RS485数据 rs485_raw_data_t raw_data; memcpy(&raw_data, packet + 2, sizeof(raw_data)); // 跳过包头 // 转换为统一数据结构 unified_data_point_t data_point = { .timestamp = k_uptime_get_32(), .acc_x = raw_data.acc_x, .acc_y = raw_data.acc_y, .acc_z = raw_data.acc_z, .gyro_x = raw_data.gyro_x, .gyro_y = raw_data.gyro_y, .gyro_z = raw_data.gyro_z, .data_source = 2 // 标记为RS485设备数据 }; // 添加到批处理 k_spinlock_key_t key = k_spin_lock(&batch_lock); batch_container_t *batch = &data_batches[active_batch_index]; if (batch->count < BATCH_SIZE) { batch->data[batch->count++] = data_point; // 批次满或超时处理 if (batch->count >= BATCH_SIZE || (batch->count > 0 && (k_uptime_get() - batch->start_timestamp) > MAX_BATCH_TIMEOUT_MS)) { batch->ready = true; batch->start_timestamp = k_uptime_get(); // 切换到备用缓冲区 active_batch_index = (active_batch_index + 1) % 2; data_batches[active_batch_index].count = 0; data_batches[active_batch_index].ready = false; // 通知BLE线程 k_sem_give(&ble_data_ready_sem); } } else { LOG_WRN("Batch buffer overflow"); } k_spin_unlock(&batch_lock, key); } static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data) { static size_t aborted_len; struct uart_data_t *buf; static uint8_t *aborted_buf; static bool disable_req; k_spinlock_key_t key; switch (evt->type) { case UART_TX_DONE: LOG_DBG("UART_TX_DONE"); // 设置方向为接收模式 gpio_pin_set_dt(&direct_pin, 0); if ((evt->data.tx.len == 0) || (!evt->data.tx.buf)) { return; } if (aborted_buf) { buf = CONTAINER_OF(aborted_buf, struct uart_data_t, data[0]); aborted_buf = NULL; aborted_len = 0; k_free(buf); } else { buf = CONTAINER_OF(evt->data.tx.buf, struct uart_data_t, data[0]); k_free(buf); } // 发送下一个数据包 buf = k_fifo_get(&fifo_uart_tx_data, K_NO_WAIT); if (buf) { // 设置方向为发送模式 gpio_pin_set_dt(&direct_pin, 1); k_busy_wait(RS485_DIR_PIN_DELAY_US); if (uart_tx(rs485_uart, buf->data, buf->len, SYS_FOREVER_MS)) { LOG_WRN("Failed to send data over UART"); gpio_pin_set_dt(&direct_pin, 0); // 确保切换回接收模式 k_free(buf); } } break; case UART_RX_RDY: const uint8_t *data = evt->data.rx.buf; size_t len = evt->data.rx.len; for (size_t i = 0; i < len; i++) { uint8_t byte = data[i]; switch (rx_state_machine.state) { case STATE_IDLE: if (byte == 0xAA) { rx_state_machine.state = STATE_GOT_AA; } break; case STATE_GOT_AA: if (byte == 0xBB) { rx_state_machine.state = STATE_RECEIVING; rx_state_machine.index = 0; rx_state_machine.buffer[rx_state_machine.index++] = 0xAA; rx_state_machine.buffer[rx_state_machine.index++] = 0xBB; } else { rx_state_machine.state = (byte == 0xAA) ? STATE_GOT_AA : STATE_IDLE; } break; case STATE_RECEIVING: rx_state_machine.buffer[rx_state_machine.index++] = byte; // 检查完整数据包 (AA + BB + payload + CRC) if (rx_state_machine.index >= sizeof(rx_state_machine.buffer)) { process_rs485_packet(rx_state_machine.buffer, rx_state_machine.index); rx_state_machine.state = STATE_IDLE; } break; } } break; case UART_RX_DISABLED: LOG_DBG("UART_RX_DISABLED"); disable_req = false; buf = k_malloc(sizeof(*buf)); if (buf) { buf->len = 0; uart_rx_enable(rs485_uart, buf->data, sizeof(buf->data), UART_RX_TIMEOUT_MS); } else { LOG_WRN("Not able to allocate UART receive buffer"); k_work_reschedule(&uart_work, K_MSEC(UART_WAIT_FOR_RX)); } break; case UART_RX_BUF_REQUEST: LOG_DBG("UART_RX_BUF_REQUEST"); buf = k_malloc(sizeof(*buf)); if (buf) { buf->len = 0; uart_rx_buf_rsp(rs485_uart, buf->data, sizeof(buf->data)); } else { LOG_WRN("Not able to allocate UART receive buffer"); } break; case UART_RX_BUF_RELEASED: LOG_DBG("UART_RX_BUF_RELEASED"); buf = CONTAINER_OF(evt->data.rx_buf.buf, struct uart_data_t, data[0]); k_free(buf); break; case UART_TX_ABORTED: LOG_DBG("UART_TX_ABORTED"); gpio_pin_set_dt(&direct_pin, 0); // 确保切换回接收模式 if (!aborted_buf) { aborted_buf = (uint8_t *)evt->data.tx.buf; aborted_len = evt->data.tx.len; } struct uart_data_t *tx_buf = CONTAINER_OF(aborted_buf, struct uart_data_t, data); size_t remaining = tx_buf->len - aborted_len; if (remaining > 0) { gpio_pin_set_dt(&direct_pin, 1); k_busy_wait(RS485_DIR_PIN_DELAY_US); uart_tx(rs485_uart, &tx_buf->data[aborted_len], remaining, SYS_FOREVER_MS); } break; default: break; } } static void uart_work_handler(struct k_work *item) { struct uart_data_t *buf = k_malloc(sizeof(*buf)); if (buf) { buf->len = 0; uart_rx_enable(rs485_uart, buf->data, sizeof(buf->data), UART_RX_TIMEOUT_MS); } else { LOG_WRN("Not able to allocate UART receive buffer"); k_work_reschedule(&uart_work, K_MSEC(UART_WAIT_FOR_RX)); } } static int rs485_init(void) { int err; if (!device_is_ready(rs485_uart)) { LOG_ERR("UART device not ready"); return -ENODEV; } // 初始化方向控制引脚 if (!device_is_ready(direct_pin.port)) { LOG_ERR("GPIO device not ready"); return -ENODEV; } err = gpio_pin_configure_dt(&direct_pin, GPIO_OUTPUT_INACTIVE); if (err) { LOG_ERR("GPIO config error: %d", err); return err; } // 初始化引脚为接收 gpio_pin_set_dt(&direct_pin, 0); // 初始化接收状态机 rx_state_machine.state = STATE_IDLE; rx_state_machine.index = 0; // 初始化UART k_work_init_delayable(&uart_work, uart_work_handler); err = uart_callback_set(rs485_uart, uart_cb, NULL); if (err) { LOG_ERR("Cannot set UART callback: %d", err); return err; } // 启动接收 k_work_schedule(&uart_work, K_NO_WAIT); return 0; } void rs485_send(const uint8_t *data, size_t len) { struct uart_data_t *tx = k_malloc(sizeof(*tx) + len); if (!tx) { LOG_ERR("Not enough memory for UART send"); return; } memcpy(tx->data, data, len); tx->len = len; // 如果发送队列为空,直接发送,否则加入队列 if (k_fifo_is_empty(&fifo_uart_tx_data)) { // 设置方向为发送模式 gpio_pin_set_dt(&direct_pin, 1); k_busy_wait(RS485_DIR_PIN_DELAY_US); if (uart_tx(rs485_uart, tx->data, tx->len, SYS_FOREVER_MS)) { LOG_WRN("Failed to start UART send"); gpio_pin_set_dt(&direct_pin, 0); // 切换回接收模式 k_free(tx); } } else { k_fifo_put(&fifo_uart_tx_data, tx); } } void rs485_thread(void) { if (rs485_init() != 0) { LOG_ERR("RS485 initialization failed"); return; } LOG_INF("RS485 thread started"); // 初始化自旋锁 k_spinlock_init(&data_spinlock); // 线程主循环 while (1) { k_sleep(K_FOREVER); } } K_THREAD_DEFINE(rs485_thread_id, 2048, rs485_thread, NULL, NULL, NULL, 5, 0, 0);
06-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值