OpenEdv-开源电子网

 找回密码
 立即注册

扫一扫,访问微社区

正点原子新作:阿波罗STM32F767&F429&探索者STM32F4开发板&赶快来下载资料哦。

查看: 808|回复: 7

基于STM32的micro raw os源码分析

[复制链接]

  离线 

1

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
58
金钱
58
注册时间
2016-3-2
在线时间
6 小时
发表于 2016-3-2 22:35:14 | 显示全部楼层 |阅读模式
正点原子公众号

  这是别人对micro raw os源码的一些分析,个人觉得写的还可以。现在贴出来给大家看看,希望给那些和我一样在看micro raw os的伙伴一些帮助。


  之一:   中断向量及中断初始化的过程

    在 vectors.s中, 首先引入自定义的中断向量功能函数的声明,
        这些函数位于ST官方(暂且这么称呼 )中断向量表的第15个位置开始向后排列

    接下来安排中断向量表:
                0--  栈顶位置指针;
                1--  复位中断 (处理过程在init.s);
                    ......
                15-  SysTick_Handler
                16-  BSP_IntHandlerXXX
                    ......

    在 bsp_int.c中,vectors.s引入的中断向量功能函数被定义为:
            BSP_IntHandlerXXX(void)
            {
                BSP_IntHandler(BSP_INT_ID_XXX);
            }
    也就是说,当发生BSP_IntHandlerXXX中断时,PC就会被引向这里
        执行函数 BSP_IntHandler(BSP_INT_ID_XXX)

    先来看看 BSP_IntHandler() 函数: // in bsp_int.c
    static void  BSP_IntHandler (CPU_DATA  int_id)
    {
        CPU_FNCT_VOID  isr;

        if (int_id < BSP_INT_SRC_NBR) {
            isr = BSP_IntVectTbl[int_id];
            if (isr != (CPU_FNCT_VOID)0) {
                isr();
            }
        }
    }

    可以看出上面的 BSP_INT_ID_XXX 是自行定义的中断函数指针数组
    BSP_IntVectTbl[]的下标, 其具体实现过程如下:

            //先定义一个函数指针:
        typedef     void     (*CPU_FNCT_VOID)(void);// in bsp.h
            //然后定义一个中断函数指针数组(你也可以称它向量表):
        static  CPU_FNCT_VOID  BSP_IntVectTbl[BSP_INT_SRC_NBR];// in bsp_int.c

    表有了,那么就要将中断向量功能函数放入表中:
    void  BSP_IntVectSet(   CPU_DATA       int_id,
                                            CPU_FNCT_VOID  isr)
    {
            RAW_SR_ALLOC();

            if (int_id < BSP_INT_SRC_NBR) {
                    RAW_CPU_DISABLE();
                    BSP_IntVectTbl[int_id] = isr;
                    RAW_CPU_ENABLE();
            }
    }
        没错,BSP_IntVectSet()就是这个作用!
        这个函数就是用来提供给我们设置自己的中断函数用的!
        来看看例子:
        BSP_IntVectSet(37,Uart_RxInt);  // in Uart.c

        这个序号 37 是在我们自己定义的表中的序号,其实这里
        我觉得用 BSP_INT_ID_USART1 代替37更好

    好了,串口1接收数据处理函数已经设置到我们的向量表中,
    如果使能了串口1接收中断,当串口1接收到数据后
        就会执行 BSP_IntHandler(BSP_INT_ID_USART1),
        这个位置就是我们放置的 Uart_RxInt()
        Uart_RxInt()又调用 sub_Uart0_RxInt()
        这样一来,我们就可以处理按键事件了:

        void sub_Uart0_RxInt(void)
        {
                RAW_U8 aa;
        
                aa = DFU_UART_PORT->DR;


                switch (aa) {
               
                        case 'u':
                                event_end_post(&l_bomb22.a1, UP_SIG, 0);
                                break;
                        
                        case 'd':
                                event_end_post(&l_bomb22.a1, DOWN_SIG, 0);
                                break;
                        
                        case 'a':
                                event_end_post(&l_bomb22.a1, ARM_SIG, 0);
                                break;

                        default:
                                break;
                }
        }
转自http://cache.baiducontent.com/c?m=9d78d513d99c1afe18b0837e7c5684230e54f13e7dc3975521dbc90ed5264c40347bfefe62670704a496233a51ff084bea87672f681e74eadb8fd75dddccd0726d9f26402f41d05613a55df49c03639b60c709bfb819e3fcab6484afa4d4db5450ce580f6d80f4d407504f9438e71547e3&p=882a9546d5dd06fb109fca2d021488&newp=8d738f1a85cc43ff57ed977d554794231610db2151d4d716&user=baidu&fm=sc&query=micro+raw+os&qid=8d13d9d800190e17&p1=1


回复

使用道具 举报

  离线 

1

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
58
金钱
58
注册时间
2016-3-2
在线时间
6 小时
 楼主| 发表于 2016-3-2 22:35:58 | 显示全部楼层
之二:   Bomb状态机初始化

    从 main() 可知,初始化是从 bomb_test()开始的

    历经 event_init(),

    来到 Bomb4_ctor(&l_bomb22, 0xd),
            
        其中 l_bomb22是Bomb4的实例, 定义在 bomb.c:    Bomb4   l_bomb22;
             0xd 是停止倒计时密码
   
        查看Bomb4_ctor()原型:
        static void Bomb4_ctor(Bomb4 *me, RAW_U8 defuse)
        {
                FSM_CONSTRUCTOR( &me->a1.super,
                                                     (stm_state_handler)&Bomb4_initial);
            me->defuse = defuse;
        }
        即是执行如下语句:
            {
                FSM_CONSTRUCTOR( &l_bomb22.a1.super,
                                                 (stm_state_handler)&Bomb4_initial);
                l_bomb22.defuse = defuse;
            }
        由 FSM_CONSTRUCTOR() 原型 #define FSM_CONSTRUCTOR(me, initial)  \
                                                                    do {                                                \
                                                                        (me)->state = 0;                \
                                                                        (me)->temp  = (initial);         \
                                                                    } while (0)

        可知 Bomb4_ctor(&l_bomb22, 0xd) 即是执行了:
        {
            &l_bomb22.a1.super.state = 0;
            &l_bomb22.a1.super.temp  = Bomb4_initial;
            l_bomb22.defuse = 0xd;
        }
            即给函数指针赋初始值, 给密码变量设置密码
            (后来的setting与timing状态之间的转换就是修改这两个函数指针!)

    至此 Bomb4_ctor(&l_bomb22, 0xd) 执行完毕!

    继续......

    来到 fsm_init(&l_bomb22.a1.super, 0);   fsm_init()函数的原型就不列了

        一开始检查 &l_bomb22.a1.super.temp 是不是被设置了,未设置则停机!
        由上面可知 &l_bomb22.a1.super.temp被设置指向了 Bomb4_initial 函数

        接下来执行指针函数,即是 执行 Bomb4_initial(),看函数原型:
        static RAW_U16 Bomb4_initial(Bomb4 *me, STATE_EVENT  *e) {
            (void)e;
            me->timeout = INIT_TIMEOUT;
            return STM_TRAN(&Bomb4_setting);
        }
            又看 STM_TRAN()原型:
            #define STM_TRAN(state)                                             \
                            (((STM_STRUCT *)me)->temp = STM_STATE_CAST(state),  \
                             STM_RET_TRAN)

            以前看 txj 大牛说到 C语言实现面向对象的继承、强制向父类提升.....
                这里 (STM_STRUCT *)me 就是:
                                    me 在这里是 Bomb4类型的变量
                                    Bomb4继承了 ACTIVE_EVENT_STRUCT类型
                                    而 ACTIVE_EVENT_STRUCT又继承了STM_STRUCT
                所以 (STM_STRUCT *)me 是向上提升了2级!

        由上分析可知初始化函数设置了 Bomb爆炸前的倒计时数值;
                            又设置 l_bomb22.a1.super->temp = Bomb4_setting
                            并返回值 STM_RET_TRAN
        至此 Bomb4_initial()执行完毕!

        继续.....

        判断返回值是否是 STM_RET_TRAN, 没错啦

        继续.....
        
        STM_TRIG(me->temp, STM_ENTRY_SIG);
        
            看 STM_TRIG() 原型:
            #define STM_TRIG(state, sig)            \
                            ((*(state))(me, &STM_GLOBAL_EVENT[sig]))
            实际上就是执行指针函数 l_bomb22.a1.super->temp,即 Bomb4_setting()
                                            输入的参数是 STM_ENTRY_SIG
            去看看 Bomb4_setting()中的 case STM_ENTRY_SIG:
            static RAW_U16 Bomb4_setting(Bomb4 *me, STATE_EVENT  *e)
            {
                switch (e->sig)
                {
                            case STM_ENTRY_SIG:
                    {
                        me->code = 0;           /* clear the defuse code */
                        return STM_RET_HANDLED;
                    }
                .....
            很简单,清零用户输入

        继续......

            me->state = me->temp;  同步,即设置函数指针 state:
                                    l_bomb22.a1.super->state = Bomb4_setting

    至此 fsm_init(&l_bomb22.a1.super, 0) 执行完毕!

    最后就是 打印开机提示信息。。。。。

至此 bomb_test() 执行完毕,返回 main() 去执行其它初始化。

回复 支持 反对

使用道具 举报

  离线 

1

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
58
金钱
58
注册时间
2016-3-2
在线时间
6 小时
 楼主| 发表于 2016-3-2 22:36:42 | 显示全部楼层
之三: 从event_tick_isr() 看链表的运用

    说到链表,我心里也是惴惴然,因为水平有限,怕说错就罪大了

    我的理解是:链表就是一串节点手拉手连接在一起,这些节点都是谁的?

    一群节点总得有个头儿,否则互相不服啊,这个头就叫链表头吧。

    我们先来看看结构体 ACTIVE_EVENT_STRUCT

    typedef struct ACTIVE_EVENT_STRUCT {

            STM_STRUCT super;
        
            RAW_TICK_TYPE tick_ctr;
        
            LIST   idle_tick_list;
               
            RAW_U8 head;

            RAW_U8 prio;
        
            RAW_U8 tail;
        
            RAW_U8 nUsed;
        
            RAW_U8 priority_bit_x;
            RAW_U8 priority_bit_y;

            RAW_U8 priority_x;
            RAW_U8 priority_y;

    } ACTIVE_EVENT_STRUCT;

    这个结构体里面保存的是 active_object(活动对象)的一些属性,
        比如:隔多久(tick_ctr)去提醒该活动对象超时--该做什么可以做了
              该活动对象的队列开始、结束于哪里,队列中的消息
              该活动对象的函数指针等等
        这个结构体里面有个节点叫 idle_tick_list,
        以后看到某个链表中的节点类型如果是 idle_tick_list的话,
        我们就知道这些节点是谁的了: ACTIVE_EVENT_STRUCT类型活动对象的!

    链表的这个特性跟我们从事劳动时遇到的事情是一样一样的:
        还记得我们在老家地里挖红薯么? 一锄头下去,两个指头捏住地面上
        的红薯藤向上提,就提出好几个大大小小的红薯来。
        是的,我们不须用两只大手去包一个坑里的所有的红薯,只须两个
        指头捏住藤,这个藤就是这一坑红薯的节点!

    好,看 event_tick_isr() 源码:
    {
        LIST *head;
            LIST *iter;
            LIST *iter_temp;
                定义了3个节点指针,下面要用

        head = &raw_idle_tick_head;
                raw_idle_tick_head 在 raw_obj.c中定义,
                在此作为链表头,并用指针head指向该链表头

        iter = head->next; 用指针iter指向链表中的第一个节点作为当前节点
                    (链表头head虽然也是节点类型,但不要用,
                     所以第一个节点就是 head->next 了)

        while (iter != head)  当前节点指向的下一节点不是链表头,即
                                            还未到达链表尾
        {
            a =  list_entry(iter,
                            ACTIVE_EVENT_STRUCT,
                            idle_tick_list);
                list_entry() 哈哈,作用就是求某活动对象结构体的首地址!

                要得到该首地址,那你有已知条件吗?  有!
                已知条件:
                        该活动对象结构体中节点的绝对位置(即内存地址)iter;
                        该活动对象结构体类型ACTIVE_EVENT_STRUCT;
                        节点idle_tick_list在该结构体中的偏移(这是相对地址)

                打个比喻:
                        你是公安部网上追逃链表中的‘节点’,现在知道你的位置;
                        你们的组织有个形式,类似结构体;
                        还知道你在该组织中排行第三,类似节点idle_tick_list在
                                                    结构体中的偏移
                    找到你之后“顺藤摸瓜”向下移动3个位置,就找到了你们组织的位置

                好了,现在得到了某活动对象的首地址

            iter_temp =  iter->next; 用iter_temp存放当前节点指向的下一个节点
                                        的位置

            if (a->tick_ctr)  由上面可知 a 保存了该活动对象结构体的首地址
                              a->tick_ctr由编译器安排一个临时变量存放
                                        该活动对象结构体中的“隔多久”参数
                如果“隔多久”参数 > 0

            {
                --a->tick_ctr;  “隔多久”参数 减 1个
                if (a->tick_ctr == 0) “隔多久”参数 减到0了
                {
                    list_delete(    iter);  从链表中删去该活动对象的节点

                    event_end_post(
                                    a,
                                    STM_TIMEOUT_SIG,
                                    0);
                        向该活动对象发送一次 timeout 消息,用户面对的
                        倒计时数减 1
                }
            }

            iter = iter_temp;   查找下一个节点所在结构体的首地址
        }
         如果遍历完整个链表则退出 while循环
    }
        
        event_tick_isr() 被 SysTick_Handler() 调用。这样就可以确定隔多久发送一次timeout消息


回复 支持 反对

使用道具 举报

  离线 

1

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
58
金钱
58
注册时间
2016-3-2
在线时间
6 小时
 楼主| 发表于 2016-3-2 22:36:58 | 显示全部楼层
之四: event_sche() 大循环和 fsm_execute() 状态机执行过程
void event_sche()
{
        ACTIVE_EVENT_STRUCT *a;
        STATE_EVENT temp;
        ACTIVE_EVENT_STRUCT_CB *acb;
        RAW_U8 y;
        RAW_U8 idle_high_priority;
        RAW_SR_ALLOC();
        while (1)
       {
                RAW_CPU_DISABLE();
                /*if get events then process it*/
                if (raw_idle_rdy_grp)                 // event_post()中: raw_idle_rdy_grp |= acb->act->priority_bit_y
                {
                        y = raw_idle_map_table[raw_idle_rdy_grp];      //接受了消息的AO的prio值在就绪表中的第几个字节中
                        idle_high_priority = ((y << 3) +                                            //每个字节8个位代表8个优先级,即基数
                                                       raw_idle_map_table[raw_rdy_tbl[y]]);    //从就绪表中得到该字节,以这个字节数值为下标查表
                                                                                                                 //raw_idle_map_table[],得到的数值就是
                                                                                                                 //接受了消息的AO的prio值在该字节的位数,
                                                                                                                 //加上基数就得到该AO的优先级数
                        //然后以这个优先级数为下标查表active_idle_task[],得到的AO就是我们要找的、接受了消息的AO
                        acb = &active_idle_task[idle_high_priority];

                        a   =  active_idle_task[idle_high_priority].act;   //此处记录的是该AO的优先级在就绪表中的索引、消息计数、入出列索引等信息
                        --a->nUsed;                                                    //消息计数减1
                        if (a->nUsed == 0)                                  //没有消息了
                            {                                   //索引到就绪表中该AO的优先级的位置清除标志
                                raw_rdy_tbl[a->priority_y] &= (RAW_U8)~a->priority_bit_x;         //位数标志
                                if (raw_rdy_tbl[a->priority_y] == 0)                                                //该字节中其它优先级的AO没有消息
                                {                                                                                                /* Clear event grp bit if this was only task pending */
                                        raw_idle_rdy_grp &= (RAW_U8)~a->priority_bit_y;        //清除该字节的信息
                                }
                        }                                                               //从该AO的消息队列中取得消息
                        temp.sig = acb->queue[a->tail].sig;
                        temp.arg = acb->queue[a->tail].para;
                        a->tail++;                                                        //消息出列索引(发送消息时用a->head作为消息入列索引)
                        if (a->tail == acb->end)                        //索引到了队列的尾部
                            {
                                    a->tail = 0;    //折回头部
                        }
                        RAW_CPU_ENABLE();
                        #if (RAW_FSM_ACTIVE > 0)                //配置为真
                                                                                                    //    (STM_STRUCT *me, STATE_EVENT *e)
                        fsm_exceute(&a->super, &temp);                // 将消息作为参数传给状态机函数
                        #else
                        hsm_exceute(&a->super, &temp);
                        #endif
                }                //if (raw_idle_rdy_grp)
                else
                {
                        RAW_CPU_ENABLE();
                        RAW_CPU_DISABLE();
                        if (raw_idle_rdy_grp == 0)
                            {                                           //进入低功耗, 等待中断的到来
                                event_user();                //event_user() is in bomb.c
                        }
                        RAW_CPU_ENABLE();
                }
            }
}
回复 支持 反对

使用道具 举报

  离线 

113

主题

1120

帖子

5

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
3029
金钱
3029
注册时间
2015-10-15
在线时间
1055 小时
发表于 2016-3-3 14:28:09 | 显示全部楼层
帮顶
回复 支持 反对

使用道具 举报

  离线 

1

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
58
金钱
58
注册时间
2016-3-2
在线时间
6 小时
 楼主| 发表于 2016-3-3 14:43:04 | 显示全部楼层

原来这个板块还有人啊,哈哈
回复 支持 反对

使用道具 举报

  离线 

113

主题

1120

帖子

5

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
3029
金钱
3029
注册时间
2015-10-15
在线时间
1055 小时
发表于 2016-3-3 14:56:46 | 显示全部楼层
Yvan 发表于 2016-3-3 06:43
原来这个板块还有人啊,哈哈

大杂烩里面都会显示的
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
微信公众平台:正点原子   点击扫码添加
回复 支持 反对

使用道具 举报

  离线 

0

主题

2

帖子

0

精华

新手上路

Rank: 1

积分
27
金钱
27
注册时间
2015-6-23
在线时间
2 小时
发表于 2017-3-13 16:14:05 | 显示全部楼层
楼主  rawos  tjx也不维护了    你这个源码能发我一份么   下载不到了    我想学学这个简单实用  以前有  后来删了  邮箱348567800@qq.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则




QQ|联系我们|手机版|官方淘宝店|新浪微博|微信公众平台|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2017-11-22 03:31

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表
/* */