[RT_Thread] 【RT-Thread读书笔记】9. RT-Thread 学习14-16章读后感

传媒学子   2019-5-11 15:20 楼主
14.创建线程首先,应当硬件初始化,然后再创建线程,硬件初始化是将线程中用到的硬件模块进行初始化。因为线程相当于裸机时的while循环前的初始化,例如使用ADC,先应当配置好ADC各项参数,通道等。
其次,需要就是创建线程。
一般包括:定义线程函数,定义线程栈,定义线程控制块,初始化线程,启动线程等。
本章讲解了在SARM静态内存和动态内存创建单线程和多线程task。

15.重映射串口到kt_printf函数
一般我们需要使用串口来来打印一些信息,辅助我们调试。 RT-Thread中有rt_kprintf()接口函数供我们使用,我们可以把串口、CAN、USB、以太网等输出设备映射过来,一般情况下,我们会采用串口。
rt_kprintf()函数在kservice.c中实现,是属于内核服务类的函数:
  1. /**
  2. * This function will print a formatted string on system console
  3. *
  4. * @param fmt the format
  5. */
  6. void rt_kprintf(const char *fmt, ...)
  7. {
  8.     va_list args;
  9.     rt_size_t length;
  10.     static char rt_log_buf[RT_CONSOLEBUF_SIZE];

  11.     va_start(args, fmt);
  12.     /* the return value of vsnprintf is the number of bytes that would be
  13.      * written to buffer had if the size of the buffer been sufficiently
  14.      * large excluding the terminating null byte. If the output string
  15.      * would be larger than the rt_log_buf, we have to adjust the output
  16.      * length. */
  17.     length = rt_vsnprintf(rt_log_buf, sizeof(rt_log_buf) - 1, fmt, args);
  18.     if (length > RT_CONSOLEBUF_SIZE - 1)
  19.         length = RT_CONSOLEBUF_SIZE - 1;
  20. #ifdef RT_USING_DEVICE
  21.     if (_console_device == RT_NULL)
  22.     {
  23.         rt_hw_console_output(rt_log_buf);
  24.     }
  25.     else
  26.     {
  27.         rt_uint16_t old_flag = _console_device->open_flag;

  28.         _console_device->open_flag |= RT_DEVICE_FLAG_STREAM;
  29.         rt_device_write(_console_device, 0, rt_log_buf, length);
  30.         _console_device->open_flag = old_flag;
  31.     }
  32. #else
  33.     rt_hw_console_output(rt_log_buf);
  34. #endif
  35.     va_end(args);
  36. }



如果使用设备驱动,则通过设备驱动函数将rt_log_buf缓冲区的内容输出到控制台。如果设备控制台打开失败则由rt_hw_console_output函数处理,这个函数需要用户单独实现。
本书只讲解如何将串口控制台重映射到rt_kprintf函数,rt_hw_console_output函数在board.c实现:
  1. /**
  2.   * [url=home.php?mod=space&uid=159083]@brief[/url]  重映射串口DEBUG_USARTx到rt_kprintf()函数
  3.   *   Note:DEBUG_USARTx是在bsp_usart.h中定义的宏,默认使用串口1
  4.   * @param  str:要输出到串口的字符串
  5.   * @retval 无
  6.   *
  7.   * @attention
  8.   *
  9.   */
  10. void rt_hw_console_output(const char *str)
  11. {        
  12.         /* 进入临界段 */
  13.     rt_enter_critical();

  14.         /* 直到字符串结束 */
  15.     while (*str!='\0')
  16.         {
  17.                 /* 换行 */
  18.         if (*str=='\n')
  19.                 {
  20.                         USART_SendData(DEBUG_USART, '\r');
  21.                         while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_TXE) == RESET);
  22.                 }

  23.                 USART_SendData(DEBUG_USART, *str++);                                 
  24.                 while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_TXE) == RESET);        
  25.         }        

  26.         /* 退出临界段 */
  27.     rt_exit_critical();
  28. }



16 RT-Thread的启动流程
RT-Thread采用的是先启动初始线程,然后由初始线程来创建各种应用线程,然后再关闭初始线程的方法来启动。
并且在mian函数中稍做了修改。

当你拿到一个移植好的RT-Thread工程的时候,你去看main函数,只能在main函数里面看到创建线程和启动线程的代码,硬件初始化,系统初始化,启动调度器等信息都看不到。那是因为RT-Thread拓展了main函数,在main函数之前把这些工作都做好了。
我们知道,在系统上电的时候第一个执行的是启动文件里面由汇编编写的复位函数Reset_Handler,复位函数的最后会调用C库函数__main,__main函数的主要工作是初始化系统的堆和栈,最后调用C中的main函数,从而去到C的世界。
  1. ; Reset handler
  2. Reset_Handler    PROC
  3.                  EXPORT  Reset_Handler             [WEAK]
  4.         IMPORT  SystemInit
  5.         IMPORT  __main

  6.                  LDR     R0, =SystemInit
  7.                  BLX     R0
  8.                  LDR     R0, =__main
  9.                  BX      R0
  10.                  ENDP

但当我们硬件仿真RT-Thread工程的时候,单步执行完__main之后,并不是跳转到C中的main函数,而是跳转到component.c中的$Sub$$main函数,因为RT-Thread使用编译器(这里仅讲解KEIL,IAR或者GCC稍微有点区别,但是原理是一样的)自带的$Sub$$和$Super$$这两个符号来扩展了main函数,使用$Sub$$main可以在执行main之前先执行$Sub$$main,在$Sub$$main函数中我们可以先执行一些预操作,当做完这些预操作之后最终还是要执行main函数,这个就通过调用$Super$$main来实现。当需要扩展的函数不是main的时候,只需要将main换成你要扩展的函数名即可,即$Sub$$function和$Super$$function。
就是在执行实际main函数值之前,可以执行$Sub$$main函数,然后采用$Super$$main回到main中, RT-Thread的硬件初始化是在components.c文件中的的$Sub$$main中完成的,因此实际的main函数中就没有硬件初始化相关的code,直接就开始了线程相关的操作。
硬件初始化相关code是在$Sub$$main的rtthread_startup();语句中完成的,这个语句调用startup函数:
int rtthread_startup(void)
{
    rt_hw_interrupt_disable();

    /* board level initalization
     * NOTE: please initialize heap inside board initialization.
     */
    rt_hw_board_init();

    /* show RT-Thread version */
    rt_show_version();

    /* timer system initialization */
    rt_system_timer_init();

    /* scheduler system initialization */
    rt_system_scheduler_init();

#ifdef RT_USING_SIGNALS
    /* signal system initialization */
    rt_system_signal_init();
#endif

    /* create init_thread */
    rt_application_init();

    /* timer thread initialization */
    rt_system_timer_thread_init();

    /* idle thread initialization */
    rt_thread_idle_init();

    /* start scheduler */
    rt_system_scheduler_start();

    /* never reach here */
    return 0;
}


这是main.c函数:
  1. /**
  2.   *********************************************************************
  3.   * @file    main.c
  4.   * @author  fire
  5.   * [url=home.php?mod=space&uid=252314]@version[/url] V1.0
  6.   * [url=home.php?mod=space&uid=311857]@date[/url]    2018-xx-xx
  7.   * @brief   RT-Thread 3.0 + STM32 工程模版
  8.   *********************************************************************
  9.   * @attention
  10.   *
  11.   * 实验平台:野火F429挑战者 STM32 开发板
  12.   * 论坛    :[url=http://www.firebbs.cn]http://www.firebbs.cn[/url]
  13.   * 淘宝    :[url=https://fire-stm32.taobao.com]https://fire-stm32.taobao.com[/url]
  14.   *
  15.   **********************************************************************
  16.   */

  17. /*
  18. *************************************************************************
  19. *                             包含的头文件
  20. *************************************************************************
  21. */
  22. #include "board.h"
  23. #include "rtthread.h"


  24. /*
  25. *************************************************************************
  26. *                               变量
  27. *************************************************************************
  28. */
  29. /* 定义线程控制块 */
  30. static rt_thread_t led1_thread = RT_NULL;

  31. /*
  32. *************************************************************************
  33. *                             函数声明
  34. *************************************************************************
  35. */
  36. static void led1_thread_entry(void* parameter);


  37. /*
  38. *************************************************************************
  39. *                             main 函数
  40. *************************************************************************
  41. */
  42. /**
  43.   * @brief  主函数
  44.   * @param  无
  45.   * @retval 无
  46.   */
  47. int main(void)
  48. {        
  49.     /*
  50.          * 开发板硬件初始化,RTT系统初始化已经在main函数之前完成,
  51.          * 即在component.c文件中的rtthread_startup()函数中完成了。
  52.          * 所以在main函数中,只需要创建线程和启动线程即可。
  53.          */
  54.         
  55.         led1_thread =                          /* 线程控制块指针 */
  56.     rt_thread_create( "led1",              /* 线程名字 */
  57.                       led1_thread_entry,   /* 线程入口函数 */
  58.                       RT_NULL,             /* 线程入口函数参数 */
  59.                       512,                 /* 线程栈大小 */
  60.                       3,                   /* 线程的优先级 */
  61.                       20);                 /* 线程时间片 */
  62.                   
  63.     /* 启动线程,开启调度 */
  64.    if (led1_thread != RT_NULL)
  65.         rt_thread_startup(led1_thread);
  66.     else
  67.         return -1;
  68. }

  69. /*
  70. *************************************************************************
  71. *                             线程定义
  72. *************************************************************************
  73. */

  74. static void led1_thread_entry(void* parameter)
  75. {        
  76.     while (1)
  77.     {
  78.         LED1_ON;
  79.         rt_thread_delay(500);   /* 延时500个tick */
  80.         rt_kprintf("led1_thread running,LED1_ON\r\n");
  81.         
  82.         LED1_OFF;     
  83.         rt_thread_delay(500);   /* 延时500个tick */                                 
  84.         rt_kprintf("led1_thread running,LED1_OFF\r\n");
  85.     }
  86. }

  87. /********************************END OF FILE****************************/



此内容由EEWORLD论坛网友传媒学子原创,如需转载或用于商业用途需征得作者同意并注明出处



回复评论 (2)

为什么RT-Thread的启动流程里面,“小心翼翼,十分谨慎”里面如何实现的。每创建一个线程后它都进入就绪态,系统会进行一次调度,怎么实现啊?

坚持自己的坚持,终究会有拨开云雾的一天
点赞  2019-7-9 11:00
引用: 沈婷婷 发表于 2019-7-9 11:00 为什么RT-Thread的启动流程里面,“小心翼翼,十分谨慎”里面如何实现的。每创建一个线程后它都 ...

“小心翼翼..”是什么?书么?

点赞  2019-7-9 19:01
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复