前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >RT-Thread编程高阶用法-函数扩展之$Sub$$与$Super$$

RT-Thread编程高阶用法-函数扩展之$Sub$$与$Super$$

作者头像
杨源鑫
发布2020-06-04 10:46:31
1.1K0
发布2020-06-04 10:46:31
举报

前面移植了RT-Thread Nano,其实准确来说那不叫移植,那叫做部署,因为移植的工作官方已经帮我们做好了。

文章链接:小熊派移植RT-Thread Nano

1、引发思考-相关资料检索

在之前的文章提到过,RT-Thread已经提前在main函数以前就把跟硬件配置、系统初始化、启动调度器等相关的都做好了,所以我们后来看到的main函数非常简洁,真是让人感觉神清气爽,有继续往下写代码的欲望,如下:

main.c

int main(void)
{
    while(1)
    {
        rt_kprintf("Hello RTT_NANO\n");
        HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
        rt_thread_mdelay(500);
    }
}

那具体RT-Thread又是如何实现在main函数执行之前就把所有初始化硬件、时钟的工作都做了呢?跟随官方文档的RT-Thread代码启动流程:

跟代码,最后发现如下代码:

/* re-define main function */
int $Sub$$main(void)
{
    rtthread_startup();
    return 0;
}

/* the system main thread */
void main_thread_entry(void *parameter)
{
    extern int main(void);
    extern int $Super$$main(void);

    /* RT-Thread components initialization */
    rt_components_init();

    /* invoke system main function */
#if defined(__CC_ARM) || defined(__CLANG_ARM)
    $Super$$main(); /* for ARMCC. */
#elif defined(__ICCARM__) || defined(__GNUC__)
    main();
#endif
}

平时工作开发中没用到这样的语法,于是只能搜索文档来看看到底是如何实现的,果然在Keil帮助手册中找到了答案:

Sub和Super这两个符号来扩展了 main 函数,这使得使用Submain可以在main函数执行之前就预先执行Submain函数,所以在Submain函数里就可以完成一些基本的硬件、时钟初始化功能,做完这些工作以后,还是得跳转到main函数去执行往后逻辑的呀,这就需要通过调用

既然main函数之前能这么用,是不是换个函数也能这么用呢?这引发我的好奇,于是继续查找文档,在armlink_user_guide手册中找到:

接下来开始做实验,然后我用stm32cubeMX生成一个基本裸机工程,下载到小熊派上来验证是否正确。

2、小熊派上进行实践

2.1 基本功能配置

配置外部时钟、调试串口、调试接口以及LED

最后生成代码。

2.2 编写代码进行验证

首先添加一个串口重定向函数,后面才能使用printf

int fputc(int ch,FILE *file)
{
  return HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,1000);
}

接下来结合文档模仿RT-Thread写出以下函数:

void $Sub$$main(void)
{
  extern int main(void);
  extern int $Super$$main(void);
  //初始化HAL
  HAL_Init();
  //初始化系统时钟
  SystemClock_Config();
  //初始化GPIO
  MX_GPIO_Init();
  //初始化串口
  MX_USART1_UART_Init();
  printf("初始化已完成\n");
  //点灯
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
  //回到真正的main函数里
  $Super$$main();
}

main函数如下:

int main(void)
{
  //延时2s
  HAL_Delay(2000);
  printf("回到main函数中\n");
  while(1)
  {
    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
    HAL_Delay(500);
  }
}

将程序编译后下载到小熊派开发板中,然后打开串口调试助手可以看到:

由此可见,这是一个很有逼格的技能,以后可以在支持这种扩展符号的编译器下将这种技能应用起来,从而简化代码,接下来我们再往上面这个程序里添加功能:添加Function函数和在它之前运行的Sub

void $Sub$$Function(void)
{
  extern void Function(void);
  extern void $Super$$Function(void);
  printf("在Function函数之前调用$Sub$$Function\n");
  $Super$$Function();
}

void Function(void)
{
  printf("执行Function函数\n");
}

int main(void)
{
  //延时2s
  HAL_Delay(2000);
  printf("回到main函数中\n");
  //调用Function函数
  Function();
  while(1)
  {
    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
    HAL_Delay(500);
  }
}

然后编译后将程序下载到小熊派开发板后,通过串口调试助手看到:

至此,我们已经完全弄明白RT-Thread是如何实现在main函数执行之前就把初始化硬件、系统初始化、启动调度器等工作都完成了的基本原理。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-06-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 嵌入式云IOT技术圈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、引发思考-相关资料检索
  • 2、小熊派上进行实践
    • 2.1 基本功能配置
      • 2.2 编写代码进行验证
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档