0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

rt-thread 優(yōu)化系列(0) SysTick 優(yōu)化分析

出出 ? 來(lái)源:出出 ? 作者:出出 ? 2022-06-21 08:55 ? 次閱讀

前言

論壇里有人提出了一個(gè)疑問(wèn),說(shuō) STM32 系列 bsp 在初始化系統(tǒng)時(shí)鐘的過(guò)程中使用到了 tick ,而 tick 需要初始化并使能 SysTick 中斷。但是呢,SysTick 中斷中有 rtt 的 tick 以及硬定時(shí)器檢測(cè),以及可能存在的系統(tǒng)任務(wù)調(diào)度。初始化時(shí)鐘是極其早期必須完成的工作,這個(gè)時(shí)候別說(shuō)系統(tǒng)了,其它外圍設(shè)備也沒(méi)有被初始化。由此產(chǎn)生一個(gè)問(wèn)題,極其早期使用了比較后期的資源,同時(shí),因?yàn)樾枰褂?SysTick 中斷而簡(jiǎn)單粗暴地使用 `__set_PRIMASK` 使能了總中斷!??!這是萬(wàn)萬(wàn)不可取的。

有人發(fā)現(xiàn)了這一矛盾之處,在[論壇](https://club.rt-thread.org/ask/article/2857.html) 里和 gitee 的 [issue]( https://gitee.com/rtthread/rt-thread/pulls/246) 里也是各執(zhí)己見(jiàn),熱鬧非凡。

下面我講講我的處理方案。

理論前提

1. rtt 標(biāo)榜的是實(shí)時(shí)操作系統(tǒng),對(duì)于系統(tǒng)中頻繁執(zhí)行的代碼需要嚴(yán)格審查,保證沒(méi)有一行多余代碼。(非實(shí)時(shí)操作系統(tǒng)也不希望自己多數(shù)時(shí)候空跑一條無(wú)用的代碼)
2. 中斷是個(gè)很棘手的東西,在不確定開(kāi)中斷會(huì)引起什么后果的時(shí)候,堅(jiān)決不能開(kāi)中斷。
3. rtt 系統(tǒng)啟動(dòng)是有它自己的流程的。分階段的,任何資源的初始化都有個(gè)階段以及先后順序。
4. 板級(jí)初始化,如非必要,如果可以延遲盡量延遲,先保證 rtt 內(nèi)核調(diào)度運(yùn)行起來(lái)。

基于以上幾點(diǎn),我的修改方案如下。

預(yù)初始化 SysTick

上電后,MCU 默認(rèn)使用的內(nèi)部晶振,時(shí)鐘也是默認(rèn)值,這個(gè)時(shí)候使用默認(rèn)值初始化 SysTick,但是**不開(kāi)啟中斷**!?。?br /> 在 STM32 的 bsp 里,因?yàn)橛腥缦抡{(diào)用關(guān)系 `rt_hw_board_init` -> `HAL_Init` -> `HAL_InitTick`。
`HAL_InitTick` 在 hal 里是弱實(shí)現(xiàn),'drv_common.c' 重新實(shí)現(xiàn)并且是個(gè)空函數(shù),這里可以借用一下。

/* re-implement tick interface for STM32 HAL */
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
   HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / RT_TICK_PER_SECOND);
   /* Return function status */
   return HAL_OK;
}

初始化系統(tǒng)時(shí)鐘

第二步配置時(shí)鐘頻率,切換內(nèi)外時(shí)鐘或者倍頻等等操作。中間如果有延時(shí)等待的需求,可以使用 SysTick 實(shí)現(xiàn) udelay 短延時(shí),進(jìn)而通過(guò) while 循環(huán)累積達(dá)到等待超時(shí)的效果。
首先,在 'drv_common.c' 文件里添加 `HAL_uDelay` 微秒延時(shí)實(shí)現(xiàn),其實(shí)就是調(diào)用 `rt_hw_us_delay` 。
然后,在 'stm32xxx_hal_conf.h' 頭文件末尾添加一個(gè)通用宏定義:

#define HAL_WAITFOR_CONDITION(condition, ms) do { \
 uint32_t cnt = 0; \
 while((condition)) { \
   if (cnt > (ms) * 1000 / 10) \
   { \
       return HAL_TIMEOUT; \
   } \
   HAL_uDelay(10); \
   cnt++; \
 }\
} while(0)

其中,condition 是等待條件,ms 是等待超時(shí)時(shí)間。
因?yàn)?udelay 了 10us ,所以整體的精度就是 10us ,時(shí)間損失可以控制在 10us 內(nèi)。

涉及到的函數(shù)有 `HAL_RCC_OscConfig` `HAL_PWREx_EnableOverDrive` `HAL_RCC_ClockConfig` `HAL_RCCEx_PeriphCLKConfig` 等。
其中一處修改前后

       /* Get Start Tick */
       tickstart = HAL_GetTick();
       /* Wait till HSE is ready */
       while(__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) == RESET)
       {
         if((HAL_GetTick() - tickstart ) > HSE_TIMEOUT_VALUE)
         {
           return HAL_TIMEOUT;
         }
       }

       /* Wait till HSE is ready */
       HAL_WAITFOR_CONDITION((__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) == RESET), HSE_TIMEOUT_VALUE);

其它地方如法炮制,只是把等待條件和等待時(shí)間添加到宏函數(shù)里。

rt_hw_board_init 調(diào)整

調(diào)整后的代碼如下:
1. 去掉開(kāi)啟關(guān)閉全局中斷操作;
2. 提前控制臺(tái)串口初始化;
3. 堆棧的初始化并不需要那么著急,但是必須在 rt_components_board_init 之前;
4. gpio 初始化也在 rt_components_board_init 之前。

其中 2-4 的調(diào)整是調(diào)試需求控制臺(tái)。
最終結(jié)果如下。

RT_WEAK void rt_hw_board_init()
{
#ifdef SCB_EnableICache
   /* Enable I-Cache---------------------------------------------------------*/
   SCB_EnableICache();
#endif
#ifdef SCB_EnableDCache
   /* Enable D-Cache---------------------------------------------------------*/
   SCB_EnableDCache();
#endif

   /* HAL_Init() function is called at the beginning of the program */
   HAL_Init();

   /* System clock initialization */
   SystemClock_Config();

   rt_hw_systick_init();

   /* USART driver initialization is open by default */
#ifdef RT_USING_SERIAL
   rt_hw_usart_init();
#endif

   /* Set the shell console output device */
#ifdef RT_USING_CONSOLE
   rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#endif

   /* Pin driver initialization is open by default */
#ifdef RT_USING_PIN
   rt_hw_pin_init();
#endif

   /* Heap initialization */
#if defined(RT_USING_HEAP)
   rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END);
#endif

   /* Board underlying hardware initialization */
#ifdef RT_USING_COMPONENTS_INIT
   rt_components_board_init();
#endif
}

修改 `rt_hw_systick_init`

去掉 `rt_hw_systick_init` 函數(shù)中使能 SysTick 中斷的操作。添加使能 SysTick 中斷函數(shù)。

void rt_hw_systick_irq_enable(void)
{
   HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

修改 `rtthread_startup`

添加使能 SysTick 中斷處理。

   rt_hw_systick_irq_enable();
      /* start scheduler */
   rt_system_scheduler_start();

SysTick_Handler 異常響應(yīng)

目前,`SysTick_Handler` 函數(shù)就下面這么清爽,不需要考慮并行增加 hal 的tick值。

void SysTick_Handler(void)
{
   /* enter interrupt */
   rt_interrupt_enter();
      rt_tick_increase();

   /* leave interrupt */
   rt_interrupt_leave();
}

運(yùn)行測(cè)試

修改后系統(tǒng)啟動(dòng)正常,運(yùn)行正常。
RCC 初始化過(guò)程如果失敗,等待超時(shí)也能正常超時(shí)返回。
經(jīng)過(guò)初步驗(yàn)證,不使用 SysTick 前提下初始化配置硬件的可行性還是有的。

其它可行性

這次嘗試僅限于最小范圍,僅僅考慮了系統(tǒng)時(shí)鐘配置過(guò)程,未考慮其它板級(jí)外設(shè)配置過(guò)程。考慮到 hal 是比較龐大的,每次 hal 升級(jí)都把所有的 hal 文件修改一遍工作量也是不小的。這樣也不方便。如果降低修改范圍,只修改 RCC 相關(guān)部分,其它外設(shè)配置仍然想正常使用 SysTick ,有一種方法就是繼續(xù)提前系統(tǒng)調(diào)度的啟動(dòng)時(shí)間點(diǎn)。
鑒于此,系統(tǒng)啟動(dòng)流程大致如下
1. 配置系統(tǒng)時(shí)鐘,同時(shí)配置 SysTick(同前);
2. 初始化 rtt 系統(tǒng)調(diào)度器;
3. 初始化 idle 線程;
4. 啟動(dòng) SysTick 中斷;
5. 啟動(dòng) idle 線程并啟動(dòng) rtt 系統(tǒng)調(diào)度;
6. 由 idle 線程初始化調(diào)試串口;
7. 由 idle 線程初始化內(nèi)存堆;
8. 由 idle 線程調(diào)用執(zhí)行 `rt_components_board_init` 初始化板級(jí)外設(shè)配置;
9. 由 idle 線程創(chuàng)建 main 和 soft timer 線程。
10. main 線程進(jìn)行組件初始化配置,以及創(chuàng)建其它應(yīng)用線程。

由此,可以做到以下幾點(diǎn):
1. 保障所有操作都在是線程中進(jìn)行的。省去很多 `if (rt_thread_self() != RT_NULL)` 的操作;
2. 保障中斷可以及早放心打開(kāi);
3. 明確啟動(dòng)流程,確定啟動(dòng)流程每一階段的工作重點(diǎn)以及必須完成的任務(wù)。

缺點(diǎn),因 idle 線程工作量變多,idle 線程??赡軙?huì)比較大。一個(gè)空閑線程占用過(guò)多的內(nèi)存也是浪費(fèi)。另外,多核處理器的 idle 線程數(shù)量和核心數(shù)量一樣,由哪個(gè) idle 線程完成上面的工作也需要做個(gè)抉擇。

> 本優(yōu)化系列所有提到的更改已經(jīng)提交到 gitee ,歡迎大家測(cè)試
https://gitee.com/thewon/rt_thread_repo

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • STM32
    +關(guān)注

    關(guān)注

    2264

    文章

    10858

    瀏覽量

    354395
  • Systick
    +關(guān)注

    關(guān)注

    0

    文章

    62

    瀏覽量

    13031
  • RT-Thread
    +關(guān)注

    關(guān)注

    31

    文章

    1265

    瀏覽量

    39853
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    RT-Thread記錄(二、RT-Thread內(nèi)核啟動(dòng)流程)

    在前面我們RT-Thread Studio工程基礎(chǔ)之上講一講RT-Thread內(nèi)核啟動(dòng)流程.
    的頭像 發(fā)表于 06-20 00:30 ?4940次閱讀
    <b class='flag-5'>RT-Thread</b>記錄(二、<b class='flag-5'>RT-Thread</b>內(nèi)核啟動(dòng)流程)

    【原創(chuàng)精選】RT-Thread征文精選技術(shù)文章合集

    優(yōu)化系列(零) SysTick 優(yōu)化分析rt-thread 優(yōu)化
    發(fā)表于 07-26 14:56

    RT-Thread編程指南

    RT-Thread編程指南——RT-Thread開(kāi)發(fā)組(2015-03-31)。RT-Thread做為國(guó)內(nèi)有較大影響力的開(kāi)源實(shí)時(shí)操作系統(tǒng),本文是RT-Thread實(shí)時(shí)操作系統(tǒng)的編程指南
    發(fā)表于 11-26 16:06 ?0次下載

    RT-Thread用戶(hù)手冊(cè)

    RT-Thread用戶(hù)手冊(cè)——本書(shū)是RT-Thread的編程手冊(cè),用于指導(dǎo)在RT-Thread實(shí)時(shí)操作系統(tǒng)環(huán)境下如何進(jìn)行編 程。
    發(fā)表于 11-26 16:16 ?0次下載

    RT-Thread全球技術(shù)大會(huì):螢石研發(fā)團(tuán)隊(duì)使用RT-Thread的技術(shù)挑戰(zhàn)

    RT-Thread全球技術(shù)大會(huì):研發(fā)團(tuán)隊(duì)使用RT-Thread的技術(shù)挑戰(zhàn) ? ? ? ? 審核編輯:彭靜
    的頭像 發(fā)表于 05-27 11:36 ?1274次閱讀
    <b class='flag-5'>RT-Thread</b>全球技術(shù)大會(huì):螢石研發(fā)團(tuán)隊(duì)使用<b class='flag-5'>RT-Thread</b>的技術(shù)挑戰(zhàn)

    RT-Thread全球技術(shù)大會(huì):Kconfig在RT-Thread中的工作機(jī)制

    RT-Thread全球技術(shù)大會(huì):Kconfig在RT-Thread中的工作機(jī)制 ? ? ? ? ? ? ? 審核編輯:彭靜
    的頭像 發(fā)表于 05-27 14:49 ?1493次閱讀
    <b class='flag-5'>RT-Thread</b>全球技術(shù)大會(huì):Kconfig在<b class='flag-5'>RT-Thread</b>中的工作機(jī)制

    RT-Thread全球技術(shù)大會(huì):在RT-Thread上編寫(xiě)測(cè)試用例

    RT-Thread全球技術(shù)大會(huì):在RT-Thread上編寫(xiě)測(cè)試用例 ? ? ? ? ? 審核編輯:彭靜
    的頭像 發(fā)表于 05-27 16:28 ?1442次閱讀
    <b class='flag-5'>RT-Thread</b>全球技術(shù)大會(huì):在<b class='flag-5'>RT-Thread</b>上編寫(xiě)測(cè)試用例

    RT-Thread全球技術(shù)大會(huì):RT-Thread測(cè)試用例集合案例

    RT-Thread全球技術(shù)大會(huì):RT-Thread測(cè)試用例集合案例 ? ? ? ? ? 審核編輯:彭靜
    的頭像 發(fā)表于 05-27 16:34 ?2052次閱讀
    <b class='flag-5'>RT-Thread</b>全球技術(shù)大會(huì):<b class='flag-5'>RT-Thread</b>測(cè)試用例集合案例

    rt-thread 優(yōu)化系列(六)啟動(dòng)流程重構(gòu)

    去年此時(shí),筆者剛接觸 rt-thread 的時(shí)候,被它的初始化過(guò)程深深折服了。第一次打開(kāi)一個(gè) rt-thread 的項(xiàng)目,竟然沒(méi)找到多線程在哪兒初始化的,"main" 函數(shù)里沒(méi)有!
    的頭像 發(fā)表于 07-04 15:30 ?1698次閱讀
    <b class='flag-5'>rt-thread</b> <b class='flag-5'>優(yōu)化</b><b class='flag-5'>系列</b>(六)啟動(dòng)流程重構(gòu)

    RT-Thread學(xué)習(xí)筆記 RT-Thread的架構(gòu)概述

    RT-Thread 簡(jiǎn)介 作為一名 RTOS 的初學(xué)者,也許你對(duì) RT-Thread 還比較陌生。然而,隨著你的深入接觸,你會(huì)逐漸發(fā)現(xiàn) RT-Thread 的魅力和它相較于其他同類(lèi)型 RTOS
    的頭像 發(fā)表于 07-09 11:27 ?4467次閱讀
    <b class='flag-5'>RT-Thread</b>學(xué)習(xí)筆記 <b class='flag-5'>RT-Thread</b>的架構(gòu)概述

    RT-Thread文檔_RT-Thread 簡(jiǎn)介

    RT-Thread文檔_RT-Thread 簡(jiǎn)介
    發(fā)表于 02-22 18:22 ?5次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>RT-Thread</b> 簡(jiǎn)介

    RT-Thread文檔_RT-Thread 潘多拉 STM32L475 上手指南

    RT-Thread文檔_RT-Thread 潘多拉 STM32L475 上手指南
    發(fā)表于 02-22 18:23 ?9次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>RT-Thread</b> 潘多拉 STM32L475 上手指南

    RT-Thread文檔_RT-Thread SMP 介紹與移植

    RT-Thread文檔_RT-Thread SMP 介紹與移植
    發(fā)表于 02-22 18:31 ?9次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>RT-Thread</b> SMP 介紹與移植

    基于RT-Thread Studio學(xué)習(xí)

    前期準(zhǔn)備:從官網(wǎng)下載 RT-Thread Studio,弄個(gè)賬號(hào)登陸,開(kāi)啟rt-thread學(xué)習(xí)之旅。
    的頭像 發(fā)表于 05-15 11:00 ?3783次閱讀
    基于<b class='flag-5'>RT-Thread</b> Studio學(xué)習(xí)

    RT-Thread v5.0.2 發(fā)布

    RT-Thread 代碼倉(cāng)庫(kù)地址: ●? https://github.com/RT-Thread/rt-thread RT-Thread 5.0.2 版本發(fā)布日志詳情: ●? htt
    的頭像 發(fā)表于 10-10 18:45 ?1371次閱讀
    <b class='flag-5'>RT-Thread</b> v5.0.2 發(fā)布