一、概述

1、调用STM32库函数配置与直接配置寄存器

① 直接配置寄存器

  使用过51单片机的朋友都知道为了将IO口配置成某种特殊功能或者配置中断控制,我们先将需要如下步骤:  

根据需要配置功能计算值---->直接写入寄存器

② 调用STM32库函数配置

  定义一个配置结构体变量---->根据配置功能依次给这个结构体变量的成员赋值----->将结构体变量的地址传入库函数,并调用STM32库函数实现配置

  其中最后一个步骤的"调用STM32库函数",实际上就是将配置结果写入寄存器。

  前两个步骤是以一种高级的形式(将结构体变量的成员和它的值以利于人理解的方式定义)将我们希望的配置参数写入结构体变量(内存)中。然后再将将结构体变量的地址传入库函数(这些参数就传给STM32库函数),STM32库函数根据设置参数计算出要写入寄存器的值,最后写入到寄存器中,完成整个配置。

③ 调用STM32库函数配置与直接配置寄存器的对比

  直接配置寄存器需要计算写入值(人做的),然后直接写入。代码量要小,执行时间少,效率高,但是辛苦了人。

  调用STM32库函数配置,计算写入值以及写入寄存器都是库函数完成的,而人就像一个领导通过结构体告诉库函数我要怎样配置。可见人的工作量大大减小了,显然辛苦了机器,不过CPU速度很高,咱们不用担心。

2、STM32库函数实现的组成

  STM32定义了配置需要的结构体,结构体变量都是见名知意的,而且用枚举定义了许多新的类型。枚举方法适合变量值比较少的情况,分别给每个值一个名字,便于用户识别和赋值。

3、声明

  以下阐述以配置GPIO实现流水灯程序为例进行说明,测试库函数是GPIO_Init(),

  硬件电路如下:

二、结构体

  GPIO_Init库函数目的就是对GPIOx设置成我们需要的参数值,参数值保存在GPIO_InitStruct。其中涉及到两个结构体,来看一下。

1、GPIO_TypeDef

  定义是在stm32f10x.h中

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;

  看一下怎么利用这个结构体类型是怎么访问寄存器CRL、CRH?

#define PERIPH_BASE           ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)

  GPIOC最终的值是0x40000000+0x10000+0x1000,也就是0x4001100,而且注意这个值不是int类型,而是一个指向GPIO_TypeDef结构体类型的常量指针。

访问方法一:

  通过定义一个值等于GPIOC的指针变量,就可以访问到端口GPIOC的每一个寄存器。

GPIO_TypeDef * GPIOx = GPIOC;
GPIOx->CRH = 0xffffffff;

访问方法二:

GPIOC->CRH = 0xffffffff;

对应的反汇编代码:

0x0800037C F04F30FF  MOV      r0,#0xFFFFFFFF
0x08000380 LDR r1,[pc,#] ; @0x0800038C
0x08000382 STR r0,[r1,#0x04]
0x08000384 E7F0
0x08000386
0x08000388 0C14
0x0800038A
0x0800038C ASRS r0,r0,#
0x0800038E ANDS r1,r1,r0

  可见GPIOC是一个常量结构体指针,而它指向的结构体成员CRH的地址因此也是个常量0x4001 1004(编译器可以计算出来)。因此可以通过这个地址直接访问结构体POTRC的成员CRH。

  我认为这句话实际上等于:

*((volatile unsigned int *) 0x40011004) = 0xffffffff;

2、GPIO_InitTypeDef 

  定义在stm32f10x_gpio.h中

typedef struct
{
uint16_t GPIO_Pin; /*!< Specifies the GPIO pins to be configured.
This parameter can be any value of @ref GPIO_pins_define */ GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins.
This parameter can be a value of @ref GPIOSpeed_TypeDef */ GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins.
This parameter can be a value of @ref GPIOMode_TypeDef */
}GPIO_InitTypeDef;

  初始化GPIO的结构体类型--GPIO_InitTypeDef有三个成员,需要设置的引脚GPIO_Pin,需要设置的引脚速度GPIO_Speed,需要设置的引脚工作模式GPIO_Mode。这三个成员包含了配置GPIO的所有信息,而且这三个成员都是见名知意。当然不可能直接将这三个成员写入到配置寄存器中,需要转化成对应的寄存器值才能写入,这就是GPIO_Init所要干的活。

① GPIO_Pin

  GPIO_Pin的类型是uint16_t,那么它的取值表含义是什么呢?

#define GPIO_Pin_0                 ((uint16_t)0x0001)  /*!< Pin 0 selected */
#define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */
#define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */
#define GPIO_Pin_3 ((uint16_t)0x0008) /*!< Pin 3 selected */
#define GPIO_Pin_4 ((uint16_t)0x0010) /*!< Pin 4 selected */
#define GPIO_Pin_5 ((uint16_t)0x0020) /*!< Pin 5 selected */
#define GPIO_Pin_6 ((uint16_t)0x0040) /*!< Pin 6 selected */
#define GPIO_Pin_7 ((uint16_t)0x0080) /*!< Pin 7 selected */
#define GPIO_Pin_8 ((uint16_t)0x0100) /*!< Pin 8 selected */
#define GPIO_Pin_9 ((uint16_t)0x0200) /*!< Pin 9 selected */
#define GPIO_Pin_10 ((uint16_t)0x0400) /*!< Pin 10 selected */
#define GPIO_Pin_11 ((uint16_t)0x0800) /*!< Pin 11 selected */
#define GPIO_Pin_12 ((uint16_t)0x1000) /*!< Pin 12 selected */
#define GPIO_Pin_13 ((uint16_t)0x2000) /*!< Pin 13 selected */
#define GPIO_Pin_14 ((uint16_t)0x4000) /*!< Pin 14 selected */
#define GPIO_Pin_15 ((uint16_t)0x8000) /*!< Pin 15 selected */
#define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< All pins selected */

  选中某一位(置1),就意味着要配置这一位。

② GPIO_Speed

  它的类型是一个枚举类型--GPIOSpeed_TypeDef

typedef enum
{
GPIO_Speed_10MHz = ,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;

  可见GPIO_Speed只有三种取值,这三种取值无疑是见名知意的。GPIO_Speed_10MHz = 1,编译器自动的会为剩余两种取值以合适的数字值取代:GPIO_Speed_2MHz=2,GPIO_Speed_50MHz=3。

  用枚举类型定义取值较少的类型带来的好处:

  • 见名知意
  • 便于检查

③ GPIO_Mode

  它的类型也是一个枚举类型--  GPIOMode_TypeDef

typedef enum
{ GPIO_Mode_AIN = 0x0,
GPIO_Mode_IN_FLOATING = 0x04,
GPIO_Mode_IPD = 0x28,
GPIO_Mode_IPU = 0x48,
GPIO_Mode_Out_OD = 0x14,
GPIO_Mode_Out_PP = 0x10,
GPIO_Mode_AF_OD = 0x1C,
GPIO_Mode_AF_PP = 0x18
}GPIOMode_TypeDef;

  这里人为的给每一种类型值都设置了数字值,其目的肯定是为了以后根据这些数字值便于得到写入寄存器的值,也就是便于GPIO_Init函数根据这些值得到对应配置寄存器的值。

三、库函数

  GPIO_Init函数的功能是配置GPIOx,实际上就是配置GPIOx的六个寄存器。

  __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;

  函数代码如下:

/**
* @brief Initializes the GPIOx peripheral according to the specified
* parameters in the GPIO_InitStruct.
* @param GPIOx: where x can be (A..G) to select the GPIO peripheral.
* @param GPIO_InitStruct: pointer to a GPIO_InitTypeDef structure that
* contains the configuration information for the specified GPIO peripheral.
* @retval None
*/
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
uint32_t tmpreg = 0x00, pinmask = 0x00;
/* Check the parameters */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin)); /*---------------------------- GPIO Mode Configuration -----------------------*/
currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
{
/* Check the parameters */
assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
/* Output mode */
currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
}
/*---------------------------- GPIO CRL Configuration ------------------------*/
/* Configure the eight low port pins */
if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
{
tmpreg = GPIOx->CRL;
for (pinpos = 0x00; pinpos < 0x08; pinpos++)
{
pos = ((uint32_t)0x01) << pinpos;
/* Get the port pins position */
currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
if (currentpin == pos)
{
pos = pinpos << ;
/* Clear the corresponding low control register bits */
pinmask = ((uint32_t)0x0F) << pos;
tmpreg &= ~pinmask;
/* Write the mode configuration in the corresponding bits */
tmpreg |= (currentmode << pos);
/* Reset the corresponding ODR bit */
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
{
GPIOx->BRR = (((uint32_t)0x01) << pinpos);
}
else
{
/* Set the corresponding ODR bit */
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
{
GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
}
}
}
}
GPIOx->CRL = tmpreg;
}
/*---------------------------- GPIO CRH Configuration ------------------------*/
/* Configure the eight high port pins */
if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
{
tmpreg = GPIOx->CRH;
for (pinpos = 0x00; pinpos < 0x08; pinpos++)
{
pos = (((uint32_t)0x01) << (pinpos + 0x08));
/* Get the port pins position */
currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
if (currentpin == pos)
{
pos = pinpos << ;
/* Clear the corresponding high control register bits */
pinmask = ((uint32_t)0x0F) << pos;
tmpreg &= ~pinmask;
/* Write the mode configuration in the corresponding bits */
tmpreg |= (currentmode << pos);
/* Reset the corresponding ODR bit */
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
{
GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
}
/* Set the corresponding ODR bit */
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
{
GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
}
}
}
GPIOx->CRH = tmpreg;
}
}

  其中的英文注释已经写的非常清晰,不在多余的注释了,主要的是列出它的流程图:

四、总结库函数的实现

  STM32定义了许多结构体、宏、枚举类型,给用户提供了见名知意的设置参数方法。

  STM32库函数根据这些设置参数计算出要写入寄存器的值,最后写入到寄存器中,完成配置。

参考资料:《STM32库开发实战指南》

STM32库函数实现方法的更多相关文章

  1. STM32库函数void USART_SendData的缺陷和解决方法

    void USART_SendData()函数在快速发送时存在问题 有丢数据的可能 转自https://blog.csdn.net/qq_27114397/article/details/506015 ...

  2. 如何快速上手使用STM32库函数

    一.背景 如前文所述,利用标准库函数的好处在于,可以快速开发,不用去对着数据手册,小心翼翼的一位一位的配置那些繁复的寄存器,因为这些工作意法半导体已经找了一些顶级的工程师帮你做了,杰作既是其库函数.当 ...

  3. STM32库函数编程、Keli/MDK、stm32f103zet6

    catalogue . Cortex-M3地址空间 . 基于标准外设库的软件开发 . 基于固件库实现串口输出(发送)程序 . 红外接收实验 . 深入分析流水灯例程 . GPIO再举例之按键实验 . 串 ...

  4. STM32库函数开发使用总结

    一.外设常具备的几类寄存器 控制寄存器xxx_CR (Control/Configuration Register): 用来配置.控制响应外设的工作方式,如GPIOx_CRL.AFIO_EXTICR1 ...

  5. STM32时钟配置方法详解

      一.在STM32中,有五个时钟源,为HSI.HSE.LSI.LSE.PLL. ①HSI是高速内部时钟,RC振荡器,频率为8MHz. ②HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源, ...

  6. STM32时钟配置方法

    一.在STM32中,有五个时钟源,为HSI.HSE.LSI.LSE.PLL. ①HSI是高速内部时钟,RC振荡器,频率为8MHz. ②HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率 ...

  7. stm32库函数建工程和使用Keil自带库建工程有没有区别?发现了同样的程序在两种情况下keil自带库可以运行的情况,不知是什么原因

    我使用库函数建的工程(非Keil自带库),为了实现SPI对Si24r1芯片数据的读写,以验证stm32是否可以和si24r1能够正常通信,发现使用库函数建的工程程序不能通过,读出来的数据和写的数据不一 ...

  8. 常用的stm32库函数

    //初始化的方式:先定义初始化机构体.再打开时钟使能.在对每一组GPIO口进行初始化. GPIO_InitTypeDef LED_GPIO; RCC_APB2PeriphClockCmd(RCC_AP ...

  9. 对STM32库函数中 assert 函数的认知

    > 本段代码取自 <stm32f4xx_gpio.c> > 可以看出进入函数第一件事就是做 assert 输入参数检查,参数合法后,根据参数做相应操作 /** * @brief ...

随机推荐

  1. VisJS 随机图

    <!doctype html> <html> <head> <title>Random nodes</title> <style ty ...

  2. cocos2dx jsb 在IOS与安卓下的一些不同之处

    cocos2dx版本 2.1.4 1:字体大小 cocosbuilder publish后应该注意IOS和安卓目录下的字体文件的规格是否齐全: 2:cc.LabelTTF 在ios下,cc.Label ...

  3. 嵌入式Linux-objdump命令的使用

    objdump命令的使用 objdump命令是Linux下的反汇编目标文件或者可执行文件的命令,它还有其他作用,下面以ELF格式可执行文件test为例详细介绍: objdump -f test 显示t ...

  4. iOS开发简单高效的数据存储

    在iOS开发过程中,不管是做什么应用,都会碰到数据保存的问题,你是用什么方法来持久保存数据的?这是在几乎每一次关于iOS技术的交流或讨论都会被提到的问题,而且大家对这个问题的热情持续高涨.本文主要从概 ...

  5. 测试浏览器最多能包含Cookie的个数和Cookie中包含的最多内容

    public class CookieTestServlet extends HttpServlet { public void doGet(HttpServletRequest request, H ...

  6. HTML左边和右边是固定的宽度但是中间是自动的布局方式

    对于这个布局方式我们可以是用绝对定位的方式来实现这个效果 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN&q ...

  7. 深入理解计算机系统第二版习题解答CSAPP 2.9

    基于三元色R(红)G(绿)B(蓝)关闭(0)和打开(1),能够创建8种不同的颜色,如下: R G B 颜色 R G B 颜色 0 0 0 黑色 1 0 0 红色 0 0 1 蓝色 1 0 1 红紫色 ...

  8. 简单的实现QQ通信功能(五)

    第五部分:聊天界面的设计及代码 一:效果图及界面设计 1. 效果图: 2. 界面设计: (1)左上角显示朋友的头像和“某某正在和某某聊天”. (2)中间的聊天窗口用了一个ListView,视图用详细信 ...

  9. Mac下sublime text 的“package control”安装

    小伙伴们好,我根据昨晚的经历写一个小总结:关于“Mac下sublime text 的“package control”安装”.本来安装package control是一个无比简单的事情,即使是初次使用 ...

  10. C# DataTable转List And List转DataTable

    // DataTable转List: IList<HousesEntity> Ilist = TableAndList.ConvertTo<HousesEntity>(dt); ...