文章目录
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等工具实现在线仿真与调试。
PS:本文代码基于AliOS Things rel_2.1.0版本
1.1 内容提要
本文主要内容涉及了AliOS Things内核启动过程、main入口与应用层代码入口、helloworld应用示例等。AliOS Things的MDK开发环境的搭建可以参考另一篇文章《基于STM32L4与MDK搭建AliOS Things2.1.0开发环境》
2 基于MDK的AOS内核启动流程
在嵌入式系统(MCU)中,启动文件(比如startup_stm32l476xx.s)是芯片上电最先运行的一段程序,是整个系统软件非常关键的部分,如果启动文件出现错误,则整个系统就跑不起来。
2.1 MCU的启动过程
MCU上电后,启动文件会自动进行一些MCU芯片底层的初始化,构建程序运行必要的环境,包括:
- 堆栈空间定义与初始化
- 变量初始化
- 申请与定义中断向量表
- 定义复位中断函数(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()函数主要完成以下工作:
-
使用aos_heap_set进行堆区域起始地址、长度的设置
-
调用stm32_soc_init,初始化系统时钟(system clock)、OS无依赖的硬件HAL初始化(比如GPIO、DMA…)
-
使用aos_init函数(其实是对于krhino_init函数的封装)进行内核的初始化
-
创建一个sys_init的系统任务,在sys_init任务中会调用应用层代码application_start()函数
-
sys_init中进一步调用stm32_soc_peripheral_init,初始化应用层使用到的usart、iic等外设,这些外设使用了内核提供相关服务。
AOS内核启动流程:
从AOS实际代码看看: -
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;
}
- 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);
}
- 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硬件板子上,并运行,实际效果如下: