RT-Thread 隐藏的宝藏之 completion

1. completion 是什么

completion 直接翻译过来是完成,所以我们可以称 rt_completion完成量。在 RT-Thread 的文档中心 中讲线程间同步时,介绍了 信号量互斥量事件集rt_completion 的作用也是实现线程间的同步,他是一个 轻量级的二值信号量

2. completion 怎么使用

completion 的使用非常简单

  1. 定义一个完成量

    struct rt_completion completion;
    
  2. 初始化完成量

    rt_completion_init(&completion);
    
  3. 等待完成量

    rt_completion_wait(&completion);
    
  4. 释放完成量

    rt_completion_done(&completion);
    

3. completion 的实现

completion 的 API 非常少,可以通过简单的代码去分析

  1. 初始化完成量

    void rt_completion_init(struct rt_completion *completion)
    {
        rt_base_t level;
        RT_ASSERT(completion != RT_NULL);
    
        level = rt_hw_interrupt_disable();
        completion->flag = RT_UNCOMPLETED;
        rt_list_init(&completion->suspended_list);
        rt_hw_interrupt_enable(level);
    }
    

    干了两件事:

    • 设置 flagRT_UNCOMPLETED
    • 初始化完成量的链表
  2. 等待完成量(以下代码有删减)

    rt_err_t rt_completion_wait(struct rt_completion *completion,
                                rt_int32_t            timeout)
    {
        result = RT_EOK;
        thread = rt_thread_self();
    
        level = rt_hw_interrupt_disable();
        if (completion->flag != RT_COMPLETED)
        {
            if (timeout == 0)
            {
            
            }
            else
            {
                /* reset thread error number */
                thread->error = RT_EOK;
    
                /* suspend thread */
                rt_thread_suspend(thread);
                /* add to suspended list */
                rt_list_insert_before(&(completion->suspended_list),
                                      &(thread->tlist));
    
                /* current context checking */
                RT_DEBUG_NOT_IN_INTERRUPT;
    
                /* start timer */
                if (timeout > 0)
                {
                    /* reset the timeout of thread timer and start it */
                    rt_timer_control(&(thread->thread_timer),
                                     RT_TIMER_CTRL_SET_TIME,
                                     &timeout);
                    rt_timer_start(&(thread->thread_timer));
                }
                /* enable interrupt */
                rt_hw_interrupt_enable(level);
    
                /* do schedule */
                rt_schedule();
    
                /* thread is waked up */
                result = thread->error;
    
                level = rt_hw_interrupt_disable();
            }
        }
        /* clean completed flag */
        completion->flag = RT_UNCOMPLETED;
    
        return result;
    }
    

    主要做了以下工作:

    • 关中断:rt_hw_interrupt_disable();
    • 挂起当前线程:rt_thread_suspend(thread);
    • 把挂起状态插入到线程的链表中:rt_list_insert_before
    • 确保当前函数执行不是在中断中:RT_DEBUG_NOT_IN_INTERRUPT;
    • 设置并启动定时器:rt_timer_start(&(thread->thread_timer));
    • 开中断:rt_hw_interrupt_enable(level);
    • 开调度器:rt_schedule();
    • 获取当前线程状态:result = thread->error;
    • 设置完成量的标志位:completion->flag = RT_UNCOMPLETED;
    • 返回线程状态

    这样就完成了线程的挂起。

  3. 释放完成量(以下代码有删减)

    void rt_completion_done(struct rt_completion *completion)
    {
        level = rt_hw_interrupt_disable();
        completion->flag = RT_COMPLETED;
    
        if (!rt_list_isempty(&(completion->suspended_list)))
        {
            /* there is one thread in suspended list */
            struct rt_thread *thread;
    
            /* get thread entry */
         thread = rt_list_entry(completion->suspended_list.next,
                                   struct rt_thread,
                                tlist);
    
            /* resume it */
            rt_thread_resume(thread);
            rt_hw_interrupt_enable(level);
    
            /* perform a schedule */
            rt_schedule();
        }
    }
    

    主要做了以下工作:

    • 关中断:rt_hw_interrupt_disable();

    • 设置 flagRT_COMPLETED

    • 检查链表不为空:rt_list_isempty

    • 获取到当前等待完成量的句柄:rt_list_entry

    • 启动被挂起的线程:rt_thread_resume(thread);

    • 开中断:rt_hw_interrupt_enable(level);

    • 开调度:rt_schedule();

4. completion 与信号量的对比

  1. completion API 个数少,资源占用少,只能释放获取,不支持多次释放
  2. semaphore API 个数多,资源占用较多,使用灵活,可以尝试获取,可以多次释放,

5. completion 如何加入工程

  1. 标准版 RT-Thread 中的 completion 源码在 "\rt-thread\components\drivers\src\completion.c"在你要使用的文件中#include completion.h直接就可以使用。
  2. Nano 版 RT-Thread 直接拷贝completion.ccompletion.h 添加到工程就可以使用。
要使用ADC DMA,您需要执行以下步骤: 1. 在RT-Thread Studio中创建一个新的RT-Thread项目,并在末尾添加ADC和DMA设备驱动程序。 2. 初始化ADC和DMA设备并设置其参数。 3. 在DMA传输完成后,将数据从DMA缓冲区传输到用户缓冲区。 4. 启动DMA传输并等待传输完成。 以下是一个使用ADC DMA的示例代码: ```c #include <rtthread.h> #include <rtdevice.h> #define ADC_DEV_NAME "adc1" #define DMA_DEV_NAME "dma1" #define SAMPLE_NUM 1024 static rt_uint16_t adc_buffer[SAMPLE_NUM]; static void adc_dma_callback(struct rt_completion *comp) { rt_completion_done(comp); } void adc_dma_test(void) { rt_device_t adc_dev, dma_dev; struct rt_completion adc_dma_complete; rt_err_t result; adc_dev = rt_device_find(ADC_DEV_NAME); if (adc_dev == RT_NULL) { rt_kprintf("Failed to find ADC device!\n"); return; } dma_dev = rt_device_find(DMA_DEV_NAME); if (dma_dev == RT_NULL) { rt_kprintf("Failed to find DMA device!\n"); return; } result = rt_device_open(adc_dev, RT_DEVICE_OFLAG_RDWR); if (result != RT_EOK) { rt_kprintf("Failed to open ADC device!\n"); return; } result = rt_device_open(dma_dev, RT_DEVICE_OFLAG_RDWR); if (result != RT_EOK) { rt_kprintf("Failed to open DMA device!\n"); rt_device_close(adc_dev); return; } /* Configure ADC */ rt_adc_enable(adc_dev, RT_TRUE); rt_adc_set_sample_rate(adc_dev, 100000); rt_adc_set_resolution(adc_dev, 12); /* Configure DMA */ rt_dma_set_callback(dma_dev, adc_dma_callback, &adc_dma_complete); rt_dma_set_source(dma_dev, RT_NULL); rt_dma_set_destination(dma_dev, adc_buffer); rt_dma_set_data_size(dma_dev, SAMPLE_NUM * sizeof(rt_uint16_t)); rt_dma_set_direction(dma_dev, RT_DMA_PERIPH_TO_MEMORY); rt_dma_set_peripheral_request(dma_dev, rt_device_get_irq(adc_dev)); /* Start DMA transfer */ rt_completion_init(&adc_dma_complete); rt_dma_start(dma_dev); rt_adc_start(adc_dev); /* Wait for DMA transfer to complete */ rt_completion_wait(&adc_dma_complete, RT_WAITING_FOREVER); /* Copy data from DMA buffer to user buffer */ // ... /* Cleanup */ rt_device_close(dma_dev); rt_device_close(adc_dev); } ``` 在此示例代码中,我们使用RT-Thread的`rt_adc_enable()`函数启用ADC,并使用`rt_adc_set_sample_rate()`和`rt_adc_set_resolution()`函数设置参数。然后,我们使用`rt_dma_set_callback()`函数设置DMA传输完成时的回调函数,并使用`rt_dma_set_source()`、`rt_dma_set_destination()`、`rt_dma_set_data_size()`和`rt_dma_set_direction()`函数设置DMA参数。最后,我们使用`rt_completion_init()`函数初始化一个RT-Thread完成对象,并使用`rt_dma_start()`和`rt_adc_start()`函数启动DMA和ADC传输。在DMA传输完成后,我们使用`rt_completion_wait()`函数等待DMA传输完成,并使用`rt_device_close()`函数关闭ADC和DMA设备。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值