RT_Thread学习笔记_1、针对单个线程、新建线程、钩子函数、临界区保护

一、新建线程,并完成一个LED闪烁,介绍我们完成一个最简单的程序需要做些什么。rtt提供自己的GPIO驱动架构,IO的初始化(rt_pin_mode())、写入(rt_pin_write())、读入(rt_pin_read())三个函数。

  1. 我们以LED闪烁为诉求,新建test.c文件,我们先写个要调用的函数led_entry(),然后写创建线程分配资源的函数led_test()
    static void led_entry(void *parameter)
    {
        rt_pin_mode(GPIOF9,PIN_MODE_OUTPUT);
    
        while (1)
        {
    			  rt_pin_write(GPIOF9, PIN_HIGH);
            rt_thread_mdelay(500);
            rt_pin_write(GPIOF9, PIN_LOW);
            rt_thread_mdelay(500);
    			
        }
    }
    
    
    void led_test(void)
    {
    		rt_thread_t tid1;
        /* 创建线程1,名称是thread1,入口是thread1_entry*/
        tid1 = rt_thread_create("thread1",
                                led_entry, RT_NULL,
                                THREAD_STACK_SIZE,
                                THREAD_PRIORITY, THREAD_TIMESLICE);
        
        /* 如果获得线程控制块,启动这个线程 */
        if (tid1 != RT_NULL)
            rt_thread_startup(tid1);
    }
  2. 填上头文件和必要的宏定义
    #include <rtthread.h>
    #include <rtdevice.h>
    
    #define THREAD_PRIORITY         10 //优先级
    #define THREAD_STACK_SIZE       512 //资源
    #define THREAD_TIMESLICE        10 //时间片
    #define GPIOF9 89   //drv_gpio.c里面查得到

     

  3. 找到main.c,添加全局函数,并且调用,完成。

    extern void led_test(void);
    
    int main(void)
    {
    		led_test();
    
    
        return RT_EOK;
    }

     

二、主要介绍创建线程的创建函数,下面是官方给出的函数说明。

 * This function will create a thread object and allocate thread object memory
 * and stack.
 *
 * @param name the name of thread, which shall be unique
 * @param entry the entry function of thread
 * @param parameter the parameter of thread enter function
 * @param stack_size the size of thread stack
 * @param priority the priority of thread
 * @param tick the time slice if there are same priority thread
 *
 * @return the created thread object
 */
rt_thread_t rt_thread_create(const char *name,
                             void (*entry)(void *parameter),
                             void       *parameter,
                             rt_uint32_t stack_size,
                             rt_uint8_t  priority,
                             rt_uint32_t tick)
{
  1. priority参数代表优先级,描述线程竞争资源的能力。
    1. 用户可以通过rt_config.h中的RT_THREAD_PRIORITY_MAX宏来修改最大支持的优先级。
    2. stm32默认支持32个优先级,最低优先级预留给空闲线程。
    3. 任务A抢占调度当前任务的发生条件:
      1. 有任务A的优先级大于当前任务的优先级。
      2. 任务A处于就绪状态。
    4. 将程序中实时性要求较高的任务优先级设置成高优先级。
  2. tick参数代表时间片,对相同的优先级的就绪线程采用时间片轮转调度。时间片起到约束线程单次运行时长作用,单位是一个时间节拍。
    1. 时间片轮询,保证两个线程轮流占用处理器。
  3. parameter参数可以将需要的变量传入调用的函数里
    // 线程调用的时候向函数里传入了(void*)类型的1    
    tid = rt_thread_create("thread1", 
                                thread_entry, (void*)1, 
                                THREAD_STACK_SIZE, 
                                THREAD_PRIORITY, THREAD_TIMESLICE)
    
    //这是函数里面同样需要void*类型的参数
    static void thread_entry(void* parameter)

     

 

三、空闲线程钩子函数

  1. 空闲线程钩子函数可以让系统在空闲的时候执行一些非紧急事务,例如系统运行指示灯闪烁,CPU使用率统计等等。
  2. rt_err_t rt_thread_idle_sethook(void(*hook)(void))设置钩子函数,放置在创建线程的函数中。
  3. rt_err_t rt_thread_idle_delhook(void(*hook)(void))删除钩子函数,放置在线程调用的函数中。
  4. 空闲线程是一个线程状态永远为就绪态的线程,所以钩子函数中不得包含使线程挂起的阻塞类函数,例如:rt_thread_delay()、rt_sem_take()。
  5. 如果需要打印系统线程的转换关系:可以在创建线程的函数中调用系统调度钩子函数rt_scheduler_sethook(),要注意的是对应的调度函数为:static void hook_of_scheduler(struct rt_thread* from, struct rt_thread* to){},里面的参数不能变。别的可以参考官方给的例程。
    int scheduler_hook(void)
    {   
        /* 设置调度器钩子 */
        rt_scheduler_sethook(hook_of_scheduler);
        
        /* 创建线程1 */
        tid1 = rt_thread_create("thread1", 
                                thread_entry, (void*)1, 
                                THREAD_STACK_SIZE, 
                                THREAD_PRIORITY, THREAD_TIMESLICE); 
        if (tid1 != RT_NULL) 
            rt_thread_startup(tid1);
    
        /* 创建线程2 */
        tid2 = rt_thread_create("thread2", 
                                thread_entry, (void*)2, 
                                THREAD_STACK_SIZE, 
                                THREAD_PRIORITY,THREAD_TIMESLICE - 5);
        if (tid2 != RT_NULL) 
            rt_thread_startup(tid2);
        return 0;
    }

        

四、对调度的函数进行临界区保护

  1. 临界资源:一次仅允许一个线程访问的共享资源,可以是一个具体的硬件设备(比如打印机),也可以是一个变量,一个缓冲区。不论是硬件或软件的资源,多个线程必须互斥的对他们进行访问。一段时间里只允许一个线程进行访问。

  2. 每个线程访问临界资源的代码称为临界区,每次只允许一个线程进入临界区。

  3. 两个方法保护临界区:

    1. 禁止调度:给任务调度器加锁,不让进行线程切换

      1. 上锁:rt_enter_critical()

      2. 解锁:rt_exit_critical()

      3. 放在需要调度的线程里。

    2. 关闭中断:因为所有的线程调度是建立在中断的基础上。但是此时所有的中断都不能用了,所有不建议用。

      void thread_entry(void *parameter)
      {
          rt_uint32_t level;
      
          while (1)
          {
              /* 关闭中断 */
              level = rt_hw_interrupt_disable();
              {
                  //写自己需要的代码
              }
      
              rt_hw_interrupt_enable(level);
      
          }
      }

 

 

 

 

 

参考:https://www.rt-thread.org/document/site/tutorial/kernel/kernel-video/

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值