打印
[应用方案]

Cortex-M内核知识点总结

[复制链接]
楼主: 米多0036
手机看帖
扫描二维码
随时随地手机跟帖
61
米多0036|  楼主 | 2023-9-15 15:46 | 只看该作者 |只看大图 回帖奖励 |倒序浏览
取向量
在入栈的同时 , 指令总线会获取程序状态字中的异常号 ,在中断向量表中查询对应的 中断服务地址入口。

使用特权

评论回复
62
米多0036|  楼主 | 2023-9-15 15:47 | 只看该作者
寄存器更新
更新栈指针SP , 选择栈指针 MSP, 更新连接寄存器LR , 程序计数器PC,在入栈完成后,栈指针会更新(PSP或者MSP因为压栈操作而生长), 在执行服务程序时,将由MSP 负责对堆栈的访问。更新LR 比较特殊,前面我们说过LR 寄存器在函数调用过程中的作用 : 它记录了被调函数的下一条指令地址,这样函数返回后就可以继续执行。但在中断服务中LR 会有特殊的用法 ,在进入中断服务程序后 , LR 会被计算赋值为 EXC_RETURN (异常返回)

使用特权

评论回复
63
米多0036|  楼主 | 2023-9-15 15:47 | 只看该作者

使用特权

评论回复
64
米多0036|  楼主 | 2023-9-15 15:47 | 只看该作者

使用特权

评论回复
65
米多0036|  楼主 | 2023-9-15 15:48 | 只看该作者

使用特权

评论回复
66
米多0036|  楼主 | 2023-9-15 15:48 | 只看该作者
LR的取值只有以上3种(以上为CM3的异常返回,其余需要找对应的ARM核) , 他能修改中断服务返回后的模式和使用哪个堆栈指针 , 还记得CONTROL 寄存器吗 它在特权模式下,也能修改模式和选择栈指针。只不过,CONTROL 寄存器是主动修改 , 这里的LR 值是系统自己判断的 , 判断依据如下:

使用特权

评论回复
67
米多0036|  楼主 | 2023-9-15 15:49 | 只看该作者
主程序在线程模式下 , 并且使用MSP 被中断 , 则 EXC_RETURN 的 值为 0xFFFFFFF9 (裸机系统为这个)

使用特权

评论回复
68
米多0036|  楼主 | 2023-9-15 15:49 | 只看该作者
主程序在线程模式下 , 并且使用PSP 被中断 , 则 EXC_RETURN 的 值为 0xFFFFFFFD (RTOS系统为这个)

使用特权

评论回复
69
米多0036|  楼主 | 2023-9-15 15:49 | 只看该作者

使用特权

评论回复
70
米多0036|  楼主 | 2023-9-15 15:50 | 只看该作者
异常返回时,只需要令PC 等于 LR 即可, 系统会自动切换模式 和 选择栈指针 , 并且从栈中恢复先前寄存器的值(包括PC重新回到打断的地方)。可以看到,中断始终使用的是主堆栈 , 因此中断的嵌套深度,局部变量,函数调用都会消耗主堆栈,在RTOS设计时,不仅需要考虑每个线程的栈空间大小,还需要考虑主堆栈的空间大小。

使用特权

评论回复
71
米多0036|  楼主 | 2023-9-15 15:50 | 只看该作者
调度器
为什么需要调度器
实时性
在顺序执行结构中,常见的任务处理模式如下:

使用特权

评论回复
72
米多0036|  楼主 | 2023-9-15 15:50 | 只看该作者
int main(void)
{
    while(1)
    {
        do_something1();
        do_something2();
        do_something3();
    }
}
void ISR(void)
{
    do_something4();
}

使用特权

评论回复
73
米多0036|  楼主 | 2023-9-15 15:51 | 只看该作者
可以发现,每一个任务的执行都受其余任务执行时间的影响,虽然有任务放在中断中执行 , 但其余非中断任务实时性得不到保证。为了提高实时性,基于一个事实,一般任务都有一个执行周期,比如按键检测任务,可以5ms扫描一次,显示任务可以50ms 刷新一次 ,传感器采集任务可以500ms采集一次。

使用特权

评论回复
74
米多0036|  楼主 | 2023-9-15 15:51 | 只看该作者
int main(void)
{
    while(1)
    {
        if(Timer5ms_flag){
                do_something1();
        }if(Timer10ms_flag){
            do_something2();
        }if(Timer8ms_flag){
            do_something3();
        }
    }
}
void ISR(void)
{
    do_something4();
}

使用特权

评论回复
75
米多0036|  楼主 | 2023-9-15 15:51 | 只看该作者
按照上述方式 , 由于将各个任务指定执行周期 , 在同一时刻让任务就绪错开 , 这样单个任务的实时性得到了提高。

按上述方法,实时性有一定的提高,多数情况下不会出现多个任务就绪的情况,但是极端情况下也会出现,如某个任务执行时间过长或执行时间不确定,破环了上图的任务错开时间运行的平衡 , 或者周期互为公约数,且启动时时间未使用质数错开 ,最终会出现类似 “共振”的现象,隔一段时间多个任务同时就绪。

使用特权

评论回复
76
米多0036|  楼主 | 2023-9-15 15:51 | 只看该作者
多个任务同时就绪 , 我们更期望的时并行处理,而不是串行,如上图所示。但是MCU为单核,无法并行处理,为了实现类并行处理,时间片轮转调度由此而生。

使用特权

评论回复
77
米多0036|  楼主 | 2023-9-15 15:52 | 只看该作者
时间片即任务允许执行的最长时间,时间到了就需要切换到下一个任务执行,时间片轮转需要考虑的是任务执行时间到了,如何切换到下一个任务,如何保存当前任务执行环境,如何恢复下一任务的执行环境等,当前主流的RTOS都支持时间片调度。虽然任务切换会消耗一定的时间,但是任务本身在时间片足够小时,几乎可以任务是并行执行。

使用特权

评论回复
78
米多0036|  楼主 | 2023-9-15 15:52 | 只看该作者
解耦合
调度器能提供统一的接口,管理任务执行时序,避免了各种就绪标志位胡乱分布耦合。后文中将介绍几种常见的调度器实现,可以发现基于调度器框架开发的优势

使用特权

评论回复
79
米多0036|  楼主 | 2023-9-15 15:52 | 只看该作者
裸机下的调度器
使用软件定时器实现调度
/***************************************************************************//**
* [url=home.php?mod=space&uid=247401]@brief[/url]         设置ctimer定时器
* [url=home.php?mod=space&uid=1543424]@Details[/url]      
*
* @param[in]     period    运行周期
* @param[in]     counter   运行次数
* @param[in]     p_func    函数指针
* @param[in]     p_arg     函数
* [url=home.php?mod=space&uid=266161]@return[/url]        分配的定时器指针,如果没有定时器分配,返回NULL
*
* [url=home.php?mod=space&uid=536309]@NOTE[/url]         
* [url=home.php?mod=space&uid=8537]@see[/url]           
* [url=home.php?mod=space&uid=157211]@warning[/url]      
******************************************************************************/
struct ctimer* ctimer_set(UINT32 ulPeriod, UINT32 ulCount, void (*pfFunc)(void* p), void* pArg);
void ctimer_mainloop(void);
void task1_process(void* pArg)
{
   
}
void task2_process(void* pArg)
{
   
}
int main(void)
{
    ctimer_set( 5,  0  , task1_process, NULL);
    ctimer_set( 20,  0 , task1_process, NULL);
    while(1)
    {
        ctimer_mainloop();
    }
}

使用特权

评论回复
80
米多0036|  楼主 | 2023-9-15 15:53 | 只看该作者
使用事件驱动的调度器
QP状态机框架: https://www.state-machine.com/
/********************************************************************
upgrade fsm:

       <--------------------------------------------------
      /                                            \      \
    idle -----> info ----->transfer ------------> finsh    \
                  \            \                            \
                   \            \                            \
                    --------------------------------------->error
                    
*********************************************************************/
R_state_t ecu_upgrade_state_idle(fsm_t* me, uint32_t evt)
{
    switch(evt)
    {
        case SYS_ENTER_EVT:
        {
            log_d("enter idle state");
            memset(&upgrade_info , 0 , sizeof(struct ecu_upgrade_info));
            return S_HANDLE();
        }
        case ECU_UPGRADE_REQUEST_EVT:
        {
            return S_TRAN(ecu_upgrade_state_transfer);
            //return S_TRAN(ecu_upgrade_state_info);
        }
        default:
        {
             return S_IGNORE();
        }
    }
}
R_state_t ecu_upgrade_state_info(fsm_t* me, uint32_t evt)
{
    switch(evt)
    {
        case SYS_ENTER_EVT:
        {
            /*step1:   session control*/
            /*step2:   security_access*/
            /*step3:   file_download request*/
            /*period:  tester present*/
            fsm_set_timeout_evt(me , 3000 , SYS_TIMEOUT_EVT);
            return S_HANDLE();
        }
        case SYS_TIMEOUT_EVT:
        {
            /*period:  tester present*/
            fsm_set_timeout_evt(me , 3000 , SYS_TIMEOUT_EVT);
            return S_HANDLE();
        }   
        default:
        {
            return S_IGNORE();
        }
    }
}
...

使用特权

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

本版积分规则