二、RT-Thread启动流程详解(硬件初始化篇)

备注:基于正点原子阿波罗STM32F767IGT6开发板,RT-Thread Studio开发环境

目录

1、入口函数的确认

2、函数执行流程 

3、关键函数功能详解

3.1 Reset_Handler

3.2 SystemInit()

3.3 entry()

3.4 rtthread_startup

3.4 rt_hw_interrupt_disable

3.4 rt_hw_board_init

3.5 hw_board_init

3.6 rt_console_set_device

3.7 rt_components_board_init

3.8 rt_show_version


1、入口函数的确认

    编译工具链为arm-none-eabi,根据链接文件link.lds中的关键字ENTRY可以确认程序的入口地址是Reset_Handler。

/*
 * linker script for STM32F767IG with GNU ld
 */

/* Program Entry, set to mark it as "used" and avoid gc */
MEMORY
{
    ROM (rx) : ORIGIN = 0x08000000, LENGTH =  1024k /* 1024K flash */
    RAM (rw) : ORIGIN = 0x20020000, LENGTH =  384k /* 384K sram */
}
ENTRY(Reset_Handler)
_system_stack_size = 0x400;

2、函数执行流程 

一图流,如图2.2.1

图2.2.1

3、关键函数功能详解

函数功能讲解增加到了代码注释中。

3.1 Reset_Handler

  主板上电复位后从Reset_Handler开始执行,在进入SystemInit函数前,主要做了如下事情:

  3.1.1 设置SP栈指针;

  3.1.2 data段数据从flash搬运到SRAM;

  3.1.3 bss段清零,然后跳转至SystemInit; 注意:地址相关信息均由link.lds链接文件在编译时确定。

  .section  .text.Reset_Handler
  .weak  Reset_Handler
  .type  Reset_Handler, %function
Reset_Handler:  
    ldr   sp, =_estack 	/* set stack pointer initialize, the '_estack' definition in the link file*/

/**
  * Data segment: Data stored at compile time (not at runtime) that can be read and written.
  * This is also known as static storage,
  * where initialized global variables and initialized static variables are stored,
  * and where constants are stored
  */
/* Copy the data segment initializers from flash to SRAM */  
    movs  r1, #0			/* save 0 to R1 */
    b  LoopCopyDataInit

CopyDataInit:				/* .data data copy */
    ldr  r3, =_sidata		/* R3 = The .data is at the starting address of Flash */
    ldr  r3, [r3, r1]		/* load the value of the [R3 + R1] address into R3 */
    str  r3, [r0, r1]		/* save the value of R3 to the address of [R0 + R1] */
    adds  r1, r1, #4		/* R1 = R1 + 4 */
    
LoopCopyDataInit:
    ldr  r0, =_sdata		/* assign the .data start address into R0 */
    ldr  r3, =_edata		/* assign the .data end address into R3 */
    adds  r2, r0, r1		/* r2 = r0 + r1, */
    cmp  r2, r3				/* compare R2 and R3 value */
    bcc  CopyDataInit		/* if R2 and R3 value not equality,  jump */
    ldr  r2, =_sbss			/* load  the .bss satrt address into R2 */
    b  LoopFillZerobss		/* jump to LoopFillZerobss */

/**
 * BSS section:
 * Global and static variables defined without initial values are placed in this section
 */
/* Zero fill the bss segment. */  
FillZerobss:
    movs  r3, #0			/* save 0 to R3 */
    str  r3, [r2], #4		/* save the value of R3 to the address of [R2], and R2 = R2 + 4 */
    
LoopFillZerobss:
    ldr  r3, = _ebss		/* load  the .bss end address into R2 */
    cmp  r2, r3				/* compare R2 and R3 value */
    bcc  FillZerobss		/* if R2 and R3 value not equality,  jump */

/* Call the clock system initialization function.*/
    bl  SystemInit			/* jump to SystemInit */
/* Call static constructors */
    /* bl __libc_init_array */
/* Call the application's entry point.*/
    bl  entry				/* jump to entry */
    bx  lr					/*  */
.size  Reset_Handler, .-Reset_Handler

3.2 SystemInit()

 该函数由ST官方提供,进行了FPU设置,时钟初始化,中断向量表偏移地址设置。

void SystemInit(void)
{
  /* FPU settings ------------------------------------------------------------*/
  #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
  #endif
  /* Reset the RCC clock configuration to the default reset state ------------*/
  /* Set HSION bit */
  RCC->CR |= (uint32_t)0x00000001;

  /* Reset CFGR register */
  RCC->CFGR = 0x00000000;

  /* Reset HSEON, CSSON and PLLON bits */
  RCC->CR &= (uint32_t)0xFEF6FFFF;

  /* Reset PLLCFGR register */
  RCC->PLLCFGR = 0x24003010;

  /* Reset HSEBYP bit */
  RCC->CR &= (uint32_t)0xFFFBFFFF;

  /* Disable all interrupts */
  RCC->CIR = 0x00000000;

  /* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
  SCB->VTOR = RAMDTCM_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
}

3.3 entry()

 调用rtthread_startup(),进入RT-Thread启动阶段。

int entry(void)
{
    rtthread_startup();
    return 0;
}

3.4 rtthread_startup

 内部进行函数调用,包括硬件初始化和OS kernel启动两大部分。

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

3.4 rt_hw_interrupt_disable

 硬件初始化前关闭中断,防止硬件初始化失败,由汇编语言实现,该函数操作了只有一位的中断屏蔽寄存器PRIMASK。

/*
 * rt_base_t rt_hw_interrupt_disable();
 * interrupt mask register
 * This register has only one bit, and when set to 1, it will turn off all interrupt-masking exceptions,
 * leaving only NMI and hard fault. The default value is 0
 *
 * operating instructions:
 *	 MRS R0, PRIMASK        ; R0=PRIMASK
 *	 MSR PRIMASK, R0        ; PRIMASK=R0
 *	 CPSID I                ; PRIMASK=1
 *	 CPSIE I                ; PRIMASK=0
 */
.global rt_hw_interrupt_disable
.type rt_hw_interrupt_disable, %function
rt_hw_interrupt_disable:
    MRS     r0, PRIMASK
    CPSID   I
    BX      LR

/*
 * void rt_hw_interrupt_enable(rt_base_t level);
 */
.global rt_hw_interrupt_enable
.type rt_hw_interrupt_enable, %function
rt_hw_interrupt_enable:
    MSR     PRIMASK, r0
    BX      LR

3.4 rt_hw_board_init

 该函数中的堆初始化函数涉及到内存管理算法,具体实现较为复杂,这里不展开讲解,感兴趣的请自行学习,

RT_WEAK void rt_hw_board_init()
{
    extern void hw_board_init(char *clock_src, int32_t clock_src_freq, int32_t clock_target_freq);

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

    hw_board_init(BSP_CLOCK_SOURCE, BSP_CLOCK_SOURCE_FREQ_MHZ, BSP_CLOCK_SYSTEM_FREQ_MHZ);

    /* Set the shell console output device */
#if defined(RT_USING_DEVICE) && defined(RT_USING_CONSOLE)
    rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#endif

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

3.5 hw_board_init

 板级驱动初始化,包括时钟初始化,系统定时器Systick初始化,GPIO_Pin设备初始化,USART串口初始化。其中设备初始化用到了设备驱动框架,会在后续章节展开讲解。

void hw_board_init(char *clock_src, int32_t clock_src_freq, int32_t clock_target_freq)
{
    extern void rt_hw_systick_init(void);
    extern void clk_init(char *clk_source, int source_freq, int target_freq);

#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();

    /* enable interrupt */
    __set_PRIMASK(0);
    /* System clock initialization */
    clk_init(clock_src, clock_src_freq, clock_target_freq);
    /* disbale interrupt */
    __set_PRIMASK(1);

    rt_hw_systick_init();

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

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

3.6 rt_console_set_device

 控制台设备绑定,这里在创建工程的时候指定为UART1(PA9、PA10),也涉及到了设备驱动框架,通过 rt_device_t rt_device_find(const char *name) 函数找到名为name的设备,然后将该设备的设备描述结构体赋值给全局变量 _console_device,然后在调用void rt_kprintf(const char *fmt, ...)等输出函数时,通过设备初始化时绑定的write函数进行具体的输出行为。

rt_device_t rt_console_set_device(const char *name)
{
    rt_device_t new_device, old_device;

    /* save old device */
    old_device = _console_device;

    /* find new console device */
    new_device = rt_device_find(name);
    if (new_device != RT_NULL)
    {
        if (_console_device != RT_NULL)
        {
            /* close old console device */
            rt_device_close(_console_device);
        }

        /* set new console device */
        rt_device_open(new_device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_STREAM);
        _console_device = new_device;
    }

    return old_device;
}

  rt_kprintf部分源码,着重理解rt_device_write()函数调用,指定了输出设备的具体行为。

void rt_kprintf(const char *fmt, ...)
{
    va_list args;
    rt_size_t length;
    static char rt_log_buf[RT_CONSOLEBUF_SIZE];

    va_start(args, fmt);
    /* the return value of vsnprintf is the number of bytes that would be
     * written to buffer had if the size of the buffer been sufficiently
     * large excluding the terminating null byte. If the output string
     * would be larger than the rt_log_buf, we have to adjust the output
     * length. */
    length = rt_vsnprintf(rt_log_buf, sizeof(rt_log_buf) - 1, fmt, args);
    if (length > RT_CONSOLEBUF_SIZE - 1)
        length = RT_CONSOLEBUF_SIZE - 1;
#ifdef RT_USING_DEVICE
    if (_console_device == RT_NULL)
    {
        rt_hw_console_output(rt_log_buf);
    }
    else
    {
        rt_uint16_t old_flag = _console_device->open_flag;

        _console_device->open_flag |= RT_DEVICE_FLAG_STREAM;
        /* by rt_console_set_device() function bound output device */
        rt_device_write(_console_device, 0, rt_log_buf, length);
        _console_device->open_flag = old_flag;
    }
#else
    rt_hw_console_output(rt_log_buf);
#endif
    va_end(args);
}

3.7 rt_components_board_init

 该函数用于执行通过 INIT_BOARD_EXPORT(fn)宏把fn函数的地址放到.rti_fn.1段,该宏展开步骤如下:

第一步:INIT_EXPORT(fn, "1")

第二步:RT_USED const init_fn_t _rt_init##fn SECTION(".rti_fn." level) = fn

第三步:__attribute__((used)) const init_fn_t__rt_init_##fn SECTION(".rti_fn." level) = fn

第四步:__attribute__((used)) const init_fn_t__rt_init_##fn __attribute__((section(x))) = fn

以drv_clk.c中的INIT_BOARD_EXPORT(clock_information)宏为例,展开后形式如下: 

__attribute__((used)) const init_fn_t __rt_init_clock_information __attribute__((section(".rti_fn." "1"))) = clock_information

 翻译过来就是把 clock_information的名字拼接为__rt_init_clock_information并把函数地址放在.rti_fn.1 section段中

void rt_components_board_init(void)
{
    const init_fn_t *fn_ptr;

    for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
    {
        (*fn_ptr)();
    }
#endif
}

 该函数的执行需要结合.map文件来看,可知该函数共执行了两个函数,int rti_board_start(void) 和 int clock_information(void)。

 *(SORT(.rti_fn*))
 .rti_fn.0      0x0800d0d8        0x4 ./rt-thread/src/components.o
                0x0800d0d8                __rt_init_rti_start
 .rti_fn.0.end  0x0800d0dc        0x4 ./rt-thread/src/components.o
                0x0800d0dc                __rt_init_rti_board_start
 .rti_fn.1      0x0800d0e0        0x4 ./drivers/drv_clk.o
                0x0800d0e0                __rt_init_clock_information
 .rti_fn.1.end  0x0800d0e4        0x4 ./rt-thread/src/components.o
                0x0800d0e4                __rt_init_rti_board_end
 .rti_fn.6      0x0800d0e8        0x4 ./rt-thread/components/finsh/shell.o
                0x0800d0e8                __rt_init_finsh_system_init
 .rti_fn.6.end  0x0800d0ec        0x4 ./rt-thread/src/components.o
                0x0800d0ec                __rt_init_rti_end
                0x0800d0f0                __rt_init_end = .
                0x0800d0f0                . = ALIGN (0x4)

3.8 rt_show_version

 该函数打印输出OS系统版本,编译日期等信息。

void rt_show_version(void)
{
    rt_kprintf("\n \\ | /\n");
    rt_kprintf("- RT -     Thread Operating System\n");
    rt_kprintf(" / | \\     %d.%d.%d build %s\n",
               RT_VERSION, RT_SUBVERSION, RT_REVISION, __DATE__);
    rt_kprintf(" 2006 - 2019 Copyright by rt-thread team\n");
}

至此,板级硬件初始化结束,下一步进行OS内核启动。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值