瑞萨单片机william hill官网
直播中

jf_1137202360

8年用户 1358经验值
擅长:嵌入式技术
私信 关注
[经验]

【RA-Eco-RA4M2-100PIN开发板】程序启动过程,中断,时钟初始化等分析

前言
本文基于Demo程序,讲解程序的启动过程。对时钟初始化,中断等关键模块进行讲解分析,了解这些细节后,以便后续进行应用开发。
分散加载文件分析
一般分析程序的启动过程从链接脚本入手,MDKARMCC编译器用的是分散加载文件.scat
在工程的Options for target xxxLinker选项卡下指定
打开该文件
先包含了
#include "memory_regions.scat"
该文件定义了存储区块划分的宏,和手册中描述是对应的

引用:      /* generated memory regions file - do not edit */

                #define RAM_START  0x20000000

                #define RAM_LENGTH 0x20000

                #define FLASH_START  0x00000000

                #define FLASH_LENGTH 0x80000

                #define DATA_FLASH_START  0x08000000

                #define DATA_FLASH_LENGTH 0x2000

                #define OPTION_SETTING_START  0x0100A100

                #define OPTION_SETTING_LENGTH 0x100

                #define OPTION_SETTING_S_START  0x0100A200

                #define OPTION_SETTING_S_LENGTH 0x100

                #define ID_CODE_START  0x00000000

                #define ID_CODE_LENGTH 0x0

                #define SDRAM_START  0x00000000

                #define SDRAM_LENGTH 0x0

                #define QSPI_FLASH_START  0x60000000

                #define QSPI_FLASH_LENGTH 0x4000000

                #define OSPI_DEVICE_0_START  0x00000000

                #define OSPI_DEVICE_0_LENGTH 0x0

                #define OSPI_DEVICE_1_START  0x00000000

                #define OSPI_DEVICE_1_LENGTH 0x0

比如SRAM0On-chip flash,其他的也可以去一一对应查看。
分散加载文件的语法可以参考MDK的帮助文档。
我们下面只讲看一些关键的地方
程序入口与栈设置
引用: LOAD_REGION_FLASH FLASH_ORIGIN ALIGN 0x80 LIMITED_FLASH_LENGTH

{

  __tz_FLASH_S +0 EMPTY 0

  {

  }



  VECTORS +0 FIXED PADVALUE 0xFFFFFFFF   ; maximum of 256 exceptions (256*4 bytes == 0x400)

  {

    *(.fixed_vectors, +FIRST)

    *(.application_vectors)

  }

如下语句将fixed_vectors段放在了FLASH_ORIGIN区域的开头,即片上flash的开头处0x00000000
根据CORTEX-M33内核的说明,程序从FLASH启动时,将最开始4字节加载到SP指针,接下来4字节加载到PC,然后跳转到PC执行。
我们搜索fixed_vectors处的代码
正式位于ra-fsp-examplesexample_projectsek_ra4m2sci_uartsci_uart_ek_ra4m2_epkeilrafspsrcbspcmsisDeviceRENESASSourcestartup.c处的
引用: /* Vector table. */

BSP_DONT_REMOVE const exc_ptr_t __Vectors[BSP_CORTEX_VECTOR_TABLE_ENTRIES] BSP_PLACE_IN_SECTION(

    BSP_SECTION_FIXED_VECTORS) =

{

    (exc_ptr_t) (&g_main_stack[0] + BSP_CFG_STACK_MAIN_BYTES), /*      Initial Stack Pointer     */

    Reset_Handler,                                             /*      Reset Handler             */

    NMI_Handler,                                               /*      NMI Handler               */

    HardFault_Handler,                                         /*      Hard Fault Handler        */

    MemManage_Handler,                                         /*      MPU Fault Handler         */

    BusFault_Handler,                                          /*      Bus Fault Handler         */

    UsageFault_Handler,                                        /*      Usage Fault Handler       */

    SecureFault_Handler,                                       /*      Secure Fault Handler      */

    0,                                                         /*      Reserved                  */

    0,                                                         /*      Reserved                  */

    0,                                                         /*      Reserved                  */

    SVC_Handler,                                               /*      SVCall Handler            */

    DebugMon_Handler,                                          /*      Debug Monitor Handler     */

    0,                                                         /*      Reserved                  */

    PendSV_Handler,                                            /*      PendSV Handler            */

    SysTick_Handler,                                           /*      SysTick Handler           */

};

可以看出启动后&g_main_stack[0] + BSP_CFG_STACK_MAIN_BYTES的值会加载到SP,
&g_main_stack[0] ~&g_main_stack[0] + BSP_CFG_STACK_MAIN_BYTES这一部分就设置为了栈空间,开始时指向栈顶(满递减栈),栈大小是BSP_CFG_STACK_MAIN_BYTES。栈使用后就往低地址使用。
然后加载Reset_HandlerPC跳转到PCReset_Handler执行。
所以芯片复位后最开始执行的代码就是Reset_Handler
这样我们就找到了函数的入口,和栈的设置。
相应的__Vector就是异常向量表。
启动过程分析
上面我们找到了代码入口
ra-fsp-examplesexample_projectsek_ra4m2sci_uartsci_uart_ek_ra4m2_epkeilrafspsrcbspcmsisDeviceRENESASSourcestartup.c中的Reset_Handler
我们继续往下看
SystemInit中先进行了使能FPU和设置中断向量表地址VTOR的操作。
后面有很多宏控制的操作,细节可以参考相关资料,下面只讲解关键的。
bsp_clock_init进行了时钟初始化,后面再分析
BSP_CFG_C_RUNTIME_INIT的值是1,下面就是根据不同编译器,进行了c运行环境的初始化。
下面是BSS段初始化为0
#if defined(__ARMCC_VERSION)
memset((uint8_t *) &Image$$BSS$$ZI$$Base, 0U, (uint32_t) &Image$$BSS$$ZI$$Length);
下面是DATA段初始化,从加载段复制到运行段
#if defined(__ARMCC_VERSION)
memcpy((uint8_t *) &Image$$DATA$$Base, (uint8_t *) &Load$$DATA$$Base, (uint32_t) &Image$$DATA$$Length);
下面是调用,放在指定段的构造函数

引用:  /* Initialize static constructors */

#if defined(__ARMCC_VERSION)

    int32_t count = Image$$INIT_ARRAY$$Limit - Image$$INIT_ARRAY$$Base;

    for (int32_t i = 0; i < count; i++)

    {

        void (* p_init_func)(void) =

            (void (*)(void))((uint32_t) &Image$$INIT_ARRAY$$Base + (uint32_t) Image$$INIT_ARRAY$$Base);

        p_init_func();

}

然后是更新时钟SystemCoreClockUpdate
最后是bsp_irq_cfg中断初始化
bsp初始化bsp_init
时钟初始化分析
前面了解到启动代码中bsp_clock_init进行了时钟初始化
使能了CACHE
bsp_clock_freq_var_init根据宏定义的参数初始化参数
这些宏在ra-fsp-examplesexample_projectsek_ra4m2sci_uartsci_uart_ek_ra4m2_epkeilra_cfgfsp_cfgbspbsp_mcu_family_cfg.h中定义
从原理图可以看到外部晶振是24MHz
#define BSP_CFG_XTAL_HZ (24000000)对应
我们再根据手册的时钟树来看PLL的来源,上面的SCKSCR 就是选择了PLL,往前看
MOSC->PLLCCR.PLSRCSEL选择MOSCPLL
->PLLCCR.PLIDIV分频
->PLLCCR.PLLMUL倍频
以上都是通过PLLCCR寄存器配置,所以我们搜索PLLCCR
可以看到对应的宏定义如下
#define BSP_CFG_PLL_SOURCE (BSP_CLOCKS_SOURCE_CLOCK_MAIN_OSC) /* PLL Src: XTAL */
#define BSP_CFG_PLL_DIV (BSP_CLOCKS_PLL_DIV_3) /* PLL Div /3 */
#define BSP_CFG_PLL_MUL BSP_CLOCKS_PLL_MUL_25_0 /* PLL Mul x25.0 */
找到对应代码在bsp_clock_init
    /* Configure the PLL registers. */
  #if 1U == BSP_FEATURE_CGC_PLLCCR_TYPE
R_SYSTEM->PLLCCR = (uint16_t) BSP_PRV_PLLCCR;
那么PLL输出时钟应该是24MHz*25/3 = 200MHz
在手册规定的范围内取了最大值
然后调用bsp_prv_clock_set_hard_reset进行时钟选择和时钟分频设置
这里要根据时钟频率设置flash等待周期
R_SYSTEM->SCKSCR = BSP_CFG_CLOCK_SOURCE; 选择时钟源
#define BSP_CFG_CLOCK_SOURCE (BSP_CLOCKS_SOURCE_CLOCK_PLL) /* Clock Src: PLL */ 配置为来源于PLL
R_SYSTEM->SCKDIVCR = BSP_PRV_STARTUP_SCKDIVCR; 设置各时钟分频值。
各分频值宏定义如下
#define BSP_CFG_ICLK_DIV (BSP_CLOCKS_SYS_CLOCK_DIV_2) /* ICLK Div /2 */
#define BSP_CFG_PCLKA_DIV (BSP_CLOCKS_SYS_CLOCK_DIV_2) /* PCLKA Div /2 */
#define BSP_CFG_PCLKB_DIV (BSP_CLOCKS_SYS_CLOCK_DIV_4) /* PCLKB Div /4 */
#define BSP_CFG_PCLKC_DIV (BSP_CLOCKS_SYS_CLOCK_DIV_4) /* PCLKC Div /4 */
#define BSP_CFG_PCLKD_DIV (BSP_CLOCKS_SYS_CLOCK_DIV_2) /* PCLKD Div /2 */
#define BSP_CFG_FCLK_DIV (BSP_CLOCKS_SYS_CLOCK_DIV_4) /* FCLK Div /4 */
#define BSP_CFG_CLKOUT_DIV (BSP_CLOCKS_SYS_CLOCK_DIV_1) /* CLKOUT Div /1 */
#define BSP_CFG_UCK_DIV (BSP_CLOCKS_USB_CLOCK_DIV_5) /* UCLK Div /5 */
所以ICLK 2分频,100MHz
也设置为了最大值
最后更新下系统时钟
SystemCoreClockUpdate
void SystemCoreClockUpdate (void)
{
    uint32_t clock_index = R_SYSTEM->SCKSCR;
    SystemCoreClock = g_clock_freq[clock_index] >> R_SYSTEM->SCKDIVCR_b.ICK;
}
仿真可以看到值和分析值是一样的
中断处理分析
前面已经从分散加载脚本和启动代码分析,VTOR的设置为__Vectors
即中断向量表为__Vectors
上述表定义了CORTEX-M33对应的各种异常。
那么用户中断向量在哪呢
再回过头来看分散加载文件
  VECTORS +0 FIXED PADVALUE 0xFFFFFFFF   ; maximum of 256 exceptions (256*4 bytes == 0x400)
  {
    *(.fixed_vectors, +FIRST)
    *(.application_vectors)
  }
紧挨着fixed_vectors的是application_vectors,那么异常向量后面的就是用户中断,即application_vectors段对应的代码。
搜索application_vectors
#define BSP_SECTION_APPLICATION_VECTORS    ".application_vectors"
继续搜索
找到ra-fsp-examplesexample_projectsek_ra4m2sci_uartsci_uart_ek_ra4m2_epkeilra_genvector_data.c
即用户的中断向量表
        BSP_DONT_REMOVE const fsp_vector_t g_vector_table[BSP_ICU_VECTOR_MAX_ENTRIES] BSP_PLACE_IN_SECTION(BSP_SECTION_APPLICATION_VECTORS) =
        {
                        [0] = sci_uart_rxi_isr, /* SCI9 RXI (Received data full) */
            [1] = sci_uart_txi_isr, /* SCI9 TXI (Transmit data empty) */
            [2] = sci_uart_tei_isr, /* SCI9 TEI (Transmit end) */
            [3] = sci_uart_eri_isr, /* SCI9 ERI (Receive error) */
        };
这个文件是RASC自动生成,当然也可以手动修改。
ra-fsp-examplesexample_projectsek_ra4m2sci_uartsci_uart_ek_ra4m2_epkeilra_genvector_data.h中定义了中断号和中断服务函数的申明。
事件号在ra-fsp-examplesexample_projectsek_ra4m2sci_uartsci_uart_ek_ra4m2_epkeilrafspsrcbspmcura4m2bsp_elc.h定义
使用了宏BSP_PRV_IELS_ENUM进行名字转换。
那么中断是如何初始化化的呢
之前启动代码看到了bsp_irq_cfg
即在该函数实现的
    for (uint32_t i = 0U; i < BSP_ICU_VECTOR_MAX_ENTRIES; i++)
    {
        R_ICU->IELSR = (uint32_t) g_interrupt_event_link_select;
}
即通过IELSR选择哪一个中断对应哪一个中断号
所以g_vector_tableg_interrupt_event_link_select的索引要意义对应。
也就是g_interrupt_event_link_select中定义了事件号,这个是bsp_elc.h中定义的和手册对应,某一事件号可以对应到某个中断号,这个是IELSR寄存器配置的。
IELSR[0]即配置中断号0对应哪个事件(中断源)
这里和其他STM32等不一样,STM32等厂家芯片中断号和中断源是固定绑定的,而这里是中断号是可以配置绑定某个中断源。
具体可以参考手册的<<13. Interrupt Controller Unit (ICU)>>
中断优先级设置调用的是R_BSP_IrqCfg->NVIC_SetPriority
使能R_BSP_IrqEnable->R_BSP_IrqEnableNoClear
就是调用CMSIS的中断操作接口,与其他CORTEX-M芯片无异。
总结
以上对关键的中断,启动过程,时钟配置等进行了讲解,只有了解这些才能更好的进行应用开发。相对于STM32等标准外设库来说,瑞萨的FSP个人感觉层次过于复杂,尤其是外设操作各种回调嵌套,对于初用着不太友好,不过也可以借鉴其一些设计思想。

附件: 您需要登录才可以下载或查看附件。没有帐号?注册

更多回帖

×
20
完善资料,
赚取积分