STM32的四种开发方式

这篇具有很好参考价值的文章主要介绍了STM32的四种开发方式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

STM32的四种开发方式

首先看下ST官方给出的四种开发方式的比较

STM32的四种开发方式

寄存器开发

寄存器编程对于从51等等芯片过渡过来的小伙伴并不陌生,不管你是什么库,最终操作的还是寄存器,所以对于标准库、HAL库、LL库都是在寄存器上的编程,所以可以直接在各种库中直接操作寄存器。

但寄存器开发方法到了STM32就变得不太容易行得通了,因为STM32的寄存器数量是51单片机的十数倍,如此多的寄存器根本无法全部记忆,开发时需要经常的翻查芯片的数据手册,此时直接操作寄存器就变得非常的费力了。但还是会有很小一部分人,喜欢去直接操作寄存器,因为这样更接近原理,知其然也知其所以然。

寄存器开发优缺点:
  1. 比其他库,直接操作寄存器来得更为直接、高效,省略了繁琐的调用和封装过程,但可移植性相对会降低一些。
  2. 同时对于芯片的熟练程度也是提出了要求,对于一款相对外设比较丰富的芯片需要花一定的时间阅读文档的等等,对开发人员有一定的门槛。
  3. 虽然开发起来相对来说比较慢一些,比较繁琐一些,但是接触的都是真正的底层内容,出了问题,我们也能从源头来快速分析解决问题,而且写的代码中省去了一些不必要的判断过程,执行效率会相对高一些,代码看起来也会清爽一些。

标准库开发

标准库全名叫标准外设库(Standard Peripheral Library),其实标准库所做的事情就是对寄存器进行了封装,形成了一套API函数供用户使用

因为STM32有非常多的寄存器,而导致了开发困难,所以为此ST公司就为每款芯片都编写了一份库文件,也就是工程文件里stm32F1xx…之类的。在这些 .c .h文件中,包括一些常用量的宏定义,把一些外设也通过结构体变量封装起来,如GPIO口时钟等。所以我们只需要配置结构体变量成员就可以修改外设的配置寄存器,从而选择不同的功能。用户不用去管寄存器到底是如何操作的,直接调用接口函数进行开发即可。

标准库在早期版本也叫固件函数库或简称固件库,不同人有不同的说法,知道这是同一个东西就行了

HAL库开发

HAL库是ST公司目前主力推的开发方式,全称就是Hardware Abstraction Layer(硬件抽象层)。

它的出现比标准库要晚,但其实和标准库一样,都是为了节省程序开发的时期,而且HAL库尤其的有效,如果说标准库把实现功能需要配置的寄存器集成了,那么HAL库的一些函数甚至可以做到某些特定功能的集成。也就是说,同样的功能,标准库可能要用几句话,HAL库只需用一句话就够了。

并且HAL库也很好的解决了程序移植的问题,不同型号的stm32芯片它的标准库是不一样的,例如在F4上开发的程序移植到F3上是不能通用的,而使用HAL库,只要使用的是相通的外设,程序基本可以完全复制粘贴,注意是相通外设,意思也就是不能无中生有,例如F7比F3要多几个定时器,不能明明没有这个定时器却非要配置,但其实这种情况不多,绝大多数都可以直接复制粘贴。

而且使用ST公司研发的 STM32CubeMx 软件,可以通过图形化的配置功能,直接生成整个使用HAL库的工程文件,可以说是方便至极,但是方便的同时也造成了它执行效率的低下。

HAL库与标准库的区别:https://www.elecfans.com/d/1814945.html(文章同时也介绍了1.句柄 2.MSP函数 3.Callback函数;对HAL库结构和移植使用都有介绍)

在前面的标准库中提到过,所有的API接口都是围绕着外设寄存器基地址,而对于HAL其围绕的是Handle,即一个外设句柄。

STM32的四种开发方式

UART_HandleTypeDef 结构体内容


/**
  * @brief  UART handle Structure definition
  */
typedef struct __UART_HandleTypeDef
{
  USART_TypeDef                 *Instance;        /*!< UART registers base address        */
  UART_InitTypeDef              Init;             /*!< UART communication parameters      */
  uint8_t                       *pTxBuffPtr;      /*!< Pointer to UART Tx transfer Buffer */
  uint16_t                      TxXferSize;       /*!< UART Tx Transfer size              */
  __IO uint16_t                 TxXferCount;      /*!< UART Tx Transfer Counter           */
  uint8_t                       *pRxBuffPtr;      /*!< Pointer to UART Rx transfer Buffer */
  uint16_t                      RxXferSize;       /*!< UART Rx Transfer size              */
  __IO uint16_t                 RxXferCount;      /*!< UART Rx Transfer Counter           */
  DMA_HandleTypeDef             *hdmatx;          /*!< UART Tx DMA Handle parameters      */
  DMA_HandleTypeDef             *hdmarx;          /*!< UART Rx DMA Handle parameters      */
  HAL_LockTypeDef               Lock;             /*!< Locking object                     */
 
  __IO HAL_UART_StateTypeDef    gState;           /*!< UART state information related to global Handle management
                                                       and also related to Tx operations.
                                                       This parameter can be a value of @ref HAL_UART_StateTypeDef */
 
  __IO HAL_UART_StateTypeDef    RxState;          /*!< UART state information related to Rx operations.
                                                       This parameter can be a value of @ref HAL_UART_StateTypeDef */
 
  __IO uint32_t                 ErrorCode;        /*!< UART Error code                    */
 
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
  void (* TxHalfCpltCallback)(struct __UART_HandleTypeDef *huart);        /*!< UART Tx Half Complete Callback        */
  void (* TxCpltCallback)(struct __UART_HandleTypeDef *huart);            /*!< UART Tx Complete Callback             */
  void (* RxHalfCpltCallback)(struct __UART_HandleTypeDef *huart);        /*!< UART Rx Half Complete Callback        */
  void (* RxCpltCallback)(struct __UART_HandleTypeDef *huart);            /*!< UART Rx Complete Callback             */
  void (* ErrorCallback)(struct __UART_HandleTypeDef *huart);             /*!< UART Error Callback                   */
  void (* AbortCpltCallback)(struct __UART_HandleTypeDef *huart);         /*!< UART Abort Complete Callback          */
  void (* AbortTransmitCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Abort Transmit Complete Callback */
  void (* AbortReceiveCpltCallback)(struct __UART_HandleTypeDef *huart);  /*!< UART Abort Receive Complete Callback  */
  void (* WakeupCallback)(struct __UART_HandleTypeDef *huart);            /*!< UART Wakeup Callback                  */
 
  void (* MspInitCallback)(struct __UART_HandleTypeDef *huart);           /*!< UART Msp Init callback                */
  void (* MspDeInitCallback)(struct __UART_HandleTypeDef *huart);         /*!< UART Msp DeInit callback              */
#endif  /* USE_HAL_UART_REGISTER_CALLBACKS */

} UART_HandleTypeDef;
  • 从UART_HandleTypedef结构体里面的内容来看,其中不仅仅包括了UART_Typedef寄存器基地址数据结构,同时还包含通信过程的数据结构,比如缓存区指针及大小等,以及各种初始化和中断回调函数等等用户型数据。

  • 那么HAL库所实现的API不再是简单的像标准库那样封装寄存器了,而是实现了一个外设对象的数据结构封装,这样对于所有的处理只需要传入这个handle对象句柄即可对该对象进行所有操作。

HAL库每个handle句柄结构体中都会存在MSP两个回调函数指针 xxCallback

STM32的四种开发方式

这算是HAL库设计不错的地方,MSP(MCU Specific Package)表示单片机的具体方案,是MCU相关的初始化处理。

在早期使用标准库学习stm32的过程中,很多小伙伴都会有这样的体会,经常性的忘记初始化引脚、初始化时钟、处理中断优先级等等错误,那么现在HAL库通过这两个回调函数在初始化过程中用来处理与当前外设功能相关性并不大的硬件配置,这样一方面起到了提醒的目的,另一方面也增强了可移植性。

  • 当然HAL库缺点也非常明显,较多的函数嵌套以及结构体索引,会导致占用更多的程序空间,并且效率上大打折扣。

  • 对于函数指针的大量使用,虽然可以带来代码的更好的扩展和复用,但是其对于MCU的RAM占用也是一笔资源。

  • 然而HAL库性能上的损失,从而让更多的项目趋向于使用高性能MCU

STM32 HAL库结构

STM32的四种开发方式

LL库开发

LL库是与HAL库打包发布的一部分库文件,其也叫底层库(Low-Layer),它也是对寄存器进行了封装,与标准库其实差别并不是很大。

STM32的四种开发方式

上图是LL库中的UART的相关实现,似乎函数声明部分该库中提供的API并没有标准库那么多,其实LL库中大部分API都是以static inline这样的内联函数形式进行定义的。

内联函数的优势在于不会像函数调用需要跳转到定义处等额外处理,而有点像宏定义一样直接展开并copy代码到执行位置,相对效率会提高很多,当然如果代码特别大,运行时间长,也就没有内联的必要了,所以其内联函数内部代码都相对比较短小。

如何进行选择

ST公司并没有强制要求一定要使用哪一种库来进行开发,所以对于库的选择还是要看个人需求,通过上面的这些描述,大家应该对这些库有了一个清晰的认识。

1、寄存器库算是所有库的根基,对于技术研发非常规范的公司会直接去封装寄存库,根据自己本公司的编码规范从而实现自身的一套API,这样一方面兼容已开发算法等应用库,同时有利于代码风格统一。虽然开发起来相对来说比较慢一些,比较繁琐一些,但是接触的都是真正的底层内容,出了问题,我们也能从源头来快速分析解决问题,而且写的代码中省去了一些不必要的判断过程,执行效率会相对高一些,代码看起来也会清爽一些。

2、标准库的话,用户可以直接使用,使用起来方便,快捷,开发速度快,相对起来也容易上手一点,但是对底层的寄存器操作原理了解不深,只知其一,不知其二,出了问题,解决起来比较麻烦。且官方的驱动库为了容错性高一些等原因,会引入一些判断机制,相对复杂一些,但是实际上有的东西是我们用不到的,这就会造成代码执行效率会相对低一些。

对于一些已使用标准库的老项目,如果技术到位的话,可以直接根据HAL或者LL库,进行版本上的升级和优化,从而降低产品开发风险和成本。

3、对于性能要求不高,且追求开发时效性,可以使用HAL与CubeMX工具结合开发,HAL 的结构更加容易整合 STM32CubeMx,而 STM32CubeMX 是 ST 这几年极力推荐的程序生成开发工具,bug再多应该也会得以修复。

使用 STM32CubeMx 开发,这个相比较来讲入门是最快的,不用接触那些库函数接口,也不用去理会那些寄存器操作,只要在图形化的界面上勾选一些选项就可以了,这简直是懒人必备神器,但是话说回来,这种开发方式,基本上接触单片机底层内容为0%,只要写应用程序就可以了,出了问题更难解决。如果换了个平台,没有这种方便的图形化配置工具,就有点不知所措了。

参考:

https://www.elecfans.com/d/1814945.html

https://www.freesion.com/article/11981446782/

https://zhuanlan.zhihu.com/p/385063476文章来源地址https://www.toymoban.com/news/detail-514612.html

到了这里,关于STM32的四种开发方式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • CSS中的四种定位方式

    在CSS中定位有以下4种: 静态定位 - static 相对定位 - relative 绝对定位 - absolute 固定定位 - fixed 静态定位是css中的默认定位方式,也就是没有定位。在此定位方式中设置:top,bottom,left,right,z-index 这些属性都是无效的。 相对位置前的位置: 相对位置后的位置: 可以看到该

    2024年02月08日
    浏览(89)
  • 记录-实现深拷贝的四种方式

    深拷贝:在堆内存中重新开辟一个存储空间,完全克隆一个一模一样的对象 浅拷贝:不在堆内存中重新开辟空间,只复制栈内存中的引用地址。本质上两个对象(数组)依然指向同一块存储空间 使用递归的方式进行对象(数组)的深拷贝 奉上已封装的深拷贝函数👇 上方函

    2023年04月21日
    浏览(41)
  • 单例模式的四种创建方式

    单例模式是日常开发中最常见的一种设计模式,常用来做为池对象,或者计数器之类的需要保证全局唯一的场景。 单例模式的目的是保证在整个程序中只存在一个对象实例,使用单例一个前提条件就是构造器私有化,不允许通过new 对象的方式。单例模式的实现主要方式有如

    2024年02月01日
    浏览(45)
  • C#对象的四种比较方式

    1.ReferenceEquals(object o1, object o2): 静态方法: 比较两个对象的引用,引用相同返回true,否则返回false,同为null是返回true; ReferenceEquals进行值类型比较时总是返回false,因为两个值类型需要分别装箱到对象中,是不同的引用 ; 从名称中便可知它用来比较两者是否是相同的引

    2024年02月16日
    浏览(34)
  • python导入库的四种方式

    目录 前言 一、import 库名 二、import 库名 as 别名(变量名) 三、from 库名 import 方法名 四、from 库名 import* 库可以抽象的理解为一个工具包,而库里的方法可以理解为工具包里各式各样的工具,每个工具作用不同。 此文例子库名为pygame,也就是工具包的名字,例子方法为ini

    2024年02月05日
    浏览(37)
  • gRpc的四种通信方式详细介绍

    🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍专栏》学会IDEA常用操作,工作效率翻倍~💐 🌊 《100天精通Golang(基础入门篇)》学会Golang语言

    2024年02月11日
    浏览(40)
  • C++文件读取的四种方式

    C++可以根据不同的目的来选取文件的读取方式,目前为止学习了C++中的四种文件读取方式。 C++文件读取的一般步骤: 1、包含头文件 #includefstream 2、创建流对象:ifstream ifs (这里的ifs是自己起的流对象名字) 3、打开文件:file.open(\\\"文件路径\\\",\\\"打开方式\\\"),打开文件后并判断文件是

    2024年02月11日
    浏览(40)
  • SpringBoot导出Excel的四种方式

           近期接到了一个小需求,要将系统中的数据导出为Excel,且能将Excel数据导入到系统。对于大多数研发人员来说,这算是一个最基本的操作了。但是……我居然有点方!         好多年没有实操这种基础的功能了。我对于excel导入导出的印象还停留在才入行时的工作经

    2024年02月03日
    浏览(41)
  • Java创建数组的四种方式

    1.使用默认值来初始化 语法: 数组元素类型 [] 数组名称 = new 数组元素类型 [数组长度] EG: int [] nums = new int [5]; //创建了一个类型为int,名字为nums ,长度为5的数组 2.先声明一个数组,再给值 语法: 数据元素类型 [] 数组名称; 数组名称 = new 数组元素类型[数组长度]; EG: int [] nums; num

    2024年02月09日
    浏览(63)
  • 【CSS】垂直居中的四种实现方式

    在 CSS 中,实现元素的垂直居中是一个常见的需求,但它的实现方法可以根据不同的布局需求和上下文环境而有所不同。对于初学者和专业的前端开发者来说,理解和掌握这些方法是非常重要的。以下是一些流行和高效的垂直居中技术: 使用 Flexbox 使用 Grid 使用定位和 Trans

    2024年01月23日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包