电子产业一站式赋能平台

PCB联盟网

搜索
查看: 51|回复: 0
收起左侧

FreeRTOS中Systick的问题

[复制链接]

395

主题

395

帖子

2304

积分

三级会员

Rank: 3Rank: 3

积分
2304
发表于 2023-12-14 21:00:00 | 显示全部楼层 |阅读模式

lww2zamndqc6401883227.png

lww2zamndqc6401883227.png

4 a3 `9 S* ], \0 d( T" @+ c. r  D8 E- o
在Cortex-M内核中,系统节拍由Systick时钟提供,当配置好系统滴答时钟后,每次时钟中断就会触发中断处理数xPortSysTickHandler()。* t. D: J! _( r/ ?" P6 @2 N: S
  • void xPortSysTickHandler( void ){    /* The SysTick runs at the lowest interrupt priority, so when this interrupt     * executes all interrupts must be unmasked.  There is therefore no need to     * save and then restore the interrupt mask value as its value is already     * known - therefore the slightly faster vPortRaiseBASEPRI() function is used     * in place of portSET_INTERRUPT_MASK_FROM_ISR(). */    vPortRaiseBASEPRI();//屏蔽归属FreeRTOS的中断优先级    {        /* Increment the RTOS tick. */        if( xTaskIncrementTick() != pdFALSE )//时钟计数处理        {            /* A context switch is required.  Context switching is performed in             * the PendSV interrupt.  Pend the PendSV interrupt. */            portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;//如果需要切换上下文操作,PendSV标记置位        }    }
    ) O# \" D2 Q, t( c$ G4 C    vPortClearBASEPRIFromISR();}
    % v. H. ~3 X) x- f: g$ @$ \这部分主要是依靠 xTaskIncrementTick(),来判断任务切换是否在此次系统时钟中断时被需要。如果是,则PendSV标记置位,等待触发PendSV中断。来看看 xTaskIncrementTick()。
  • BaseType_t xTaskIncrementTick( void ){    TCB_t * pxTCB;    TickType_t xItemValue;    BaseType_t xSwitchRequired = pdFALSE;& ?. ~( V2 k! E. b2 S6 F
        /* Called by the portable layer each time a tick interrupt occurs.     * Increments the tick then checks to see if the new tick value will cause any     * tasks to be unblocked. */    traceTASK_INCREMENT_TICK( xTickCount );
    2 h2 M0 [/ N& Y! M    if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) //调度是否被挂起,默认为否    {        /* Minor optimisation.  The tick count cannot change in this         * block. */        const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1;+ n$ Y6 w. G3 ~( L: l& F4 v$ w" V
            /* Increment the RTOS tick, switching the delayed and overflowed         * delayed lists if it wraps to 0. */        xTickCount = xConstTickCount;
    0 g: Z, g% s; {$ `) L" a6 U        if( xConstTickCount == ( TickType_t ) 0U ) /*lint !e774 'if' does not always evaluate to false as it is looking for an overflow. 如果xConstTickCount为0,说明溢出了*/        {            taskSWITCH_DELAYED_LISTS();/*切换延迟列表*/        }        else        {            mtCOVERAGE_TEST_MARKER();        }
    3 v5 i1 k6 ~! [, Q1 `        /* See if this tick has made a timeout expire.  Tasks are stored in         * the  queue in the order of their wake time - meaning once one task         * has been found whose block time has not expired there is no need to         * look any further down the list. */        if( xConstTickCount >= xNextTaskUnblockTime )        {            for( ; ; )            {                if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )                {                    /* The delayed list is empty.  Set xNextTaskUnblockTime                     * to the maximum possible value so it is extremely                     * unlikely that the                     * if( xTickCount >= xNextTaskUnblockTime ) test will pass                     * next time through. */                    xNextTaskUnblockTime = portMAX_DELAY; /*lint !e961 MISRA exception as the casts are only redundant for some ports. */                    break;                }                else                {                    /* The delayed list is not empty, get the value of the                     * item at the head of the delayed list.  This is the time                     * at which the task at the head of the delayed list must                     * be removed from the Blocked state. */                    pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too.  Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */                    xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );
    3 @+ V, e3 p% b' c; a. j关键问题是,这个函数使用到了 pxDelayedTaskList, 这定义在本文件
    # D4 t$ q. X9 ?. v4 r
  • PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList;
    6 M  j* z( h3 l# b4 E  b' \该变量初始化为0,该变量正常初始化位置在创建 Task 对象等的函数中, 也就是说,如果在Tick中断到来时,如果还没有任务被创建,就会导致不可预期的结果,中断服务函数会使用这个野指针,执行任务切换。这会导致触发栈溢出钩子函数,或者是直接Hardfault。有些硬件初始化需要借助delay功能,不得不在初始化之前配置SysTick。而又不希望在硬件初始化阶段触发这个Bug。所以在配置SysTick之前,先创建一个初始化任务,初始化 pxDelayedTaskList 这个指针,在初始化任务里配置SysTick,和其他初始化,这样能够避免此类问题。或者是在配置SysTick的时候屏蔽中断,一切准备就绪后,开启中断。执行vTaskStartScheduler()默认创建一个空闲任务。==========
    ( o* E/ r0 e: J往期回顾:
    1 Z3 ^1 W0 B4 `3 @4 z3 Istrlen和sizeof的异同/ O1 f7 S. Z/ B  b# L! N
    STM32CubeMx的串口DMA收发数据; b1 r! y8 a- w* @" t  p# M- X
    好看的PCB也是产品的优势
    $ L; G* a1 M# c/ N1 K+ |STM32的DMA的五大问题
    . Z' U" h" Q) X5 l, R单片机的各个通信协议的波特率
    5 ?4 A+ V. n, Y% e  Q3 G==========5 D6 e' c5 U2 h5 W3 ~! G$ z
    原文链接:点击阅读原文
    ; }: ~& Z1 [0 m; J平台:博客园# N- {/ A: h& Q$ z1 S
    作者:ficusdxx2 N. p4 A2 U6 q& Q, |: M

    2vvnohsyebn6401883327.png

    2vvnohsyebn6401883327.png

    4 u6 H2 [% R  Y* [9 s! M% x% U, ^* W1 F2 C6 n3 D. Q: ]( t

    5uzxtnehbn26401883428.png

    5uzxtnehbn26401883428.png

    ! t# U3 j& e+ c4 T- c% ?+ ?# m) P' W. _) c9 o

    fqwb1bevkbq6401883528.png

    fqwb1bevkbq6401883528.png
  • 回复

    使用道具 举报

    发表回复

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

    本版积分规则


    联系客服 关注微信 下载APP 返回顶部 返回列表