267022|2181

190

帖子

0

TA的资源

五彩晶圆(初级)

楼主
 

献给新手:解析STM32的库函数 [复制链接]

 

意法半导体在推出STM32微控制器之初,也同时提供了一套完整细致的固件开发包,里面包含了在STM32开发过程中所涉及到的所有底层操作。通过在程序开发中引入这样的固件开发包,可以使开发人员从复杂冗余的底层寄存器操作中解放出来,将精力专注应用程序的开发上,这便是ST推出这样一个开发包的初衷。

但这对于许多从51/AVR这类单片机的开发转到STM32平台的开发人员来说,势必有一个不适应的过程。因为程序开发不再是从寄存器层次起始,而要首先去熟悉STM32所提供的固件库。那是否一定要使用固件库呢?当然不是。但STM32微控制器的寄存器规模可不是常见的8位单片机可以比拟,若自己细细琢磨各个寄存器的意义,必然会消耗相当的时间,并且对于程序后续的维护,升级来说也会增加资源的消耗。对于当前“时间就是金钱”的行业竞争环境,无疑使用库函数进行STM32的产品开发是更好的选择。本文将通过一个简单的例子对STM32的库函数做一个简单的剖析。

以最常用的GPIO设备的初始化函数为例,如下程序段一:

GPIO_InitTypeDef GPIO_InitStructure;                                                                                                         1

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;                                                                                              2

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                                                                        3

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;                                                                        4

GPIO_Init(GPIOA , &GPIO_InitStructure);                                                                                                   5

这是一个在STM32的程序开发中经常使用到的GPIO初始化程序段,其功能是将GPIOA.4口初始化为推挽输出状态,并最大翻转速率为50MHz。下面逐一分解:

l  首先是1,该语句显然定义了一个GPIO_InitTypeDef类型的变量,名为GPIO_InitStructure,则找出GPIO_InitTypeDef的原型位于“stm32f10x_gpio.h”文件,原型如下:

typedef struct

{

u16 GPIO_Pin;

GPIOSpeed_TypeDef GPIO_Speed;

GPIOMode_TypeDef GPIO_Mode;

}GPIO_InitTypeDef;

游客,如果您要查看本帖隐藏内容请回复

[ 本帖最后由 losingamong 于 2011-5-31 22:23 编辑 ]
此帖出自stm32/stm8论坛

最新回复

感谢分享,认真学习     详情 回复 发表于 2024-12-26 07:55

点评

回复查看:tongue: :tongue: :tongue:  详情 回复 发表于 2013-11-5 00:20
多谢分享!学习  详情 回复 发表于 2013-11-4 14:09
学习了  详情 回复 发表于 2013-10-12 11:39
为嘛回复依然看不到PDF呢  详情 回复 发表于 2013-9-27 11:25
正好学习下  详情 回复 发表于 2013-9-26 20:16
点赞 关注(12)
 

回复
举报

190

帖子

0

TA的资源

五彩晶圆(初级)

推荐
 
由此可知GPIO_InitTypeDef是一个结构体类型同义字,其功能是定义一个结构体,该结构体有三个成员分别是u16类型的GPIO_Pin、GPIOSpeed_TypeDef 类型的GPIO_Speed和GPIOMode_TypeDef 类型的GPIO_Mode。继续探查GPIOSpeed_TypeDef和GPIOMode_TypeDef类型,在“stm32f10x_gpio.h”文件中找到对GPIOSpeed_TypeDef的定义:
        typedef enum
{
                  GPIO_Speed_10MHz = 1,
                  GPIO_Speed_2MHz,
                  GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
则可知GPIOSpeed_TypeDef枚举类型同一只,其功能是定义一个枚举类型变量,该变量可表示GPIO_Speed_10MHz、GPIO_Speed_2MHz和GPIO_Speed_50MHz三个含义(其中GPIO_Speed_10MHz已经定义为1,读者必须知道GPIO_Speed_2MHz则依次被编译器赋予2,而GPIO_Speed_50MHz为3)。
同样也在“stm32f10x_gpio.h”文件中找到对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_Mode_AIN、GPIO_Mode_AF_OD等(也可以轻易判断出这表示GPIO设备的工作模式)。
至此对程序段一的○1解析可以做一个总结:
该行定义一个结构体类型的变量GPIO_InitStructure,并且该结构体有3个成员,分别为GPIO_Pin、GPIO_Speed和GPIO_Mode,并且GPIO_Pin表示GPIO设备引脚GPIO_Speed表示GPIO设备速率和GPIO_Mode表示GPIO设备工作模式。

接下来是2,此句是一个赋值语句,把GPIO_Pin_4赋给GPIO_InitStructure结构体中的成员GPIO_Pin,可以在“stm32f10x_gpio.h”文件中找到对GPIO_Pin_4做的宏定义:
#define GPIO_Pin_4                ((u16)0x0010)
        因此○2的本质是将16位数0x0010赋给GPIO_InitStructure结构体中的成员GPIO_Pin。
3语句和2相似将GPIO_Speed_50MHz赋给GPIO_InitStructure结构体中的成员GPIO_Speed,但注意到此处GPIO_Speed_50MHz只是一个枚举变量,并非具体的某个值。
4语句亦和2语句类似,把GPIO_Mode_Out_PP赋给GPIO_InitStructure结构体中的成员GPIO_Mode,从上文可知GPIO_Mode_Out_PP的值为0x10。
5是一个函数调用,即调用GPIO_Init函数,并提供给该函数2个参数,分别为GPIOA和&GPIO_InitStructure,其中&GPIO_InitStructure表示结构体变量GPIO_InitStructure的地址,而GPIOA则在“stm32f10x_map.h”文件中找到定义:

#ifdef _GPIOA
#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)
#endif
此三行代码是一个预编译结构,首先判断是否定义了宏_GPIOA。可以在“stm32f10x_conf.h”中发现对_GPIOA的定义为:
#define _GPIOA
这表示编译器会将代码中出现的GPIOA全部替换为((GPIO_TypeDef *) GPIOA_BASE)。从该句的C语言语法可以判断出((GPIO_TypeDef *) GPIOA_BASE)的功能为将GPIOA_BASE强制类型转换为指向GPIO_TypeDef类型的结构体变量。如此则需要找出GPIOA_BASE的含义,依次在“stm32f10x_map.h”文件中找到:
#define GPIOA_BASE            (APB2PERIPH_BASE + 0x0800)
和:
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
还有:
#define PERIPH_BASE           ((u32)0x40000000)
明显GPIOA_BASE表示一个地址,通过将以上3个宏展开可以得到:

GPIOA_BASE = 0x40000000 + 0x10000 + 0x0800
此处的关键便在于0x40000000、0x10000和0x0800这三个数值的来历。读者应该通过宏名猜到了,这就是STM32微控制器的GPIOA的设备地址。通过查阅STM32微控制器开发手册可以得知,STM32的外设起始基地址为0x40000000,而APB2总线设备起始地址相对于外设基地址的偏移量为0x10000,GPIOA设备相对于APB2总线设备起始地址偏移量为0x0800。
对○5句代码进行一个总结:调用GPIO_Init函数,并将STM32微控制器的GPIOA设备地址和所定义的结构体变量GPIO_InitStructure的地址传入。
        以上是对GPIOA初始化库函数的剖析,现继续转移到函数内部分析,GPIO_Init函数原型如程序段二:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
        u32 currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
        u32 tmpreg = 0x00, pinmask = 0x00;
       
        /* 检查参数是否正确 */
        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));  
       
        /* 将工作模式暂存至 currentmode 变量中 */
        currentmode = ((u32)GPIO_InitStruct->GPIO_Mode) & ((u32)0x0F);
        /* 如果欲设置为任意一种输出模式,则再检查”翻转速率“参数是否正确 */
        if ((((u32)GPIO_InitStruct->GPIO_Mode) & ((u32)0x10)) != 0x00)
        {
                assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
                currentmode |= (u32)GPIO_InitStruct->GPIO_Speed;
        }
       
        /* 设置低八位引脚(即 pin0 ~ pin7) */
        if (((u32)GPIO_InitStruct->GPIO_Pin & ((u32)0x00FF)) != 0x00)
        {
                /* 读出当前配置字 */
                tmpreg = GPIOx->CRL;       
                for (pinpos = 0x00; pinpos < 0x08; pinpos++)
                {
                        /* 获取将要配置的引脚号 */
                        pos = ((u32)0x01) << pinpos;
                        currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
                        if (currentpin == pos)
                        {
                                /* 先清除对应引脚的配置字 */
                                pos = pinpos << 2;
                                pinmask = ((u32)0x0F) << pos;

                                tmpreg &= ~pinmask;
                                /* 写入新的配置字 */
                                tmpreg |= (currentmode << pos);
                                /* 若欲配置为上拉 / 下拉输入,则需要配置 BRR 和 BSRR寄存器 */
                                if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
                                {
                                        GPIOx->BRR = (((u32)0x01) << pinpos);
                                }
                                else
                                {
                                        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
                                        {
                                                GPIOx->BSRR = (((u32)0x01) << pinpos);
                                        }
                                }
                        }
                }
                /* 写入低八位引脚配置字 */
                GPIOx->CRL = tmpreg;
        }
       
        /* 设置高八位引脚(即 pin8 ~ pin15),流程和第八位引脚配置流程一致,不再作解析 */
        if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
        {
                tmpreg = GPIOx->CRH;
                for (pinpos = 0x00; pinpos < 0x08; pinpos++)
                {
                        pos = (((u32)0x01) << (pinpos + 0x08));
                        currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
                        if (currentpin == pos)
                        {
                                pos = pinpos << 2;
                                pinmask = ((u32)0x0F) << pos;
                                tmpreg &= ~pinmask;
                                tmpreg |= (currentmode << pos);
                                if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
                                {
                                        GPIOx->BRR = (((u32)0x01) << (pinpos + 0x08));
                                }
                                if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
                                {
                                        GPIOx->BSRR = (((u32)0x01) << (pinpos + 0x08));
                                }
                        }
                }
                GPIOx->CRH = tmpreg;
        }
}
        这段程序的流程是:首先检查由结构体变量GPIO_InitStructure所传入的参数是否正确,然后对GPIO寄存器进行“保存——修改——写入”的操作,完成对GPIO设备的设置工作。显然,结构体变量GPIO_InitStructure所传入参数的目的是设置对应GPIO设备的寄存器。而STM32的参考手册对关于GPIO设备的设置寄存器的描述如下图1和表1(仅列出低八位引脚寄存器描述,高八位引脚类同):

该寄存器为32位,其中分为8份,每份4位,对应低八位引脚的设置。每一个引脚的设置字分为两部分,分别为CNF和MODE,各占两位空间。当MODE的设置字为0时,表示将对应引脚配置为输入模式,反之设置为输出模式,并有最大翻转速率限制。而当引脚配置为输出模式时,CNF配置字则决定引脚以哪种输出方式工作(通用推挽输出、通用开漏输出等)。通过对程序的阅读和分析不难发现,本文最初程序段中GPIO_InitStructure所传入参数的对寄存器的作用如下:
1、GPIO_Pin_4被宏替换为0x0010,对应图1可看出为用于选择配置GPIOx_CRL的[19:16]位,分别为CNF4[1:0]、MODE4[1:0]。
2、GPIO_Speed_50MHz为枚举类型,包含值0x03,被用于将GPIOx_CRL位中的MODE4[1:0]配置为b11(此处b意指二进制)。
3、GPIO_Mode亦为枚举类型,包含值0x10,被用于将GPIOx_CRL位中的MODE4[1:0]配置为b00。事实上GPIO_Mode的值直接影响寄存器的只有低四位,而高四位的作用可以从程序段二中看出,是用于判断此参数是否用于GPIO引脚输出模式的配置。
至此应不难知道STM32的固件库最后是怎样影响最底层的寄存器的。总结起来就是:固件库首先将各个设备所有寄存器的配置字进行预先定义,然后封装在结构或枚举变量中,待用户调用对应的固件库函数时,会根据用户传入的参数从这些封装好的结构或枚举变量中取出对应的配置字,最后写入寄存器中,完成对底层寄存器的配置。
可以看到,STM32的固件库函数对于程序开发人员来说是十分便利的存在,只需要填写言简意赅的参数就可以在完全不关心底层寄存器的前提下完成相关寄存器的配置,具有相当不错的通用性和易用性,也采取了一定措施保证库函数的安全性(主要引入了参数检查函数assert_param)。但同时也应该知道,通用性、易用性和安全性的代价是加大了代码量,同时增加了一些逻辑判断代码造成了一定的时间消耗,在对时间要求比较苛刻的应用场合需要评估使用固件库函数对程序运行时间所带来的影响。读者在使用STM32的固件库函数进行程序开发时,应该意识到这些问题。

[ 本帖最后由 losingamong 于 2011-5-31 22:17 编辑 ]
此帖出自stm32/stm8论坛

点评

  太感谢了,一直不清楚到底是怎么回事。现在明白了相见恨晚~~  详情 回复 发表于 2019-8-16 08:57
太感谢了,一直不清楚到底是怎么回事。现在明白了  详情 回复 发表于 2013-7-23 16:31
Look,look  详情 回复 发表于 2013-6-11 10:06
一点都不会啊  详情 回复 发表于 2013-5-26 18:45
en wssss  详情 回复 发表于 2013-3-24 11:32
 
 

回复

190

帖子

0

TA的资源

五彩晶圆(初级)

板凳
 

编辑帖子太纠结了,估计看的人也纠结,论坛的编辑器 ……还是上pdf吧,一楼,老规矩,回复可见。

PS:问部分小仙,是不是有的人不回复也可见?

 

[ 本帖最后由 losingamong 于 2011-5-31 22:22 编辑 ]
此帖出自stm32/stm8论坛

点评

楼主讲得精辟啊  详情 回复 发表于 2014-7-9 10:14
好东西 学习学习 谢谢  详情 回复 发表于 2014-7-4 17:30
HCQ
感谢楼主分享,楼主辛苦了  详情 回复 发表于 2014-2-26 16:56
感謝分享~!  详情 回复 发表于 2012-12-13 15:51
再看看吧!  详情 回复 发表于 2012-12-11 15:24
 
 

回复

888

帖子

3

TA的资源

五彩晶圆(初级)

4
 
恩,来学习下!!!
此帖出自stm32/stm8论坛
 
个人签名邮箱:ternence.hsu@foxmail.com
 
 

回复

13

帖子

0

TA的资源

一粒金砂(中级)

5
 
看一下附件,LZ辛苦了
此帖出自stm32/stm8论坛

点评

正好学习一下  详情 回复 发表于 2013-4-14 22:08
我回复了,可是看不到附件......  详情 回复 发表于 2013-2-26 19:08
 
 
 

回复

285

帖子

3701

TA的资源

五彩晶圆(中级)

6
 

原帖由 losingamong 于 2011-5-31 22:19 发表 编辑帖子太纠结了,估计看的人也纠结,论坛的编辑器 ……还是上pdf吧,一楼,老规矩,回复可见。 PS:问部分小仙,是不是有的人不回复也可见? 

 

回楼主:不回复 不可见

此帖出自stm32/stm8论坛
 
 
 

回复

2万

帖子

74

TA的资源

管理员

7
 

回复 板凳 losingamong 的帖子

请问楼主 编辑器遇到了什么问题?
此帖出自stm32/stm8论坛
加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身
 
个人签名

加油!在电子行业默默贡献自己的力量!:)

 
 

回复

7219

帖子

192

TA的资源

五彩晶圆(高级)

8
 
楼主很善于思考
此帖出自stm32/stm8论坛
 
 
 

回复

53

帖子

0

TA的资源

一粒金砂(中级)

9
 
嗯,论坛的编辑器要改进才行啊!
此帖出自stm32/stm8论坛
 
 
 

回复

16

帖子

0

TA的资源

一粒金砂(中级)

10
 
嗯,论坛的编辑器要改进才行啊!
此帖出自stm32/stm8论坛

点评

hen hao  详情 回复 发表于 2012-10-22 18:03

赞赏

1

查看全部赞赏

 
 
 

回复

39

帖子

0

TA的资源

一粒金砂(中级)

11
 
先看一看
此帖出自stm32/stm8论坛
 
 
 

回复

1

帖子

0

TA的资源

一粒金砂(初级)

12
 
LZ辛苦
此帖出自stm32/stm8论坛
 
 
 

回复

63

帖子

0

TA的资源

一粒金砂(高级)

13
 
了解一下。
此帖出自stm32/stm8论坛
 
 
 

回复

360

帖子

0

TA的资源

纯净的硅(初级)

14
 
LZ辛苦
此帖出自stm32/stm8论坛
 
 
 

回复

6

帖子

0

TA的资源

一粒金砂(初级)

15
 
谢谢楼主~~~~
此帖出自stm32/stm8论坛
 
 
 

回复

4

帖子

0

TA的资源

一粒金砂(初级)

16
 
多谢指点,好老师!
此帖出自stm32/stm8论坛
 
 
 

回复

3

帖子

0

TA的资源

一粒金砂(初级)

17
 

回复 楼主 losingamong 的帖子

回复一下,看看是否可见!
此帖出自stm32/stm8论坛
 
 
 

回复

4

帖子

0

TA的资源

一粒金砂(初级)

18
 
   看看,呵呵。我是新手,很需要哦。
此帖出自stm32/stm8论坛
 
 
 

回复

11

帖子

0

TA的资源

一粒金砂(中级)

19
 
谢谢!
此帖出自stm32/stm8论坛
 
 
 

回复

1

帖子

0

TA的资源

一粒金砂(初级)

20
 
对新手很有有用啊!
此帖出自stm32/stm8论坛
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/9 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表