0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

rt-thread 优化系列(0) SysTick 优化分析

出出 来源:出出 作者:出出 2022-06-21 08:55 次阅读

前言

论坛里有人提出了一个疑问,说 STM32 系列 bsp 在初始化系统时钟的过程中使用到了 tick ,而 tick 需要初始化并使能 SysTick 中断。但是呢,SysTick 中断中有 rtt 的 tick 以及硬定时器检测,以及可能存在的系统任务调度。初始化时钟是极其早期必须完成的工作,这个时候别说系统了,其它外围设备也没有被初始化。由此产生一个问题,极其早期使用了比较后期的资源,同时,因为需要使用 SysTick 中断而简单粗暴地使用 `__set_PRIMASK` 使能了总中断!!!这是万万不可取的。

有人发现了这一矛盾之处,在[论坛](https://club.rt-thread.org/ask/article/2857.html) 里和 gitee 的 [issue]( https://gitee.com/rtthread/rt-thread/pulls/246) 里也是各执己见,热闹非凡。

下面我讲讲我的处理方案。

理论前提

1. rtt 标榜的是实时操作系统,对于系统中频繁执行的代码需要严格审查,保证没有一行多余代码。(非实时操作系统也不希望自己多数时候空跑一条无用的代码)
2. 中断是个很棘手的东西,在不确定开中断会引起什么后果的时候,坚决不能开中断。
3. rtt 系统启动是有它自己的流程的。分阶段的,任何资源的初始化都有个阶段以及先后顺序。
4. 板级初始化,如非必要,如果可以延迟尽量延迟,先保证 rtt 内核调度运行起来。

基于以上几点,我的修改方案如下。

预初始化 SysTick

上电后,MCU 默认使用的内部晶振,时钟也是默认值,这个时候使用默认值初始化 SysTick,但是**不开启中断**!!!
在 STM32 的 bsp 里,因为有如下调用关系 `rt_hw_board_init` -> `HAL_Init` -> `HAL_InitTick`。
`HAL_InitTick` 在 hal 里是弱实现,'drv_common.c' 重新实现并且是个空函数,这里可以借用一下。

/* 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;
}

初始化系统时钟

第二步配置时钟频率,切换内外时钟或者倍频等等操作。中间如果有延时等待的需求,可以使用 SysTick 实现 udelay 短延时,进而通过 while 循环累积达到等待超时的效果。
首先,在 'drv_common.c' 文件里添加 `HAL_uDelay` 微秒延时实现,其实就是调用 `rt_hw_us_delay` 。
然后,在 'stm32xxx_hal_conf.h' 头文件末尾添加一个通用宏定义:

#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 是等待超时时间。
因为 udelay 了 10us ,所以整体的精度就是 10us ,时间损失可以控制在 10us 内。

涉及到的函数有 `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);

其它地方如法炮制,只是把等待条件和等待时间添加到宏函数里。

rt_hw_board_init 调整

调整后的代码如下:
1. 去掉开启关闭全局中断操作;
2. 提前控制台串口初始化;
3. 堆栈的初始化并不需要那么着急,但是必须在 rt_components_board_init 之前;
4. gpio 初始化也在 rt_components_board_init 之前。

其中 2-4 的调整是调试需求控制台。
最终结果如下。

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` 函数中使能 SysTick 中断的操作。添加使能 SysTick 中断函数。

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 异常响应

目前,`SysTick_Handler` 函数就下面这么清爽,不需要考虑并行增加 hal 的tick值。

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

   /* leave interrupt */
   rt_interrupt_leave();
}

运行测试

修改后系统启动正常,运行正常。
RCC 初始化过程如果失败,等待超时也能正常超时返回。
经过初步验证,不使用 SysTick 前提下初始化配置硬件的可行性还是有的。

其它可行性

这次尝试仅限于最小范围,仅仅考虑了系统时钟配置过程,未考虑其它板级外设配置过程。考虑到 hal 是比较庞大的,每次 hal 升级都把所有的 hal 文件修改一遍工作量也是不小的。这样也不方便。如果降低修改范围,只修改 RCC 相关部分,其它外设配置仍然想正常使用 SysTick ,有一种方法就是继续提前系统调度的启动时间点。
鉴于此,系统启动流程大致如下
1. 配置系统时钟,同时配置 SysTick(同前);
2. 初始化 rtt 系统调度器;
3. 初始化 idle 线程;
4. 启动 SysTick 中断;
5. 启动 idle 线程并启动 rtt 系统调度;
6. 由 idle 线程初始化调试串口;
7. 由 idle 线程初始化内存堆;
8. 由 idle 线程调用执行 `rt_components_board_init` 初始化板级外设配置;
9. 由 idle 线程创建 main 和 soft timer 线程。
10. main 线程进行组件初始化配置,以及创建其它应用线程。

由此,可以做到以下几点:
1. 保障所有操作都在是线程中进行的。省去很多 `if (rt_thread_self() != RT_NULL)` 的操作;
2. 保障中断可以及早放心打开;
3. 明确启动流程,确定启动流程每一阶段的工作重点以及必须完成的任务。

缺点,因 idle 线程工作量变多,idle 线程栈可能会比较大。一个空闲线程占用过多的内存也是浪费。另外,多核处理器的 idle 线程数量和核心数量一样,由哪个 idle 线程完成上面的工作也需要做个抉择。

> 本优化系列所有提到的更改已经提交到 gitee ,欢迎大家测试
https://gitee.com/thewon/rt_thread_repo

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • STM32
    +关注

    关注

    2239

    文章

    10671

    浏览量

    348732
  • Systick
    +关注

    关注

    0

    文章

    62

    浏览量

    12950
  • RT-Thread
    +关注

    关注

    31

    文章

    1148

    浏览量

    38867
收藏 人收藏

    评论

    相关推荐

    RT-Thread记录(二、RT-Thread内核启动流程)

    在前面我们RT-Thread Studio工程基础之上讲一讲RT-Thread内核启动流程.
    的头像 发表于 06-20 00:30 4409次阅读
    <b class='flag-5'>RT-Thread</b>记录(二、<b class='flag-5'>RT-Thread</b>内核启动流程)

    RT-Thread Studio怎么为单个c文件打开编译器优化

    RT-Thread Studio怎么为单个c文件打开编译器优化
    发表于 02-19 06:45

    SystemView如何在RT-Thread上对系统进行调试分析

    本文主要介绍 SystemView可视化分析工具,以及如何在 RT-Thread 上使用它对系统进行调试分析
    发表于 03-30 07:39

    【原创精选】RT-Thread征文精选技术文章合集

    优化系列(零) SysTick 优化分析rt-thread 优化
    发表于 07-26 14:56

    RT-Thread Studio Release发布如何优化编译大小呢?

    保存在外部flash映射后能够直接跳转运行吗?如果不能是不是需要变更链接文件这些,RTT Studio中如何操作?RT-Thread Studio Release发布如何优化编译大小,随便加点东西就很大了。
    发表于 03-06 10:15

    RT-Thread编程指南

    RT-Thread编程指南——RT-Thread开发组(2015-03-31)。RT-Thread做为国内有较大影响力的开源实时操作系统,本文是RT-Thread实时操作系统的编程指南
    发表于 11-26 16:06 0次下载

    RT-Thread开发,如何有效学习RT-Thread的五个步骤

    RT-Thread推出RT-Thread Inside战略开放RT-Thread开发平台授权合作,与硬件十万个为什么合作首次推出第一款RT-Inside的开发板——iBox物联网开发套
    的头像 发表于 09-25 09:55 3.4w次阅读
    <b class='flag-5'>RT-Thread</b>开发,如何有效学习<b class='flag-5'>RT-Thread</b>的五个步骤

    RT-Thread开源作品秀】基于RT-Thread的星务平台研究

    本作品为了验证星务软件在RT-Thread系统运行的可行性,底层是否能够驱动星务软件,同时扩展RT-Thread应用范围。ART-Pi作为卫星下位机,...
    发表于 01-25 18:26 5次下载
    【<b class='flag-5'>RT-Thread</b>开源作品秀】基于<b class='flag-5'>RT-Thread</b>的星务平台研究

    RT-Thread新版入门系列教程18讲

    作为一名 RTOS 的初学者,也许你对 RT-Thread 还比较陌生。然而,随着你的深入接触,你会逐渐发现 RT-Thread 的魅力和它相较于其他同类...
    发表于 01-25 20:00 0次下载
    <b class='flag-5'>RT-Thread</b>新版入门<b class='flag-5'>系列</b>教程18讲

    rt-thread 优化系列(六)启动流程重构

    去年此时,笔者刚接触 rt-thread 的时候,被它的初始化过程深深折服了。第一次打开一个 rt-thread 的项目,竟然没找到多线程在哪儿初始化的,"main" 函数里没有!
    的头像 发表于 07-04 15:30 1334次阅读
    <b class='flag-5'>rt-thread</b> <b class='flag-5'>优化</b><b class='flag-5'>系列</b>(六)启动流程重构

    RT-Thread学习笔记 RT-Thread的架构概述

    RT-Thread 简介 作为一名 RTOS 的初学者,也许你对 RT-Thread 还比较陌生。然而,随着你的深入接触,你会逐渐发现 RT-Thread 的魅力和它相较于其他同类型 RTOS
    的头像 发表于 07-09 11:27 3991次阅读
    <b class='flag-5'>RT-Thread</b>学习笔记 <b class='flag-5'>RT-Thread</b>的架构概述

    RT-Thread已经全面支持极海APM32F1系列MCU

    近日,RT-Thread 和其高级会员合作伙伴极海半导体宣布:正式完成APM32F4系列MCU的RT-Thread 物联网操作系统适配及RT-Thread Studio IDE的支持。
    发表于 08-30 09:45 983次阅读

    RT-Thread文档_RT-Thread 简介

    RT-Thread文档_RT-Thread 简介
    发表于 02-22 18:22 5次下载
    <b class='flag-5'>RT-Thread</b>文档_<b class='flag-5'>RT-Thread</b> 简介

    RT-Thread文档_RT-Thread SMP 介绍与移植

    RT-Thread文档_RT-Thread SMP 介绍与移植
    发表于 02-22 18:31 7次下载
    <b class='flag-5'>RT-Thread</b>文档_<b class='flag-5'>RT-Thread</b> SMP 介绍与移植

    基于RT-Thread Studio学习

    前期准备:从官网下载 RT-Thread Studio,弄个账号登陆,开启rt-thread学习之旅。
    的头像 发表于 05-15 11:00 2580次阅读
    基于<b class='flag-5'>RT-Thread</b> Studio学习