单片机的开发工作量,主要集中在两个地方,一是调通各种外设,二是实现产品功能。
像较高级的语言,比如c++/java/python等、因为将底层操作进行了封装,所以只需要集中关注第二点。事实上,越到后,底层越封装,上层应用开发就越简单,这也是软件开发这个领域甚至其他更多领域的必然发展趋势。
回顾下传统单片机软件开发方式:
芯片厂商提供数据手册、示例代码、开发环境;单片机软件工程师面向产品功能,查阅数据手册,参考官方示例代码进行开发;
硬件操作的方式是用C语言对寄存器进行直接读写以操作硬件。
在简单单片机(如51单片机)上这一套工作的很好,但是随着单片机变复杂就带来一些问题。比如我们要花费大量时间去研究数据手册,研究具体的时序,找出具体寄存器的地址,了解地址每一位所对应的含义等等,这不是不能办到,而是,底层的东西是相对固定的,我们可以将底层的这些操作进行封装,供上层调用。这也是分层设计的合理应用。不过,虽然封装会让使用起来更加简单,但是效率也会有所降低,封装的层数越多,效率就越低。这也是显而易见的,为了封装,必然要引入更多的代码,更多的资源。当然,总体来说利大于弊。
所以,STM32的标准库是什么?
其标准库全称为:标准外设库。就是将常用的外设操作(比如底层时序和上层时序等)进行封装。外设库简化了我们开发产品的两大工作量的第一个。外设库以源码方式提供,这个源码本身写的很标准,可以用作学习素材。
本质上,这些库和我们自己写的文件没有什么区别,只不过,因为太常用,而且相对固定,所以已经写好,提供给我们使用。该怎么编译怎么编译,该怎么链接还是怎么链接。
注意:
外设库只是帮助我们简化编程,简化的主要是劳动量。外设库一定程度上降低了编程难度,但是只会库、离了库就不会编程、库函数调用出了问题就束手无策,那是不可取的。
获取标准库
STM32固件库是官方推出来的对底层寄存器进行操作的函数库,编写程序时不用考虑怎么操作寄存器,只需要调用库函数就能实现对应功能。方便了使用STM32芯片进行开发的人员,使开发工作更简单快捷,对于代码来说可读性也更好。从ST官网。
地址如下:
STM32微控制器软件 - STMicroelectronics
现在是2022年8月,去官网找最新F1的标准外设库,显示已经下线,找不到了:
标准库是第一代固件库,实现的外设相对较少,封装上的效率相对较低,而且,随着网路技术和USB技术的快速发展,已经很难满足当代的需求。比如标准库没有网络功能,没有文件系统,没有GUI等等。
现在STM32的固件库有标准库,HAL库和LL库三种,官方现在主推HAL库,标准库不再更新(F1标准库好像干脆直接下架了),新学的话建议直接学习HAL库,配合STM32CubeMX的配置工具非常容易使用STM32。其不仅效率有所提升,而且新增了对网络使用等的协议。
为什么标准库不是通用的?而是需要对应到某种型号的单片机?
因为不同型号的单片机,在CPU设计上,以及外设定义、引脚定义等各方面都有所差异。所以,不可能存在通用的一套代码,只能说,尽可能地通用。
标准库目录结构
虽然当前已经不推荐使用标准库,但是为了更好地学习HAL库,还是有必要先学习一下标准库。虽然官网已经找不到F1的标准库,那只能去其他地方,比如百度,去找。
找到一个:STM32F10x_StdPeriph_Lib_V3.5.0
里面的目录如下:
接下来针对这些文件夹逐个介绍。
_htmresc
没啥用,就放了ST和CMSIS的LOGO图片。
CMSIS是啥?看LOGO上的标注可知它是ARM公司Cortex-M系列的接口标准。
全称为:Cortex Microcontroller Software Interface Standard,Cortex-M软件接口标准。
使用CMSIS,可以为处理器和外设实现一致且简单的软件接口,从而简化软件的重用、缩短微控制器新开发人员的学习过程,并缩短新设备的上市时间。软件的创建被嵌入式行业公认为主要成本系数。通过在所有Cortex-M 芯片供应商产品中标准化软件接口,这一成本会明显降低,尤其是在创建新项目或将现有软件迁移到新设备时。
CMSIS是ARM公司与多家不同的芯片和软件供应商一起紧密合作定义的,提供了内核与外设、实时操作系统和中间设备之间的通用接口。
详情参考百度百科:CMSIS_百度百科 (baidu.com)
Libraries
这是最重要的一个目录,该目录包含了库函数与启动文件等,是标准库的实体部分。
该文件夹下又有两个子文件夹:CMSIS和STM32F10x_StdPeriph_Driver
CMSIS子目录:
CMSIS子目录是STM32F10x的内核库目录,核心子目录为CM3,其余目录可忽略。
CM3下又有两个子目录:CoreSupport和DeviceSupport
CoreSupport子目录:
内有2个重要文件,一个是core_cm3.c(内核通用源文件),另一个是core_cm3.h(内核通用头文件)。上述文件位于CMSIS核心层的核内外设访问层,由ARM公司提供,包含用于访问内核寄存器的名称、地址定义等内容。DeviceSupport子目录:
\DeviceSupport\ST\STM32F10x
这些文件位于CMSIS核心层的设备外设访问层,由ST公司提供,包含片上核外设寄存器外称、地址定义、中断向量定义等。
startup:启动文件子目录,内包含4个子目录,其中arm子目录内存的都是根据FLASH容量大小所对应的启动文件;
stm32f10x.h:STM32F10x头文件;
system_stm32f10x.c:系统初始化源文件;
ststem_stm32f10x.h: 系统初始化头文件;
STM32F10x_StdPeriph_Driver子目录:
包含inc和src,显然,一个放的是头文件,一个放的是源文件,二者是一一对应的。
这个目录是干嘛的呢?看着里面文件名称。包含什么adc/bkp/can/dma/flash/gpio/rcc/spi/i2c等等。
明白了吗?我们说的外设库,就在这里被定义封装了。此目录是STM32F10x标准外设驱动库函数目录,包括了所有STM32F10x微控制器的外设驱动。
我们平常所说的驱动,就是把外设调通的一些程序代码。主要是实现这些外设的读写和控制。涉及到某个外设的底层及上层时序。
Project
此目录存放ST公司官方提供的STM32F10x外设驱动示例(STM32F10x_StdPeriph_Example)和工程模板(STM32F10x_StdPeriph_Template)。
外设驱动示例是在给你演示怎么使用这些外设:
工程模板是提供了不同开发工具下的工程,展示其一套工程目录的建立方式,具有一定的借鉴意义:
我们打开MDK的工程模板:MDK-ARM/Project.uvproj
显示的目录结构如下:
User放的是用户自己的代码;
StdPeriph_Driver放的是各种外设驱动;
CMSIS放的是系统初始化等内核代码;
STM32_EVAL暂时未知,好像是ARM官方的评估板,如果想了解详情,参考下文:初学者如何处理STM32创建工程时stm32_eval.h的问题_匠川的博客-CSDN博客_stm32_eval.h没加到工程;
MDK-ARM放的是启动代码;
Doc放的是相关文档。
不过实际工作中,我们并不会完全按照这个文件目录,仅作参考。
Utilities
里面放的好像就是评估板相关实现代码。
stm32f10x_stdperiph_lib_um,这个是外设库API文档,参考即可,一般直接看源码。
认识标准库
在标准库文件中,实际上可以只留下这几个目录,其他的可以全部删除,以方便用SI查看。
CM3
inc
src
core_cm3.c
位置:\Libraries\CMSIS\CM3\CoreSupport
该c文件是单片机的内核部分,共784行代码。那么,其中包含了哪些内容呢?
@brief CMSIS Cortex-M3 Core Peripheral Access Layer Source File
这里面定义的是内核相关的一些寄存器及其封装。
暂时不管。
stm32f10x.h(重要)
较为重要的一个头文件。
位置:\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x
这个文件有八千多行代码,很多有用的宏定义以及结构体定义等都在这里面,比如:
我们自己操作寄存器时,也要写很多寄存器地址的定义。比如:
在标准库的文件中,统一帮我们定义好了。但两者是一致的,并没有本质区别。
其他还有对启动代码的选择:
可以允许我们将所有的启动代码都放到工程中,然后通过此处来选择使用。
system_stm32f10x.c/system_stm32f10x.h(重要)
直接摘录源码说明:
* @brief CMSIS Cortex-M3 Device Peripheral Access Layer System Source File. * * 1. This file provides two functions and one global variable to be called from * user application: * - SystemInit(): Setups the system clock (System clock source, PLL Multiplier * factors, AHB/APBx prescalers and Flash settings). * This function is called at startup just after reset and * before branch to main program. This call is made inside * the "startup_stm32f10x_xx.s" file. * * - SystemCoreClock variable: Contains the core clock (HCLK), it can be used * by the user application to setup the SysTick * timer or configure other parameters. * * - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must * be called whenever the core clock is changed * during program execution. * * 2. After each device reset the HSI (8 MHz) is used as system clock source. * Then SystemInit() function is called, in "startup_stm32f10x_xx.s" file, to * configure the system clock before to branch to main program. * * 3. If the system clock source selected by user fails to startup, the SystemInit() * function will do nothing and HSI still used as system clock source. User can * add some code to deal with this issue inside the SetSysClock() function. * * 4. The default value of HSE crystal is set to 8 MHz (or 25 MHz, depedning on * the product used), refer to "HSE_VALUE" define in "stm32f10x.h" file. * When HSE is used as system clock source, directly or through PLL, and you * are using different crystal you have to adapt the HSE value to your own * configuration.
这里面主要是系统初始化的内容,其中关键是时钟的配置。
标准库对硬件的封装
外设是怎么被封装起来的,你好奇吗?
- 先定义好各个寄存器的地址;
- 然后进一步封装成结构体;
- 然后通过结构体访问元素的形式去给相关寄存器赋值。
使用结构体方式访问寄存器的原理
C语言访问寄存器的本质是C语言访问内存,本质思路是:定义一个指针(临时变量)指向这块内存,然后*p = xx这种方式去解引用指针从而向目标内存中写入内容。
缺陷:当寄存器多了之后每一个寄存器都要定义一套套路,很麻烦。
解决思路:就是打包,批发式的定义,用结构体(想一下为什么不用数组?)的方式进行打包。具体做法是:把整个一个模块的所有寄存器(地址是连接的)打包在一个结构体中,每个寄存器对应结构体中的一个元素,然后结构体基地址对应寄存器组的基地址,将来就可以通过结构体的各个元素来访问各个寄存器了。
结构体方式来访问寄存器和指针式访问寄存器,本质上其实是一样的,区别是C语言的封装不同。具体可以自行查看源码,如果有看不懂的,就结合数据手册和百度,总之,如果有需要,可以硬着头皮看下去。文章来源:https://www.toymoban.com/news/detail-497403.html
此处不赘述。文章来源地址https://www.toymoban.com/news/detail-497403.html
到了这里,关于STM32的标准库及其使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!