搜索
bottom↓
回复: 16

看 Raw-os 源码所得, 基于stm32f10x的micro_raw_os_fix

[复制链接]

出0入4汤圆

发表于 2015-5-3 21:53:23 | 显示全部楼层 |阅读模式
本帖最后由 sunliezhi 于 2015-5-3 21:55 编辑

这几天天气好热,趁热看了看raw-os-----micro_raw_os_fix的源码,
下面写写我的理解,还是那句: Any err-correcting or suggestion are Welcome!!!

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

    在 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;
                }
        }

出0入4汤圆

 楼主| 发表于 2015-5-4 14:12:46 | 显示全部楼层
本帖最后由 sunliezhi 于 2015-5-4 14:19 编辑

                    之二:   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() 去执行其它初始化。

出0入4汤圆

 楼主| 发表于 2015-5-4 14:48:01 | 显示全部楼层
本帖最后由 sunliezhi 于 2015-5-4 17:01 编辑

                    之三: 从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消息

    以上是我的理解,如有不对欢迎指出,谢谢!

出0入4汤圆

 楼主| 发表于 2015-5-4 16:57:06 | 显示全部楼层
本帖最后由 sunliezhi 于 2015-5-6 12:07 编辑

                    之四: 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();
                }
            }
}

出0入0汤圆

发表于 2015-5-4 17:11:11 | 显示全部楼层
先mark了,慢慢看,

出0入4汤圆

 楼主| 发表于 2015-5-4 17:22:16 | 显示全部楼层
wangkx1990 发表于 2015-5-4 17:11
先mark了,慢慢看,

多谢指教!

出0入0汤圆

发表于 2015-5-5 08:16:32 | 显示全部楼层
说实话,有没有一个点灯的例子。不要一上来就是系统分析,拿来能用起来是第一步。

出0入0汤圆

发表于 2015-5-5 08:45:58 | 显示全部楼层

出0入4汤圆

 楼主| 发表于 2015-5-5 13:07:37 | 显示全部楼层
本帖最后由 sunliezhi 于 2015-5-5 13:32 编辑
wxlcj 发表于 2015-5-5 08:16
说实话,有没有一个点灯的例子。不要一上来就是系统分析,拿来能用起来是第一步。 ...


整了个闪灯的例子!

说明:
              简单的将 ‘bomb’ 换成了 ‘led’ ;
               ‘a’ 功能:  在setting 和 timing 状态之间切换;
               ‘u’、‘d’ 键在 setting 状态 增、减闪烁频率,在 timing 状态不起作用(取消了密码功能);
              暂时还是沿用的PC键盘,迟点改成采用板上的实体按键

               我的target 是stm32f10x_vet6, 不好意思,文件名写错了,应该是 raw-os, 写成rwa-os了

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2015-5-5 14:46:08 | 显示全部楼层
sunliezhi 发表于 2015-5-5 13:07
整了个闪灯的例子!

说明:

谢谢,我下来学习一下。

出0入4汤圆

 楼主| 发表于 2015-5-5 17:16:45 | 显示全部楼层
wxlcj 发表于 2015-5-5 14:46
谢谢,我下来学习一下。

看懂了其中的状态机转换过程之后,修改就不难了
其中的优先级设置我还没看懂,继续看

出0入4汤圆

 楼主| 发表于 2015-5-6 12:33:14 | 显示全部楼层
本帖最后由 sunliezhi 于 2015-5-6 12:39 编辑

整个系统的结构形式:
                           后台查询消息状态执行  +   前台发送消息(中断)

启用了2个中断资源:
                        1. SysTick中断, 作为定时查询链表之用;
                        2. 串口接收中断, 作为与PC交互之用.

系统维护了:
                       1.  AO任务表active_idle_task[],  以优先级为顺序;
                       2.  AO链表raw_idle_tick_head, 为AO的超时消息提供服务;
                       3.  AO就绪表raw_rdy_tbl[], 以置位相应标志位表明该任务就绪

出0入0汤圆

发表于 2017-4-20 12:48:50 | 显示全部楼层
micro raw-os 非常适合小资源的MCU上跑
还支持类似多优先级的功能

出0入4汤圆

 楼主| 发表于 2017-4-20 22:22:43 | 显示全部楼层
3444542 发表于 2017-4-20 12:48
micro raw-os 非常适合小资源的MCU上跑
还支持类似多优先级的功能

确实,兄台定是深有研究呀

出0入0汤圆

发表于 2017-4-21 17:27:30 | 显示全部楼层
sunliezhi 发表于 2017-4-20 22:22
确实,兄台定是深有研究呀

现在用的raw-os在 stm32F407在产品上,很棒,功能特性很多
micro raw-os的代码很少,我是因为看懂了raw-os的状态机,后来才理解的QP状态机,主要是QP代码宏太多了
micro raw-os的状态就更精简

出0入4汤圆

 楼主| 发表于 2017-4-21 20:32:12 | 显示全部楼层
raw-os复杂些,本来打算啃完micro-接下来就看看它,后来上班了就放下了
常联系!

出0入4汤圆

 楼主| 发表于 2018-8-6 21:43:35 | 显示全部楼层
时隔三年, 现在再来看当时写的笔记,已经看不大懂了  
值得欣慰的是保存了当时的实例(我电脑上面的已经找不到了),可以下载来慢慢消化
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-19 02:03

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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