AliOS Things入门(2) AliOS Things2.1.0+MDK第一个应用"Hello World"


1 前沿

本文主要讲述了基于MDK与AliOS Things的第一个应用程序——串口输出“hello world”,能够做为AliOS Things入门的第一个里程碑,打开AliOS Things(如下简称AOS)知识的大门。
相比GCC+makefile文件方式,基于IDE(MDK\IAR…)的好处在于,不须要关心makefile的编译规则等,经过IDE工具创建好工程后,点击“编译(make)”按键,IDE会自动经过内嵌的编译器工具链(MDK-armcc、IAR-icc…)、连接脚本文件(.sct)等完成可执行目标文件的输出(.hex、.bin、.elf…),并可结合JLink\STLink等工具实如今线仿真与调试。ios

PS:本文代码基于AliOS Things rel_2.1.0版本web

1.1 内容提要

本文主要内容涉及了AliOS Things内核启动过程、main入口与应用层代码入口、helloworld应用示例等。AliOS Things的MDK开发环境的搭建能够参考另外一篇文章《基于STM32L4与MDK搭建AliOS Things2.1.0开发环境》编程

  • 基于MDK的AliOS Things启动流程
    • AliOS Things main函数入口
    • 应用程序入口
  • AliOS Things 第一个应用helloworld

2 基于MDK的AOS内核启动流程

在嵌入式系统(MCU)中,启动文件(好比startup_stm32l476xx.s)是芯片上电最早运行的一段程序,是整个系统软件很是关键的部分,若是启动文件出现错误,则整个系统就跑不起来。
app

2.1 MCU的启动过程

MCU上电后,启动文件会自动进行一些MCU芯片底层的初始化,构建程序运行必要的环境,包括:svg

  • 堆栈空间定义与初始化
  • 变量初始化
  • 申请与定义中断向量表
  • 定义复位中断函数(Reset_Handler)
    • 用SystemInit()函数来初始化系统的各类时钟、向量表偏移,而后调用(跳转到)__main()函数
    • __main()调用库函数初始化堆栈
  • 其余中断异常服务函数定义(弱[WEAK]申明)

一般,芯片原厂都提供了MDK对应内核芯片(好比ST STM32L4xx…)启动文件,启动文件由汇编程序编写,通常命名为startup_xxx.s,xxx为支持的芯片型号,好比startup_stm32l476xx.s。

基于MDK,通常默认都是使用芯片原厂提供的启动文件,并不须要涉及启动文件的修改。

在这里插入图片描述
函数

2.2 AOS内核启动过程

经过“2.1 MCU的启动过程”完成了MCU从系统上电开始运行到系统进入应用程序主代码(main函数)的准备工做。接下来即是RTOS内核启动。
下文配合AliOS Things 2.1.0中的stm32l476rg-nucleo示例进行说明。
工具

2.2.1 AOS的main程序入口

AOS的main()函数主要完成如下工做:ui

  1. 使用aos_heap_set进行堆区域起始地址、长度的设置spa

  2. 调用stm32_soc_init,初始化系统时钟(system clock)、OS无依赖的硬件HAL初始化(好比GPIO、DMA…).net

  3. 使用aos_init函数(实际上是对于krhino_init函数的封装)进行内核的初始化

  4. 建立一个sys_init的系统任务,在sys_init任务中会调用应用层代码application_start()函数

  5. sys_init中进一步调用stm32_soc_peripheral_init,初始化应用层使用到的usart、iic等外设,这些外设使用了内核提供相关服务。
    AOS内核启动流程:在这里插入图片描述
    从AOS实际代码看看:

  6. main()函数所处的位置位于platform\mcu\stm32l4xx_cube\aos\aos.c文件中。

static void sys_init(void)
{
    // 应用使用到的标准外设(USART\IIC..)初始化,好比helloworld示例应用中使用的串口USART,放在这里,主要是由于USART使用了内核相关服务
    stm32_soc_peripheral_init();
#ifdef BOOTLOADER
    main();
#else
    hw_start_hal();
    board_init();
    var_init();
#ifdef AOS_COMP_CPLUSPLUS
    cpp_init();
#endif
#if defined (AOS_OTA_RECOVERY_TYPE)
    sys_clear_ota_flag();
#endif
    // 完成各个组件的初始化以及进入应用程序
    aos_components_init(&kinit);
#ifndef AOS_BINS
    // 跳转到应用层代码入口函数
    application_start(kinit.argc, kinit.argv);  /* jump to app/example entry */
#endif
#endif
}

static void sys_start(void)
{
    // 堆的起始地址和长度初始化,该函数位于soc_impl.c中
    aos_heap_set();
    
    // 系统时钟(system clock)初始化、HAL初始化(好比GPIO、DMA...)
    stm32_soc_init();
    
    // 内核初始化
    // 1.内存初始化、内核对象初始化
    // 2.新建默认任务(定时器任务、空闲任务、工做队列等)
    aos_init();
    
    //创建aos-init任务,任务入口为sys_init,aos_start()执行后,内核调度sys_init,并调用应用层入口
    //krhino_task_dyn_create(&g_aos_init, "aos-init", 0, AOS_DEFAULT_APP_PRI, 0, AOS_START_STACK, (task_entry_t)sys_init, 1);
    krhino_task_create(&demo_task_obj, "aos-init", 0,AOS_DEFAULT_APP_PRI, 
        0, demo_task_buf, AOS_START_STACK, (task_entry_t)sys_init, 1);
    
    aos_start();
}

int main(void)
{
    sys_start();
    return 0;
}
  1. platform\mcu\stm32l4xx_cube\aos\soc_impl.c aos_heap_set()主要用于设定堆空间的起始地址与长度
extern unsigned int Image$$RW_IRAM1$$ZI$$Limit;
//comes from component board, may different to each other as hardware resource
extern size_t g_iram1_start;
extern size_t g_iram1_total_size;
k_mm_region_t g_mm_region[1];
int           g_region_num = 1;
void aos_heap_set(void)
{
    // 在.sct连接文件中指定的地址
    g_mm_region[0].start = (uint8_t*)&Image$$RW_IRAM1$$ZI$$Limit;
    // 长度=RAM区的尾地址-bss段的结束地址-偏移地址
    g_mm_region[0].len   =
        (g_iram1_start + g_iram1_total_size - (size_t)&Image$$RW_IRAM1$$ZI$$Limit);
}
  1. board\stm32l476rg-nucleo\aos\soc_init.c 定义了硬件平台的系统时钟、GPIO外设等初始化(好比按键、LED等),这些外设自己与AOS内核无直接依赖关系。
void stm32_soc_init(void)
{
    HAL_Init();

    /* Configure the system clock */
    SystemClock_Config();

    /**Configure the Systick interrupt time */
    HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/RHINO_CONFIG_TICKS_PER_SECOND);

    MX_GPIO_Init();

    MX_DMA_Init();
}

void stm32_soc_peripheral_init(void)
{
    /*default uart init*/
    stduart_init();
    hal_i2c_init(&brd_i2c1_dev);
}

static void stduart_init(void)
{
    uart_0.port = 0;
    uart_0.config.baud_rate = 115200;
    uart_0.config.data_width = DATA_WIDTH_8BIT;
    uart_0.config.flow_control = FLOW_CONTROL_DISABLED;
    uart_0.config.mode = MODE_TX_RX;
    uart_0.config.parity = NO_PARITY;
    uart_0.config.stop_bits = STOP_BITS_1;

    hal_uart_init(&uart_0);
}

2.2.2 应用程序入口

AliOS Things rel_2.1.0提供sys_init(任务)\application_start()接口,做为应用层代码的入口。应用层参考代码放在app\example目录下,在application_start()函数中实现应用代码,如3章节 Hello Word示例应用。

int application_start(int argc, char *argv[])

3 第一个应用 HelloWorld

AliOS Things的hello world应用程序放在alios-things\app\example\helloworld\helloworld.c

int application_start(int argc, char *argv[])
{
    int count = 0;
    printf("nano entry here!\r\n");

    while(1) {
        // 串口输出hello world
        printf("hello world! count %d \r\n", count++);
        // 闪烁LED
        board_drv_led_ctrl(count & 0x01);
        // 挂起1s
        aos_msleep(1000);
    };
}

经过Jlink等下载到STM32硬件板子上,并运行,实际效果以下:
image.png

4 参考