还在老一套?STM32使用新KEIL5的IDE,全新开发模式RTE介绍及使用

news2024/11/28 4:37:09

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框架模式的本意。


以下是我的分享,希望对大家有所帮助。另外,本文中如果有需改善的地方,欢迎留言,谢谢!

什么是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))


/**
  \fn          int32_t LED_Initialize (void)
  \brief       Initialize LEDs
  \returns
   - \b  0: 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;
}

/**
  \fn          int32_t LED_Uninitialize (void)
  \brief       De-initialize LEDs
  \returns
   - \b  0: 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;
}

/**
  \fn          int32_t LED_On (uint32_t num)
  \brief       Turn on requested LED
  \param[in]   num  LED number
  \returns
   - \b  0: 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;
}

/**
  \fn          int32_t LED_Off (uint32_t num)
  \brief       Turn off requested LED
  \param[in]   num  LED number
  \returns
   - \b  0: 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;
}

/**
  \fn          int32_t LED_SetOut (uint32_t val)
  \brief       Write value to LEDs
  \param[in]   val  value to be displayed on LEDs
  \returns
   - \b  0: 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;
}

/**
  \fn          uint32_t LED_GetCount (void)
  \brief       Get number of LEDs
  \return      Number of available LEDs
*/
uint32_t LED_GetCount (void) {

  return LED_COUNT;
}

代码分析:该代码属于应用层,为应用提供了几个操作LED的接口(Board_LED.h声明功能接口),代码里包含了GPIO_STM32F10x.h驱动接口头文件,使用了GPIO接口驱动。通过它可以看出,应用层的功能封装和实现,基本不涉及寄存器操作。都是通过驱动接口配置和访问的。

main入口函数介绍

/***************************************************************************//**
*  \file       main.c
*
*  \details    LED Blinking using RTX CMSIS V2 RTOS 
*
*  \author     EmbeTronicX
*
*  \Tested with Proteus
*
* *****************************************************************************/ 
#include "RTE_Components.h"
#include  CMSIS_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-D
    LED_On(3);
    osDelay(1000);                      //1sec delay
    //Turn OFF the LED of Port-D
    LED_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 delay
	LED_SetOut(1);
  }
}

/*
** main function
**
**  Arguments:
**      none
**   
*/ 
int main (void) 
{
  // System Initialization
  SystemCoreClockUpdate();
	
  LED_Initialize();
 
  osKernelInitialize();                       // Initialize CMSIS-RTOS
  osThreadNew(LED_Blink_PortD, NULL, NULL);   // Create application main thread
  osThreadNew(LED_Blink_PortE, NULL, NULL);   // Create application main thread
  osKernelStart();                            // Start thread execution
  for (;;) 
  {
    //Dummy infinite for loop.
  }
}

以上是main入口函数的实现,你有在里面发现寄存器和特殊的硬件操作吗?没有的,应用层看不到跟某一平台的相关性,更别提会出现寄存器操作了。这跟以往的开发模式有很大的不同,结构更清晰合理了。如果你的应用中混杂了寄存器或板级的驱动接口调用,请留意这样用是否合适,有没有其他的方式。

以上代码未移植嵌入式内核就可以使用RTX嵌入式系统啦,是不是很简单和方便?如果想换系统怎么办?只需要改配置就行。如果要换单片机呢?那么也放心,你的很多应用层代码都是可以复用了,仅是改下驱动层的接口参数配置。

串口驱动使用

可以看下串口的使用有多么的简单,完全颠覆了以往传统的开发方式。只需先配置好串口硬件管脚后,剩下的就是像上位机纯软件的开发一样调用接口函数即可,以下示例可以看下够简单吧。

在RTE_device.h文件中,配置串口一的硬件管脚如下:

 使用如下(main.c):

#include <string.h>
#include "RTE_Components.h"
#include  CMSIS_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);
/**
  * @brief  USART1 callback function.
  * @param  event: 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开发模式。总之使用也很简单,关键是要转变思想,一定要有软件分层的思想,摒弃以往不好的开发习惯。让分封更清晰,模块化更利于复用,让维护更简单,让升级更方便。

最后,期待国产单片机开发IDE软件也能朝着更好用,易用的方向发展,变得更好。

其他资源

STM32 RTOS - GPIO Tutorial (CMSIS V2)

Documentation – Arm Developer

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/537787.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt Quick系列(1)—开发界面以及相关文件介绍

作者&#xff1a;CCAccept 专栏&#xff1a;Qt Quick 文章目录 开发界面相关文件介绍.pro文件.pri文件&#xff08;这个一般要稍微大一点的Qt项目才会用到&#xff09;main.cppmain.qml 开发界面 如何具体的写代码实现Qt Quick的UI界面&#xff0c;首先我们需要新建一个空的…

Java面向对象程序设计实验报告(实验三 继承的练习)

✨作者&#xff1a;命运之光 ✨ 专栏&#xff1a;Java面向对象程序设计实验报告 ​ 目录 ✨一、需求设计 ✨二、概要设计 ✨三、详细设计 ✨四、调试结果 ✨五、测试结果 ✨附录&#xff1a;源程序代码&#xff08;带注释&#xff09; demo3类 Person类 Student类 …

[架构之路-201]-《软考-系统分析师》- 关键技术 - 结构化分析方法与面向对象分析(分析与设计的区别)

目录 前言&#xff1a; 一、分析与设计的区别 二、结构化分析方法 2.1 实体关系图&#xff1a;E - R 图 &#xff08;名词&#xff09; 2.2. 数据流图&#xff08;数据的流动&#xff09; (1) 顶层图。 (2) 逐层分解。 2.3. 状态转换图&#xff08;动作&#xff09; …

电压比较器

电压比较器&#xff1a; 应用一&#xff1a;过压&#xff0c;低压检测&#xff08;over and undervoltage detectors&#xff09; 如果Vin<Vs&#xff0c;则VoutVpullup。 如果Vin>Vs&#xff0c;则Vout0V。 应用二&#xff1a;窗口比较器&#xff08;window comparat…

Zadoff-Chu序列

ZC序列的定义 ZC序列有两个重要的参数&#xff1a; 根索引&#xff08;root index&#xff09; q 1 , 2 , ⋯ , N z c − 1 q1,2,\cdots,N_{zc}-1 q1,2,⋯,Nzc​−1ZC序列的长度 N z c N_{zc} Nzc​&#xff0c;一定得是奇数&#xff08;常常是质数&#xff09; 给定上述两…

网络:网络分层与协议/OSI七层模型/(TCP/IP模型)

一、简单理解 OSI模型(Open System Interconnection)&#xff1a; 七层模型&#xff0c;亦称OSI&#xff08;Open System Interconnection&#xff09;。参考模型是国际标准化组织&#xff08;ISO&#xff09;制定的一个用于计算机或通信系统间互联的标准体系&#xff0c;一般…

数据结构——带头双向循环链表实现

目录 前言 一、结构介绍 二、增删查改的实现 1.在某一位置前插入数据 2.头插 3.尾插 4.删除某一位置的节点 5.头删 6.尾删 7.查找 8.打印 9.销毁 三、完整项目代码 1.头文件(List.h) 2.函数文件(List.c) 3.主函数测试文件(test.c) 前言 在学习数据结构过程中&…

HQL语法

HQL基础语法 Hive中的语句叫做HQL语句,是一种类似SQL的语句,基本上和SQL相同但是某些地方也是有很大的区别. 数据库操作 创建数据库 1.创建一个数据库,数据库在HDFS上的默认存储路径是/hive/warehouse/*.db。 create database hive01; 避免要创建的数据库已经存在错误&…

【零基础学机器学习 3】机器学习类型简介:监督学习 - 无监督学习 - 强化学习

机器学习是人工智能的一种应用,从大量数据中学习并解决特定问题。它使用计算机算法,通过经验自动提高效率。 机器学习主要有三种类型:监督、无监督和强化学习。 监督学习 概述 监督学习是一种使用标记数据来训练机器学习模型的机器学习类型。在标记数据中,输出已经是已知…

javascript-基础知识点总结

目录 &#xff08;一&#xff09;基础语法 1、javaScript引入方式 2、变量与常量 3、数据类型 typeof操作符 4、运算符 5、输出函数 6、类型转化 7、转移字符 8、注释 &#xff08;二&#xff09;流程控制 1、选择结构 switch 2、循环结构 for &#xff08;三&…

简单网络管理协议 SNMP

文章目录 1 概述1.1 结构1.2 操作 2 SNMP2.1 报文格式2.2 五大报文类型 3 扩展3.1 网工软考真题 1 概述 #mermaid-svg-95KMV1m3prKJgwv1 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-95KMV1m3prKJgwv1 .error-ico…

【云原生】K8sYaml文件详细

K8sYaml文件详细 一、Kubernetes支持的文件格式1、yaml和json的主要区别2、yaml语言格式 二、YAML操作1、查看API资源版本标签2、编写资源配置清单3、创建资源对象4、查看创建的pod5、创建service服务对外提供访问并测试6、创建资源对象7、测试访问 三、Kubernetes中的Port四、…

chatgpt赋能Python-new_init_python

新版Python中的__new__和__init__方法 Python作为一种高级编程语言&#xff0c;具有简单易学、代码可读性高和编写效率高等特点&#xff0c;因此越来越受到程序员们的青睐。其中&#xff0c;__new__和__init__方法是Python中的两个非常重要的方法&#xff0c;它们在实例化一个…

chatgpt赋能Python-matplotlib绘制雷达图

Matplotlib 绘制雷达图 在数据分析和可视化领域&#xff0c;雷达图是一种常用的图表类型。Matplotlib 是一个用于绘制 2D 图形的 Python 库&#xff0c;也可以用于绘制雷达图。本文将介绍如何使用 Matplotlib 绘制雷达图&#xff0c;包括数据准备、绘图方式和样式设置。 数据…

改进YOLOv5 | 在 C3 模块中添加【SE】【CBAM】【 ECA 】【CA】注意力机制 | 附详细结构图

文章目录 1. SE 注意力模块1.1 原理1.2 C3_SE 代码2. CBAM 注意力模块2.1 原理2.2 C3_CBAM 代码3. CA 注意力模块3.1 原理3.2 C3_CA 代码4. ECA 注意力模块4.1 原理4.2 C3_ECA 代码5. 添加方式💡6. C3_Attention 结构图 🍀7. 相关推荐🍀

Ada语言学习(1)Basic Knowledge

文章目录 说在前头命名注释数字变量变量类型signed integersEnumerationsFloating Points 类型重用&#xff08;继承&#xff09;类型转换 运算符属性&#xff08;Attributes&#xff09;练习 说在前头 本系列教程将会通过提问的方式来完成整个学习过程&#xff0c;因为当你能…

瑞吉外卖 - 公共字段自动填充功能(10)

某马瑞吉外卖单体架构项目完整开发文档&#xff0c;基于 Spring Boot 2.7.11 JDK 11。预计 5 月 20 日前更新完成&#xff0c;有需要的胖友记得一键三连&#xff0c;关注主页 “瑞吉外卖” 专栏获取最新文章。 相关资料&#xff1a;https://pan.baidu.com/s/1rO1Vytcp67mcw-PD…

每日一练 | 网络工程师软考真题 Day3

1、以下图的两种编码方案分别是 。 A&#xff0e;①差分曼彻斯特编码&#xff0c;②双相码 B&#xff0e;①NRZ编码&#xff0c;②差分曼彻斯特编码 C&#xff0e;①NRZ-I编码&#xff0c;②曼彻斯特编码 D&#xff0e;①极性码&#xff0c;②双极性码 2、假设模拟…

windows下安装emsdk

先从github上下载emsdk工程&#xff1a; # Get the emsdk repo git clone https://github.com/emscripten-core/emsdk.git# Enter that directory cd emsdkgit pull 更新到最新代码 clone 工程可以在 git bash here 命令行下&#xff1a; 执行下面的指令用cmd命令行&#xf…

chatgpt赋能Python-numpy_加速

介绍 NumPy是Python编程语言的数值计算扩展库。它包括一个强大的N维数组对象和用于处理这些数组的函数。NumPy是一个基于Python的免费开源软件项目&#xff0c;由社区驱动。这个库提供了一种高效的存储和处理大型矩阵数据的方式&#xff0c;同时也提供了非常高效的处理大量数据…