打印
[应用相关]

RT-Thread 基于stm32 读后感

[复制链接]
楼主: dingbo95
手机看帖
扫描二维码
随时随地手机跟帖
41
dingbo95|  楼主 | 2018-10-28 15:24 | 只看该作者 回帖奖励 |倒序浏览
具体定义如下:
/* 已知一个结构体里面的成员地址,反推出结构体的首地址 */

#define rt_container_of(ptr,type,member) \
          ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))


#define rt_list_entry(node, type, member) \
          rt_container_of(node, type, member)


使用特权

评论回复
42
dingbo95|  楼主 | 2018-10-28 15:24 | 只看该作者
我们主要关注的是这个地方   ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))  这里究竟如何实现的,下面一步步来分析

使用特权

评论回复
43
dingbo95|  楼主 | 2018-10-28 15:24 | 只看该作者
先看&((type *)0)->member:  把“0”强制转换为指针类型,从而该指针指向了数据段的基址,可以取到以“0”为基地址的一个type型变量member域的地址。因此这个地址也就等于member域到结构体基地址的偏移字节数。

使用特权

评论回复
44
dingbo95|  楼主 | 2018-10-28 15:25 | 只看该作者
再来看 ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))):(char *)(ptr)使得指针的加减操作步长为一字节,(unsigned long)(&((type*)0)->member)等于ptr指向的member到该member所在结构体基地址的偏移字节数。二者一减便得出该结构体的地址。转换为(type *)型的指针,完成!

使用特权

评论回复
45
dingbo95|  楼主 | 2018-10-28 15:25 | 只看该作者
RT-Thread  ——初始化线程栈线程第一次运行的时候,加载到CPU寄存器的参数就放在线程栈里面,改函数 rt_hw_stack_init()在cpuport.c中实现。

使用特权

评论回复
46
dingbo95|  楼主 | 2018-10-28 15:27 | 只看该作者

具体代码如下:

/*  初始化线程栈  */
rt_uint8_t *rt_hw_stack_init(void      *tentry,
                                   void      *parameter,
                                                                                                                 rt_uint8_t   *stack_addr)
{
      struct stack_frame *stack_frame;
            rt_uint8_t  *stk;
            unsigned long  i;
       
            /* 获取栈顶指针 rt_hw_stack_init 在调用时,传给stack_addr的是(栈顶指针-4)*/
            stk = stack_addr + sizeof(rt_uint32_t);
            
            /*让stk指针向下8字节对齐*/
            stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk,8);
       
            /*stk指针继续向下移动sizeof(struct stack_frame)个偏移*/
            stk -=sizeof(struct stack_frame);
            
            /* 将stk指针强制转换为 stack_frame类型后存到stack_frame*/
            stack_frame =(struct stack_frame *)stk;
       
            /*以stack_frame 为起始地址,将栈空间里面的sizeof(struct stack_frame)个内存初始化为0xdeadbeef */
            for(i=0; i<sizeof(struct stack_frame)/sizeof(rt_uint32_t); i++)
            {
                    ((rt_uint32_t *)stack_frame)[i] = 0xdeadbeef;
                  }
            
                        /*初始化异常发生时自动保存的寄存器*/
                        stack_frame->exception_stack_frame.r0 =(unsigned long)parameter;
                        stack_frame->exception_stack_frame.r1  = 0;                        /* r1 */
      stack_frame->exception_stack_frame.r2  = 0;                        /* r2 */
      stack_frame->exception_stack_frame.r3  = 0;                        /* r3 */
      stack_frame->exception_stack_frame.r12 = 0;                        /* r12 */
      stack_frame->exception_stack_frame.lr  = 0;                        /* lr */
      stack_frame->exception_stack_frame.pc  = (unsigned long)tentry;    /* entry point, pc */
      stack_frame->exception_stack_frame.psr = 0x01000000L;              /* PSR */
      
                        /* 返回线程指针 */
                        return stk;
}


使用特权

评论回复
47
dingbo95|  楼主 | 2018-10-28 15:27 | 只看该作者
代码 stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk,8);   让stk这个指针向下8个字节对齐,确保stk是8字节对齐的地址。

使用特权

评论回复
48
dingbo95|  楼主 | 2018-10-28 15:28 | 只看该作者
代码 stk -=sizeof(struct stack_frame);     stk 指针继续向下移动sizeof(struct stack_frame)个偏移,即16字的大小。

使用特权

评论回复
49
dingbo95|  楼主 | 2018-10-28 15:28 | 只看该作者
代码 stack_frame =(struct stack_frame *)stk;   将stk指针强制转换为stack-frame类型后存到指针变量stack_frame中。

使用特权

评论回复
50
dingbo95|  楼主 | 2018-10-28 15:28 | 只看该作者
改函数最终会返回线程栈顶指针stk, 在线程栈初始化函数中,定义了struct  stack_frame 类型的结构体指针stack_frame

使用特权

评论回复
51
dingbo95|  楼主 | 2018-10-28 15:29 | 只看该作者
结构体类型具体如下:
struct exception_stack_frame
{
   /*异常发生时自动加载CPU寄存器的内容*/
         rt_uint32_t  r0;
         rt_uint32_t  r1;
         rt_uint32_t  r2;
         rt_uint32_t  r3;
         rt_uint32_t  r12;
         rt_uint32_t  lr;
   rt_uint32_t  pc;
         rt_uint32_t  psr;
};


struct stack_frame
{
   /*异常发生时需要手动加载CPU寄存器的内容*/
         rt_uint32_t   r4;
         rt_uint32_t   r5;
         rt_uint32_t   r6;
         rt_uint32_t   r7;
         rt_uint32_t   r8;
         rt_uint32_t   r9;
   rt_uint32_t   r10;
         rt_uint32_t   r11;
         struct exception_stack_frame   exception_stack_frame;
};


使用特权

评论回复
52
xuanhuanzi| | 2018-10-28 16:06 | 只看该作者
只有自己动手搞起来才容易理解

使用特权

评论回复
53
dingbo95|  楼主 | 2018-10-28 19:36 | 只看该作者
xuanhuanzi 发表于 2018-10-28 16:06
只有自己动手搞起来才容易理解

动手是一方面 也得看源码啊

使用特权

评论回复
54
dingbo95|  楼主 | 2018-10-28 19:38 | 只看该作者
调度器初始化

调度器是操作系统的核心,主要功能是实现线程的切换,即从就绪列表中找到优先级最高的线程,然后执行该线程。关于调度器的代码,RT-Thread在scheduler.c文件中实现。


使用特权

评论回复
55
dingbo95|  楼主 | 2018-10-28 19:39 | 只看该作者
调度器初始化:调度器在使用前必须初始化,具体代码如下:
/* 初始化系统调度器 */
void rt_system_scheduler_init(void)
{
    register  rt_base_t  offset;
         /* 线程就绪列表初始化 */
         for(offset =0; offset<RT_THREAD_PRIORITY_MAX;offset++)
        {
           rt_list_init(&rt_thread_priority_table[offset]);
        }
        /* 初始化当前线程控制块指针 */
        rt_current_thread = RT_NULL;
}


使用特权

评论回复
56
dingbo95|  楼主 | 2018-10-28 19:39 | 只看该作者
代码中  register  rt_base_t  offset;  定义了一个局部变量,用 register修饰,防止编译器优化。初始化完后整个就绪列表为空。

使用特权

评论回复
57
dingbo95|  楼主 | 2018-10-28 19:39 | 只看该作者
rt_current_thread = RT_NULL; 初始化当前线程控制块为空,rt_current_thread 是在scheduler.c文件中定义的一个struct rt_thread 类型的全局指针,用于指向当前正在运行的线程控制块。

使用特权

评论回复
58
dingbo95|  楼主 | 2018-10-28 19:40 | 只看该作者
调度器的初始化放在硬件初始化之前,具体代码如下:
     /*硬件初始化*/
       
         /* 硬件初始化*/

         /*调度器初始化*/
         rt_system_scheduler_init();


使用特权

评论回复
59
dingbo95|  楼主 | 2018-10-28 19:40 | 只看该作者
启动调度器

调度器的启动由函数rt_system_scheduler_start()来实现,具体代码如下:

/* 启动系统调度器 */
void rt_system_scheduler_start(void)
{
   register struct rt_thread *to_thread;
       
         /* 手动指定第一个运行的线程 */
         to_thread = rt_list_entry(rt_thread_priority_table[0].next,
                                   struct rt_thread,
                                                                                                                 tlist);
         rt_current_thread = to_thread;
               
   /*
                        切换到第一个线程,改函数在context_rvds.S中实现,
      在rthw.h声明,用于实现第一次线程切换。
      当一个汇编函数在C文件中调用的时候,如果有形参,
      则执行的时候会将形参传入CPU寄存器r0
         */                                                                                                 
   rt_hw_context_switch_to((rt_uint32_t)&to_thread->sp);                                                                                                                   
}



使用特权

评论回复
60
dingbo95|  楼主 | 2018-10-28 19:41 | 只看该作者
调度器在启动的时候会从就绪列表中取出优先级最高的线程的线程控制块,然后切换到该线程。目前的线程还不支持优先级,可以手动指定第一个运行的线程为就绪列表下标为0这条链表里面挂着的线程。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则