注册 登录
电子工程世界-论坛 返回首页 EEWORLD首页 频道 EE大学堂 下载中心 Datasheet 专题
liuluqqzj的个人空间 https://home.eeworld.com.cn/space-uid-361122.html [收藏] [复制] [分享] [RSS]
日志

【空调精灵】STM32F7-Disco首次运行RTT

已有 1106 次阅读2015-10-28 15:33 |个人分类:Cortex-M7

文章目录 STM32F7-DISCO评估板极简介 获得RTT最新版本库 编译烧写新工程 RTT启动过程介绍STM32F7-DISCO评估板极简介
STM32F7-DISCO评估板基于STM32F746NG微控制器,板载资源丰富。板子的供电接口有四种选择:外部5v供电、5v st-link供电、USB-FS及USB-HS供电。通过反面的JP1跳帽选择供电方式,此处选择5v st-link即可。
烧写官方demo
从官网下载最新的st-link烧写工具,安装之后打开烧写工具,打开要烧写的hex文件(位于官方cube库的STM32Cube_FW_F7_V1.1.0\Projects\STM32746G-Discovery\Demonstration下),连接开发板的同时选择外部flash烧写算法,如下所示:

完成上述步骤后点击烧写验证即可。
获得RTT最新版本库
RTT为国产开源的实时嵌入式操作系统,类linux风格并附带有丰富的第三方库支持,从官方的github库中将最新版本克隆到本地。其代码库的组织结构如下所示:

其中bsp/下包含了rtt针对各种型号处理器的初始化模版工程,从中找到stm32f7-disco文件目录,可以看到里面包含有一个keil下的模版工程,此工程还不能直接使用,需要按照官方的指导手册来一步步配置,最终通过SCons来调用MDK工具链编译生成新工程project.uvprojx。由于官方文档描述十分详细此处不再赘述。
编译烧写新工程
在完成前两步的基础上,我们已经具备了针对f746的新工程,使用最新的keil ide打开该工程。如果ide中还没有安装stm32f7xx的芯片支持包,建议去keil官网直接下载安装,keil下的自动安装非常慢,而且多半会失败。
在更新了芯片库之后,点击编译会出现 Use MicroLIB的相关错误,解决方式:在工程选项中去除对C库的勾选,同时删除工程中重复添加的main.c及sram.c,如下所示:

完成上述工作之后,工程编译无错误,点击下载可以看到板子上位于复位按钮旁边的led1在闪烁。此处打开串口连接可以看到终端出现类似linux命令行的执行窗口。

至此,rtt在f746上的初次运行就完成了,此处描述较为简洁,详细的步骤还需要翻阅相关的用户手册及安装配置相关的软件环境。
RTT启动过程介绍
打开一个新工程首先会去查看main.c源文件,让人意外的是工程中的main.c干净的不像话…只有一条语句:return 0;但是板子的灯在闪烁,串口也有命令交互,因此可以肯定的是rtt已经启动了,只不过代码的初始化过程不在main.c中。
使用过IAR的童鞋应该知道IAR编译工程后在进入main.c之前添加了一段函数代码,此处采用的正是此机制,只不过换成了keil。MCU第一次上电运行的运行过程一般为:
  • 系统上电进入Reset_Handler中断,执行 SystemInit;
  • SystemInit完成系统时钟源及向量表的配置后,跳转到main函数;
  • 系统开始运行用户程序;
为了知道在跳转到main函数之前系统做了什么,通过调试开启单步运行可以看到,程序运行到此处:

从图中可以看到,不同的编译器在跳转到main函数之前执行所替代的函数各不相同,对于当前的keil工程,通过int $Sub$$main(void)来取代main函数的跳转,再通过int $Super$$main(void);跳回到main函数中。而系统的初始化过程则在int $Sub$$main(void);中完成。
在int rtthread_startup(void)中有一系列的rtt初始化函数,重点关注函数rt_hw_board_init();及rt_application_init();
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(); // 系统调度器初始化    /* 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;}
rt_hw_board_init();函数中主要实现的是系统时钟及外设的初始化,见如下代码注释:
/** * This function will initial STM32 board. */void rt_hw_board_init(){    /* Configure the MPU attributes as Write Through */    //mpu_init();   // 没有配置mpu    /* Enable the CPU Cache */    CPU_CACHE_Enable();    /* STM32F7xx HAL library initialization:    - Configure the Flash ART accelerator on ITCM interface    - Configure the Systick to generate an interrupt each 1 msec    - Set NVIC Group Priority to 4    - Global MSP (MCU Support Package) initialization    */    HAL_Init(); // 硬件抽象层初始化,中断,systick配置    /* Configure the system clock @ 200 Mhz */    SystemClock_Config();   // 配置系统时钟    /* init systick */    SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);   // 又配置了一遍    /* set pend exception priority */    NVIC_SetPriority(PendSV_IRQn, (1 << __NVIC_PRIO_BITS) - 1); // 又配置了一遍#ifdef RT_USING_COMPONENTS_INIT // 已开启    rt_components_board_init(); // 板载组件初始化#endif#ifdef RT_USING_EXT_SDRAM   // 已开启    rt_system_heap_init((void*)EXT_SDRAM_BEGIN, (void*)EXT_SDRAM_END);  // 初始化外部内存    sram_init();#else    rt_system_heap_init((void*)HEAP_BEGIN, (void*)HEAP_END);#endif#ifdef RT_USING_CONSOLE    rt_console_set_device(RT_CONSOLE_DEVICE_NAME);  // 设置新控制台设备#endif}
上述代码中调用了板级组件初始化函数rt_components_board_init,该函数体的代码如下所示:
/** * RT-Thread Components Initialization for board */void rt_components_board_init(void){#if RT_DEBUG_INIT   // 未开启    int result;    const struct rt_init_desc *desc;    for (desc = &__rt_init_desc_rti_start; desc < &__rt_init_desc_rti_board_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;    // led mpu sdram uart    for (fn_ptr = &__rt_init_rti_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)        {        (*fn_ptr)();    }#endif}
从代码上直观理解,fn_ptr为一个函数指针,__rt_init_rti_start及__rt_init_rti_board_end为函数指针的起始及结束地址,通过for循环来调用这一系列的指针函数来完成班级组件的初始化。
而fn_ptr所指向的函数究竟是什么,有两种方式可以探别,一种是在调试中的(*fn_ptr)();语句处打断点,每次运行到此处时进入函数内部执行,则可以看到当前for循环所调用的指针函数;另一种方式则是直观的看代码,查找这两个起始及结束地址的定义,出现如下代码:
static int rti_start(void){    return 0;}INIT_EXPORT(rti_start, "0");// __rt_init_rti_startstatic int rti_board_end(void){    return 0;}INIT_EXPORT(rti_board_end, "1.end");// __rt_init_rti_board_end
INIT_EXPORT的宏定义如下:
#define INIT_EXPORT(fn, level)  \        const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn
SECTION的宏定义如下:
#define SECTION(x)                  __attribute__((section(x)))
__attribute__为ARM编译器的扩展属性,realview编译工具手册中对其描述如下:

将 INIT_EXPORT(rti_start, "0");宏展开,得到的函数语句如下:
const init_fn_t __rt_init_rti_start __attribute__((section(".rti_fn.0"))) = rti_start;
因此__rt_init_rti_start为一个函数指针,指向rti_start函数,同时__attribute__的section属性将该函数指针放在名为.rti_fn.0的段中。同理__rt_init_rti_board_end也是一个函数指针,指向rti_board_end函数,同时指定该函数指针存放在.rti_fn.1.end段中。
程序分析到这里,已经明白了编译器中关于段的含义,但是.rti_fn.0段与.rti_fn.1.end段之间到底还存在哪些函数指针,则需要进一步分析。工程全局搜索宏INIT_EXPORT,可看到如下宏定义:
/* board init routines will be called in board_init() function */        #define INIT_BOARD_EXPORT(fn)           INIT_EXPORT(fn, "1")        /* device/component/fs/app init routines will be called in init_thread */        /* device initialization */        #define INIT_DEVICE_EXPORT(fn)          INIT_EXPORT(fn, "2")        /* components initialization (dfs, lwip, ...) */#define INIT_COMPONENT_EXPORT(fn)       INIT_EXPORT(fn, "3")/* file system initialization (dfs-elm, dfs-rom, ...) */#define INIT_FS_EXPORT(fn)              INIT_EXPORT(fn, "4")/* environment initialization (mount disk, ...) */#define INIT_ENV_EXPORT(fn)                INIT_EXPORT(fn, "5")/* appliation initialization (rtgui application etc ...) */#define INIT_APP_EXPORT(fn)             INIT_EXPORT(fn, "6")
从宏定义中可以看到,根据编译器生成的段排列顺序,所有使用INIT_EXPORT(fn, "1")宏指定的指针函数都将介于.rti_fn.0段与.rti_fn.1.end段之间,即调用INIT_BOARD_EXPORT导出函数的语句处都会生成一个函数指针存放在这两个段之间。全局搜索INIT_BOARD_EXPORT,可以得知rt_components_board_init函数中的for循环调用的是以下函数:

其中stm32_hw_usart_init初始化并注册usart1设备,剩下的三个函数则是sdram、mpu及led引脚的初始化。
为了验证上述结论的正确性,打开工程生成的.map文件,全局搜索关键字.rti_fn.可以看到编译器放置在该段范围内的函数指针:
解决了stm32_hw_usart_init函数,剩下的rt_application_init则简单许多,该函数创建了一个主任务main_thread_entry,任务回调函数如下:
/* 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)    $Super$$main(); /* for ARMCC. */ // 此时才会去调用main函数#elif defined(__ICCARM__) || defined(__GNUC__)    main();#endif}
该任务调用了rtt的组件初始化函数rt_components_init,该函数完成了一些系统的初始化服务,包括i2c、finsh、led_task等,分析过程同rt_components_board_init函数,不再赘述。任务的尾部才跳回到main函数中去处理用户代码。初始化的最后回到rtthread_startup函数中完成rtt的任务调度及启动。

本文来自论坛,点击查看完整帖子内容。

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

热门文章