这里以stm32F1xx系列所用的标准库STM32F10x_StdPeriph_Lib_V3.5.0为例,使用hal库的开发者可以同理理解,相当于在标准库基础上继续封装了一层;
库函数是对MCU的内核和外设寄存器地址的抽象,结合芯片的手册为寄存器地址赋予形象的数据结构,并形成若干的C语言源文件和头文件以及汇编文件(也可称作SDK),其中的API函数会对寄存器地址进行操作(读和写);
整体使用了面向对象编程的封装思想,寄存器为属性,函数为操作方法;
STM32F10x_StdPeriph_Lib_V3.5.0包括如下文件和目录:
_htmresc/
Libraries/
Project/
Release_Notes.html
stm32f10x_stdperiph_lib_um.chm
Utilities/
主要介绍Libraries,其它目录可以自己了解
Libraries目录详细介绍
.
├── CMSIS
│ ├── CM3
│ │ ├── CoreSupport
│ │ │ ├── core_cm3.c//操作堆栈指针、优先级等寄存器的汇编封装
│ │ │ └── core_cm3.h//中断类型/NVIC/DEBUG/systick/ITM寄存器的地址和结构定义
│ │ └── DeviceSupport
│ │ └── ST
│ │ └── STM32F10x
│ │ ├── Release_Notes.html
│ │ ├── startup //使用不同编译器/IDE的情况下不同容量MCU启动文件选择
│ │ │ ├── arm //MDK-ARM toolchain
│ │ │ │ ├── startup_stm32f10x_cl.s
│ │ │ │ ├── startup_stm32f10x_hd.s
│ │ │ │ ├── startup_stm32f10x_hd_vl.s
│ │ │ │ ├── startup_stm32f10x_ld.s
│ │ │ │ ├── startup_stm32f10x_ld_vl.s
│ │ │ │ ├── startup_stm32f10x_md.s
│ │ │ │ ├── startup_stm32f10x_md_vl.s
│ │ │ │ └── startup_stm32f10x_xl.s
│ │ │ ├── gcc_ride7 //RIDE7 toolchain
│ │ │ │ ├── startup_stm32f10x_cl.s
│ │ │ │ ├── startup_stm32f10x_hd.s
│ │ │ │ ├── startup_stm32f10x_hd_vl.s
│ │ │ │ ├── startup_stm32f10x_ld.s
│ │ │ │ ├── startup_stm32f10x_ld_vl.s
│ │ │ │ ├── startup_stm32f10x_md.s
│ │ │ │ ├── startup_stm32f10x_md_vl.s
│ │ │ │ └── startup_stm32f10x_xl.s
│ │ │ ├── iar //EWARM toolchain
│ │ │ │ ├── startup_stm32f10x_cl.s
│ │ │ │ ├── startup_stm32f10x_hd.s
│ │ │ │ ├── startup_stm32f10x_hd_vl.s
│ │ │ │ ├── startup_stm32f10x_ld.s
│ │ │ │ ├── startup_stm32f10x_ld_vl.s
│ │ │ │ ├── startup_stm32f10x_md.s
│ │ │ │ ├── startup_stm32f10x_md_vl.s
│ │ │ │ └── startup_stm32f10x_xl.s
│ │ │ └── TrueSTUDIO //Atollic toolchain
│ │ │ ├── startup_stm32f10x_cl.s
│ │ │ ├── startup_stm32f10x_hd.s
│ │ │ ├── startup_stm32f10x_hd_vl.s
│ │ │ ├── startup_stm32f10x_ld.s
│ │ │ ├── startup_stm32f10x_ld_vl.s
│ │ │ ├── startup_stm32f10x_md.s
│ │ │ ├── startup_stm32f10x_md_vl.s
│ │ │ └── startup_stm32f10x_xl.s
│ │ ├── stm32f10x.h//主要定义外设寄存器的起始地址和各个外设的寄存器定义
│ │ ├── system_stm32f10x.c//设置MCU总线时钟,中断向量表位置的函数
│ │ └── system_stm32f10x.h
│ ├── CMSIS_changes.htm
│ ├── CMSIS debug support.htm
│ ├── Documentation
│ │ └── CMSIS_Core.htm
│ └── License.doc
└── STM32F10x_StdPeriph_Driver
├── inc //包括各个外设的头文件,对操作寄存器读写时一些配置的宏定义和函数声明
│ ├── misc.h
│ ├── stm32f10x_adc.h
│ ├── stm32f10x_bkp.h
│ ├── stm32f10x_can.h
│ ├── stm32f10x_cec.h
│ ├── stm32f10x_crc.h
│ ├── stm32f10x_dac.h
│ ├── stm32f10x_dbgmcu.h
│ ├── stm32f10x_dma.h
│ ├── stm32f10x_exti.h
│ ├── stm32f10x_flash.h
│ ├── stm32f10x_fsmc.h
│ ├── stm32f10x_gpio.h
│ ├── stm32f10x_i2c.h
│ ├── stm32f10x_iwdg.h
│ ├── stm32f10x_pwr.h
│ ├── stm32f10x_rcc.h
│ ├── stm32f10x_rtc.h
│ ├── stm32f10x_sdio.h
│ ├── stm32f10x_spi.h
│ ├── stm32f10x_tim.h
│ ├── stm32f10x_usart.h
│ └── stm32f10x_wwdg.h
├── Release_Notes.html
└── src //对操作寄存器读写时的函数定义
├── misc.c
├── stm32f10x_adc.c
├── stm32f10x_bkp.c
├── stm32f10x_can.c
├── stm32f10x_cec.c
├── stm32f10x_crc.c
├── stm32f10x_dac.c
├── stm32f10x_dbgmcu.c
├── stm32f10x_dma.c
├── stm32f10x_exti.c
├── stm32f10x_flash.c
├── stm32f10x_fsmc.c
├── stm32f10x_gpio.c
├── stm32f10x_i2c.c
├── stm32f10x_iwdg.c
├── stm32f10x_pwr.c
├── stm32f10x_rcc.c
├── stm32f10x_rtc.c
├── stm32f10x_sdio.c
├── stm32f10x_spi.c
├── stm32f10x_tim.c
├── stm32f10x_usart.c
└── stm32f10x_wwdg.c
对单片机或者MCU的控制本质来说是对寄存器的读写,寄存器从功能上大致可以分为三类:配置、数据、状态,配置寄存器一般用于初始化且大多数情况配置一次,数据寄存器经常性的被访问读或者写输出/获取外设与外部器件、内部内存、寄存器之间的数据交换,状态寄存器用于外设工作状态的查询或者被异步中断置位;
随着MCU的功能越来越强,寄存器位宽也从最初的几bit到32bit、64bit、128bit,每个寄存器的每个bit位都有自己的含义,当我们需要操作实现某个功能的时候需要知道要操作哪些寄存器和寄存器中的哪些位;
从以上可知,如果只是给你了外设寄存器地址和主控芯片的数据手册,会像当初操作51单片机类似去自己查寄存器配置,这是一个效率比较低的过程,所以ST为我们提供了标准外设库方便了我们的开发;
以GPIOA外设的设计为例:
1、寄存器地址结构化
见stm32f10x.h头文件
//GPIOA的寄存器地址
#define PERIPH_BASE ((uint32_t)0x40000000)
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
//芯片手册中描述的GPIO寄存器定义
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
} GPIO_TypeDef;
//用类型对地址强制转换
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
//对以上的寄存器操作定义了函数接口
void GPIO_DeInit(GPIO_TypeDef* GPIOx);
void GPIO_AFIODeInit(void);
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState);
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);
以上对GPIOA的外设库函数介绍,可以看出对外设寄存器和寄存器操作的封装借鉴了面向对象编程的思想,显得整齐且可读性强;以此外设为例去理解其它外设的库函数设计就更易理解了;
1、虽然库函数给我们的编程提供了便利,但是当我们从初学者对库函数有个一定认识之后,可以多深入库函数的内部看下它的内部实现和原理,可以学习下库函数的编写声明定义的写法规范性和严谨性,对于自己以后写代码开发有启发意义;
2、对外设的操作不是孤立的,比如对GPIO的操作不仅需要设置GPIO的寄存器,也要设置GPIO所在总线的相关配置,当需要将GPIO做其它模式比如ADC功能时也要结合ADC外设,当需要做中断时要结合NVIC,要以联系的思路去认识某个外设;
3、对于各个外设的学习要全面,比如GPIO除了基本的写高写低输入高低外它还有事件输出功能,ADC除了基本的轮训方式还有结合DMA的采集方式,当你将一个外设学习的透彻时是不是感觉很酷,当你对它了如指掌时可能会有更多创新性的灵活运用它的方式;