Keil新版本出来了,推出了一种全新开发模式RTE框架(Run-Time Environment),更好用了。然而网上的教程资料竟还都是把Keil5当成Keil4来用,直接不使用这个功能。当前正点原子或野火的教程提供的例程虽有提到Keil5,但也是基本上当Keil4来用,还是传统的模式或标准库或HAL库。当然这用来学习挺好,但是如果用来开发建议还是使用下Keil5的RTE框架。
RTE框架相当于把软件架构模块化和分层的思想引进来,直接给你提供了现成的板级驱动包和可视化模块配置,你需要做的只是配置和在驱动层之上开发应用就行了。且包含了包管理功能,包和组件使用和升级更方便了,这是一大特色,其他厂家都可以贡献和提供驱动包,其他三方都可以提供模块组件,开放共建。包括嵌入式内核你都不要移植,且可以切换想用哪个用哪个。不得不佩服老外IDE做的是真用心啊,KEIL是单片机IDE界绝对的王者。
希望国产IDE软件也多学习学习老外,如果国产软件也能这么强大好用该多好,那时候国产芯片想不火都难。STM32的火是有原因的,因为它的操作使用上比起其他单片机都简单。这得益于ST这家公司的睿智,先是提供标准库,后HAL库,如今ST推出的STM32CubeMX配置软件,就是想让你更简单更好用,易用性上甩其他厂家几条街,就是让你只用它家的就行了,以此来笼络你,垄断你。
嵌入式应用也需要好的架构,多年前我就分享过一篇文章《嵌入式软件架构设计》,分享了下模块化组件化和代码分层的思想。嵌入式应用软件架构设计_嵌入式系统软件架构图_特立独行的猫a的博客-CSDN博客
今天Keil5的这一全新的RTE框架的理念和思想,不正是模块化组件化和代码分层的体现吗?希望后续的开发,别在应用里混杂着驱动去用了,得有分层和搭积木的思想。以下介绍下Keil5的这种RTE框架的思想和使用。
前言
曾记得十年前使用STM32时那叫一个麻烦,参考正点原子和野火的教程学习直接操作寄存器。
其实这也没啥,毕竟这是传统的单片机的开发方式。如之前的51,avr,pic,msp430,我想这几款单片机都玩过的肯定都是古董玩家,想当年这几款单片机没少折腾我,当然都是对着手册操作寄存器,也没啥难的,就是很低效,换一种单片机就得先熟悉它的风格。
现在单片机开发变简单了许多,这归功于stm32做的不错。之所以stm32它一直很火,是因为它开创了一个先河,j降低了单片机的门槛,让单片机操作越来越简单了。ST官方最先推出了标准库,然而依旧觉得不是很简单。再后来ST官方又推出了HAL库,接着STM32CubeMX配置软件的横空出世,让STM32可视化配置硬件参数和寄存器,变更好用了。但STM32CubeMX仅用在ST的产品上,深度绑定。
今天STM32常见的开发常用的还是那几种,使用标准库或使用HAL库,或使用STM32CubeMX配置软件辅助配置。无论是正点原子的还是野火的教程,这方面的资料和代码例程都很多。软件IDE常用的有IAR和Keil4。
这里要介绍下Keil5的全新的开发方式RTE框架的使用。
可视化的最新的keil5软件真强大,但网上提供的教程大多是基于传统keil4的开发模式,有的虽然标题提到了RTE这种全新的包管理模式,但是还是混杂着标准库或HAL库的概念让人混淆不清,还在教你如何手工添加库和文件,没体现RTE的好处和使用。
其实RET这种模式下就不要提什么标准库或HAL库了,就当是各个厂家提供好了现成的板级驱动就好。你需要在做些配置且在驱动层之上开发应用。驱动层之下不用你过多关注,驱动层和常用模块都已提供好了。这是未来的一种趋势,因此建议尝试和使用下Keil5的RTE开发模式。
我通过查阅官网拿到第一手资料,在此整理下我的总结分享给大家。后续再看到应用层直接操作寄存器和板级接口操作的,不要再这样混着用啦,这种非RTE框架模式的本意。
不知你有没有觉察到,Keil5引入的这一开发理念很先进。会成为今后的主流趋势,值得我们学习借鉴。以后软件开发者可以把精力放在快速的开发应用和创新上,驱动操作封装成了芯片厂家要做的事。当多数国人还在卷单片机寄存器操作的时候,老外又一次走在了前列。Keil5不愧是王者,IDE好用易用性上排第一。以后不但芯片硬件上可替换,软件应用层也可以做到很少改动。
以下是我的分享,希望对大家有所帮助。另外,本文中如果有需改善的地方,欢迎留言,谢谢!
什么是RTE?
Keil5 最新的 RTE 框架是一种用于配置嵌入式软件应用程序的软件组件。它提供了图形用户界面 (GUI),使开发人员可以轻松配置系统组件,如处理器外设、通信接口和内存管理,而不需要手动编写代码。相比原来使用的标准库或 HAL 库的方式,Keil5 RTE 框架的优点在于它简化了开发流程,提供了一种简单直观的配置方法,并减少了必须手动编写的代码数量,可以节省时间并减少错误。
软件组件介绍
MDK 提供软件组件(Software Components),用于使用称为运行时环境(RTE) 的框架创建应用程序。这些组件以独立于 µVision 安装的软件包形式提供。软件包由 Arm 或第三方提供。
Software Components
图为Arm Keil提供的软件包,结构清晰。软件包可以包含设备支持,包括驱动程序、CMSIS 库和中间件组件。其中的设备系列包,由各个芯片厂家提供。
设备系列包 (DFP):包含用于微控制器设备系列的 CMSIS 系统/启动、驱动程序和闪存算法。
CMSIS:包含通用 CMSIS 组件(CORE、DSP 和 NN 库,以及 RTOS 实现)。
MDK-Middleware:包含属于 MDK-Professional 一部分的中间件库。
在项目中使用软件组件:
1.使用Pack Installer安装或更新包含预构建软件组件的软件包。
2.使用窗口Manage Run-Time Environment将软件组件添加到项目中。添加的组件显示在“项目”窗口中。
3.(可选)设置组件的选项以指定属性。
4.(可选)为特定目标选择软件包。
新项目结构介绍
不要再用以往传统的手动创建所有目录,再引入HAL库的落后方式了。当然可以在创建一些应用层的目录。其他层的按照配置出来的已经很清晰了。以下举例介绍下最的项目结构。
以上目录中,默认配置生成的是绿色显示的。Device就相当于底层驱动层,驱动层里面小写字母开头的文件,是各个厂家官方提供的固件操作功能。大写字母开头的就是驱动接口及实现。在应用层使用时最好严格分层,不要混合调用固件接口或操作寄存器了,而是应通过大写字母开头的驱动接口去配置和调用。
CMSIS是一个标准,以上CMSIS里的是标准驱动的实现,其中的串口驱动和操作系统内核放置在里面。那个Board Support算是个模块儿化组件,提供了一些通用的硬件模块操作,如以上的是个跟板子硬件相关的LED灯的操作实现,可以拿来复用,不过需要根据板子的实际管脚分配来配置。
应用层使用介绍
Board Support这个文件夹,虽然是配置出来的,但是它实际就是一个应用层功能实现。可以用来学习,看下它是如何使用GPIO接口驱动了。那么后续你的使用基本跟它类似。它的文件内容如下:
#include "Board_LED.h"#include "GPIO_STM32F10x.h"const GPIO_PIN_ID Pin_LED[] = {{ GPIOE,8 },{ GPIOE,9 },{ GPIOE, 10 },{ GPIOE, 11 },{ GPIOE, 12 },{ GPIOE, 13 },{ GPIOE, 14 },{ GPIOE, 15 },};#define LED_COUNT (sizeof(Pin_LED)/sizeof(GPIO_PIN_ID))/**\fnint32_t LED_Initialize (void)\brief Initialize LEDs\returns - \b0: function succeeded - \b -1: function failed*/int32_t LED_Initialize (void) {uint32_t n;/* Configure pins: Push-pull Output Mode (50 MHz) with Pull-down resistors */for (n = 0; n < LED_COUNT; n++) {GPIO_PortClock (Pin_LED[n].port, true);GPIO_PinWrite(Pin_LED[n].port, Pin_LED[n].num, 0);GPIO_PinConfigure(Pin_LED[n].port, Pin_LED[n].num,GPIO_OUT_PUSH_PULL,GPIO_MODE_OUT2MHZ);}return 0;}/**\fnint32_t LED_Uninitialize (void)\brief De-initialize LEDs\returns - \b0: function succeeded - \b -1: function failed*/int32_t LED_Uninitialize (void) {uint32_t n;/* Configure pins: Input mode, without Pull-up/down resistors */for (n = 0; n < LED_COUNT; n++) {GPIO_PinConfigure(Pin_LED[n].port, Pin_LED[n].num,GPIO_IN_FLOATING,GPIO_MODE_INPUT);}return 0;}/**\fnint32_t LED_On (uint32_t num)\brief Turn on requested LED\param[in] numLED number\returns - \b0: function succeeded - \b -1: function failed*/int32_t LED_On (uint32_t num) {int32_t retCode = 0;if (num < LED_COUNT) {GPIO_PinWrite(Pin_LED[num].port, Pin_LED[num].num, 1);}else {retCode = -1;}return retCode;}/**\fnint32_t LED_Off (uint32_t num)\brief Turn off requested LED\param[in] numLED number\returns - \b0: function succeeded - \b -1: function failed*/int32_t LED_Off (uint32_t num) {int32_t retCode = 0;if (num < LED_COUNT) {GPIO_PinWrite(Pin_LED[num].port, Pin_LED[num].num, 0);}else {retCode = -1;}return retCode;}/**\fnint32_t LED_SetOut (uint32_t val)\brief Write value to LEDs\param[in] valvalue to be displayed on LEDs\returns - \b0: function succeeded - \b -1: function failed*/int32_t LED_SetOut (uint32_t val) {uint32_t n;for (n = 0; n < LED_COUNT; n++) {if (val & (1<<n)) {LED_On (n);} else {LED_Off(n);}}return 0;}/**\fnuint32_t LED_GetCount (void)\brief Get number of LEDs\returnNumber of available LEDs*/uint32_t LED_GetCount (void) {return LED_COUNT;}
代码分析:该代码属于应用层,为应用提供了几个操作LED的接口(Board_LED.h声明功能接口),代码里包含了GPIO_STM32F10x.h驱动接口头文件,使用了GPIO接口驱动。通过它可以看出,应用层的功能封装和实现,基本不涉及寄存器操作。都是通过驱动接口配置和访问的。
main入口函数介绍
/***************************************************************************//***\file main.c**\detailsLED Blinking using RTX CMSIS V2 RTOS **\author EmbeTronicX**\Tested with Proteus** *****************************************************************************/ #include "RTE_Components.h"#includeCMSIS_device_header#include "cmsis_os2.h"#include "stm32f10x.h"#include "Board_LED.h" /*** This thread will turns ON and turns OFF the PORT-D LEDs with 1second delay.****Arguments:**arg-> Argument of this thread. osThreadNew()'s 2nd arg has to come here. ** */__NO_RETURN static void LED_Blink_PortD( void *arg ) {(void)arg;//unused variable//set Port D as output//GPIOD->MODER = 0x55555555;for (;;)//infinite for loop{//Turn ON the LED of Port-DLED_On(3);osDelay(1000);//1sec delay//Turn OFF the LED of Port-DLED_Off(2);//GPIOD->BSRR = 0xFFFF0000;osDelay(1000);//1sec delay}}/*** This thread will turns ON and turns OFF the PORT-E LEDs with 3second delay.****Arguments:**arg-> Argument of this thread. osThreadNew()'s 2nd arg has to come here. ** */__NO_RETURN static void LED_Blink_PortE( void *arg ) {(void)arg;//unused variable//set Port E as output//GPIOE->MODER = 0x55555555;for (;;)//infinite for loop{//Turn ON the LED of Port-E//GPIOE->BSRR = 0x0000FFFF;LED_SetOut(0);osDelay(3000);//3sec delay//Turn OFF the LED of Port-E//GPIOE->BSRR = 0xFFFF0000;osDelay(3000);//3sec delayLED_SetOut(1);}}/*** main function****Arguments:**none** */ int main (void) {// System InitializationSystemCoreClockUpdate();LED_Initialize(); osKernelInitialize(); // Initialize CMSIS-RTOSosThreadNew(LED_Blink_PortD, NULL, NULL); // Create application main threadosThreadNew(LED_Blink_PortE, NULL, NULL); // Create application main threadosKernelStart();// Start thread executionfor (;;) {//Dummy infinite for loop.}}
以上是main入口函数的实现,你有在里面发现寄存器和特殊的硬件操作吗?没有的,应用层看不到跟某一平台的相关性,更别提会出现寄存器操作了。这跟以往的开发模式有很大的不同,结构更清晰合理了。如果你的应用中混杂了寄存器或板级的驱动接口调用,请留意这样用是否合适,有没有其他的方式。
以上代码未移植嵌入式内核就可以使用RTX嵌入式系统啦,是不是很简单和方便?如果想换系统怎么办?只需要改配置就行。如果要换单片机呢?那么也放心,你的很多应用层代码都是可以复用了,仅是改下驱动层的接口参数配置,这就是这一开发模式的好处。以上代码示例不是针对stm32这一种,在keil中支持的任一单片机我都可以轻易切换。
串口驱动使用
可以看下串口的使用有多么的简单,完全颠覆了以往传统的开发方式。只需先配置好串口硬件管脚后,剩下的就是像上位机纯软件的开发一样调用接口函数即可,以下示例可以看下够简单吧。
在RTE_device.h文件中,配置串口一的硬件管脚如下:
使用如下(main.c):
#include #include "RTE_Components.h"#includeCMSIS_device_header#include "cmsis_os2.h"#include "stm32f10x.h" #include "Board_LED.h"#include "Driver_USART.h"extern ARM_DRIVER_USART Driver_USART1; /* Variable definitions ------------------------------------------------------*/static uint8_t rxBuffer[1024] = {0};static uint8_t txBuffer[1024] = {0}; /* Function declarations -----------------------------------------------------*/static void USART1_Callback(uint32_t event);/*** @briefUSART1 callback function.* @paramevent: USART events notification mask.* @return None.*/static void USART1_Callback(uint32_t event){if(event & ARM_USART_EVENT_RX_TIMEOUT){Driver_USART1.Control(ARM_USART_ABORT_RECEIVE, 1);uint32_t length = Driver_USART1.GetRxCount();memcpy(txBuffer, rxBuffer, length);Driver_USART1.Send(txBuffer, length);Driver_USART1.Receive(rxBuffer, sizeof(rxBuffer));}}void uart_init(){Driver_USART1.Initialize(USART1_Callback);Driver_USART1.PowerControl(ARM_POWER_FULL);Driver_USART1.Control(ARM_USART_MODE_ASYNCHRONOUS |ARM_USART_DATA_BITS_8 |ARM_USART_PARITY_NONE |ARM_USART_STOP_BITS_1 |ARM_USART_FLOW_CONTROL_NONE, 115200);Driver_USART1.Control(ARM_USART_CONTROL_TX, 1);Driver_USART1.Control(ARM_USART_CONTROL_RX, 1);Driver_USART1.Receive(rxBuffer, sizeof(rxBuffer));}
结语
最后,欢迎大家体验Keil5的全新的RTE开发模式。总之使用也很简单,关键是要转变思想,一定要有软件分层的思想,摒弃以往不好的开发习惯,尤其是使用stm32长期以来形成的思维惯性。让代码分封更清晰,模块化更利于复用,让维护更简单,让升级更方便。
当咱们还在卷寄存器操作的时候,老外又一次走在了前列。推广下这一单片机开发理念,这一开发理念是未来的趋势,今后人人都可以会点软件的基础上操作和使用单片机。驱动封装是芯片厂家的事。国产IDE软件应该像keil学习,让广大开发者好用,才有市场,才能国产化崛起!
最后,期待国产单片机开发IDE软件也能朝着更好用,易用的方向发展,变得更好。
其他资源
STM32 RTOS – GPIO Tutorial (CMSIS V2)
Documentation – Arm Developer