看 Raw-os 源码所得, 基于stm32f10x的micro_raw_os_fix
本帖最后由 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 voidBSP_IntHandler (CPU_DATAint_id)
{
CPU_FNCT_VOIDisr;
if (int_id < BSP_INT_SRC_NBR) {
isr = BSP_IntVectTbl;
if (isr != (CPU_FNCT_VOID)0) {
isr();
}
}
}
可以看出上面的 BSP_INT_ID_XXX 是自行定义的中断函数指针数组
BSP_IntVectTbl[]的下标, 其具体实现过程如下:
//先定义一个函数指针:
typedef void (*CPU_FNCT_VOID)(void);// in bsp.h
//然后定义一个中断函数指针数组(你也可以称它向量表):
staticCPU_FNCT_VOIDBSP_IntVectTbl;// in bsp_int.c
表有了,那么就要将中断向量功能函数放入表中:
voidBSP_IntVectSet( CPU_DATA int_id,
CPU_FNCT_VOIDisr)
{
RAW_SR_ALLOC();
if (int_id < BSP_INT_SRC_NBR) {
RAW_CPU_DISABLE();
BSP_IntVectTbl = 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;
}
} 本帖最后由 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))
实际上就是执行指针函数 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() 去执行其它初始化。 本帖最后由 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消息
以上是我的理解,如有不对欢迎指出,谢谢! 本帖最后由 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; //接受了消息的AO的prio值在就绪表中的第几个字节中
idle_high_priority = ((y << 3) + //每个字节8个位代表8个优先级,即基数
raw_idle_map_table]); //从就绪表中得到该字节,以这个字节数值为下标查表
//raw_idle_map_table[],得到的数值就是
//接受了消息的AO的prio值在该字节的位数,
//加上基数就得到该AO的优先级数
//然后以这个优先级数为下标查表active_idle_task[],得到的AO就是我们要找的、接受了消息的AO
acb = &active_idle_task;
a =active_idle_task.act; //此处记录的是该AO的优先级在就绪表中的索引、消息计数、入出列索引等信息
--a->nUsed; //消息计数减1
if (a->nUsed == 0) //没有消息了
{ //索引到就绪表中该AO的优先级的位置清除标志
raw_rdy_tbl &= (RAW_U8)~a->priority_bit_x; //位数标志
if (raw_rdy_tbl == 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.sig;
temp.arg = acb->queue.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();
}
}
} 先mark了,慢慢看,{:lol:} wangkx1990 发表于 2015-5-4 17:11
先mark了,慢慢看,
多谢指教! 说实话,有没有一个点灯的例子。不要一上来就是系统分析,拿来能用起来是第一步。 {:victory:}{:victory:}{:victory:} 本帖最后由 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了
sunliezhi 发表于 2015-5-5 13:07
整了个闪灯的例子!
说明:
谢谢,我下来学习一下。 wxlcj 发表于 2015-5-5 14:46
谢谢,我下来学习一下。
看懂了其中的状态机转换过程之后,修改就不难了
其中的优先级设置我还没看懂,继续看 本帖最后由 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[], 以置位相应标志位表明该任务就绪 micro raw-os 非常适合小资源的MCU上跑
还支持类似多优先级的功能 3444542 发表于 2017-4-20 12:48
micro raw-os 非常适合小资源的MCU上跑
还支持类似多优先级的功能
确实,兄台定是深有研究呀 sunliezhi 发表于 2017-4-20 22:22
确实,兄台定是深有研究呀
现在用的raw-os在 stm32F407在产品上,很棒,功能特性很多
micro raw-os的代码很少,我是因为看懂了raw-os的状态机,后来才理解的QP状态机,主要是QP代码宏太多了
micro raw-os的状态就更精简 raw-os复杂些,本来打算啃完micro-接下来就看看它,后来上班了就放下了
常联系! 时隔三年, 现在再来看当时写的笔记,已经看不大懂了{:sweat:}
值得欣慰的是保存了当时的实例(我电脑上面的已经找不到了),可以下载来慢慢消化
页:
[1]