RTT目录结构
最核心的文件只有5个:
- src\thread.c:线程相关的文件,比如创建线程、启动线程。
- src\timer.c:定时器相关的文件。
- src\scheduler.c:调度器:维护线程的就绪链表、提供临界区的进出函数。
- src\ipc.c:实现了线程间通信的各类对象:消息队列、信号量、互斥量、事件等。
- include\rtservice.h:提供了链表操作
启动流程及main函数
在startup_stm32f103xe.S中,首先进入SystemInit,系统初始化,初始化系统时钟,以及向量表重定位。
void SystemInit (void)
{
/* Reset the RCC clock configuration to the default reset state(for debug purpose) */
/* Set HSION bit */
RCC->CR |= 0x00000001U;
/* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#if !defined(STM32F105xC) && !defined(STM32F107xC)
RCC->CFGR &= 0xF8FF0000U;
#else
RCC->CFGR &= 0xF0FF0000U;
#endif /* STM32F105xC */
/* Reset HSEON, CSSON and PLLON bits */
RCC->CR &= 0xFEF6FFFFU;
/* Reset HSEBYP bit */
RCC->CR &= 0xFFFBFFFFU;
/* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
RCC->CFGR &= 0xFF80FFFFU;
#if defined(STM32F105xC) || defined(STM32F107xC)
/* Reset PLL2ON and PLL3ON bits */
RCC->CR &= 0xEBFFFFFFU;
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x00FF0000U;
/* Reset CFGR2 register */
RCC->CFGR2 = 0x00000000U;
#elif defined(STM32F100xB) || defined(STM32F100xE)
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x009F0000U;
/* Reset CFGR2 register */
RCC->CFGR2 = 0x00000000U;
#else
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x009F0000U;
#endif /* STM32F105xC */
#if defined(STM32F100xE) || defined(STM32F101xE) || defined(STM32F101xG) || defined(STM32F103xE) || defined(STM32F103xG)
#ifdef DATA_IN_ExtSRAM
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM */
#endif
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
#endif
}
然后进入entry
/* Add -eentry to arm-none-eabi-gcc argument */
int entry(void)
{
rtthread_startup();
return 0;
}
#endif
进入内核启动
int rtthread_startup(void)
{
rt_hw_interrupt_disable(); //关中断
/* board level initialization
* 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();
#ifdef RT_USING_SMP
rt_hw_spin_lock(&_cpus_lock);
#endif /*RT_USING_SMP*/
/* start scheduler */
rt_system_scheduler_start();
/* never reach here */
return 0;
}
#endif
系统开始调度后,进入main_thread_entry
/* the system main thread */
void main_thread_entry(void *parameter)
{
extern int main(void);
extern int $Super$$main(void);
#ifdef RT_USING_COMPONENTS_INIT
/* RT-Thread components initialization */
rt_components_init();
#endif
#ifdef RT_USING_SMP
rt_hw_secondary_cpu_up();
#endif
/* invoke system main function */
#if defined(__CC_ARM) || defined(__CLANG_ARM)
$Super$$main(); /* for ARMCC. */
#elif defined(__ICCARM__) || defined(__GNUC__)
main();
#endif
}
rt_components_init()组件初始化
进入main函数
void rt_components_init(void)
{
#if RT_DEBUG_INIT
int result;
const struct rt_init_desc *desc;
rt_kprintf("do components initialization.\n");
for (desc = &__rt_init_desc_rti_board_end; desc < &__rt_init_desc_rti_end; desc ++)
{
rt_kprintf("initialize %s", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d done\n", result);
}
#else
const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
{
(*fn_ptr)();
}
#endif
}
组件初始化:初始化一系列组件,创建了SHELL线程,用户可以在串口上输入各种命令。因此main函数为空函数时,也可以通过SHELL访问。
#if defined(__CC_ARM) || defined(__CLANG_ARM)
$Super$$main(); /* for ARMCC. */
#elif defined(__ICCARM__) || defined(__GNUC__)
main();
#endif
S
u
p
e
r
Super
Super$main函数:就是调用main函数
rt_components_init函数中已经创建了SHELL线程,用户可以通过串口跟它交互。所以即使main函数为空,还是可以通过串口操作开发板。
每个工程都有一个 main.c 文件:
- 可以把 main 函数写为空函数
- 可以在 main 函数里面创建线程
- 也可以在 main.c 任意位置使用宏 MSH_CMD_EXPORT 定义命令,然后在串口中执行这个命令
数据类型和编程规范
RT-Thread的数据类型定义在rtdef.h中,涉及8位、16位、32位、64位的定义。
格式为都以rt_开头,然后指明是否是有符号类型和位数,最后以_t结尾。
/* RT-Thread basic data type definitions */
#ifndef RT_USING_ARCH_DATA_TYPE
typedef signed char rt_int8_t; /**< 8bit integer type */
typedef signed short rt_int16_t; /**< 16bit integer type */
typedef signed int rt_int32_t; /**< 32bit integer type */
typedef unsigned char rt_uint8_t; /**< 8bit unsigned integer type */
typedef unsigned short rt_uint16_t; /**< 16bit unsigned integer type */
typedef unsigned int rt_uint32_t; /**< 32bit unsigned integer type */
#ifdef ARCH_CPU_64BIT
typedef signed long rt_int64_t; /**< 64bit integer type */
typedef unsigned long rt_uint64_t; /**< 64bit unsigned integer type */
#else
typedef signed long long rt_int64_t; /**< 64bit integer type */
typedef unsigned long long rt_uint64_t; /**< 64bit unsigned integer type */
#endif
#endif
typedef int rt_bool_t; /**< boolean type */
typedef long rt_base_t; /**< Nbit CPU related date type */
typedef unsigned long rt_ubase_t; /**< Nbit unsigned CPU related data type */
typedef rt_base_t rt_err_t; /**< Type for error number */
typedef rt_uint32_t rt_time_t; /**< Type for time stamp */
typedef rt_uint32_t rt_tick_t; /**< Type for tick count */
typedef rt_base_t rt_flag_t; /**< Type for flags */
typedef rt_ubase_t rt_size_t; /**< Type for size number */
typedef rt_ubase_t rt_dev_t; /**< Type for device */
typedef rt_base_t rt_off_t; /**< Type for offset */
还定义特殊数据类型,比如rt_base_t是基础数据类型,和芯片位数有关,在Cortex-M3等32位架构中,就是32位的;在 A57 等 64 位架构中,它就是 64 位的。
rt_err_t用于记录错误编号。
rt_time_t 用于时间戳
rt_tick_t用于周期性时钟tick计数。
函数名使用英文小写,单词之间使用_连接,通常以rt开头。
如果函数入口参数是空,必须使用void作为入口参数。
内部静态函数,则以_开头。