-2-
}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的定义:
typedefenum{
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设备的工作模式)。至此对程序段一的①解析可以做一个总结:该行定义一个结构体类型的变量GPIO_InitStructure,并且该结构体有3个成员,分别为GPIO_Pin、GPIO_Speed和GPIO_Mode,并且GPIO_Pin表示GPIO设备引脚GPIO_Speed表示GPIO设备伍茄宽速率和GPIO_Mode表示GPIO设备工作模式。
接下来是②,此句是一个赋值语句,把GPIO_Pin_4赋给GPIO_InitStructure结构体中的成员GPIO_Pin,可以在“stm32f10x_gpio.h”文件中找到对GPIO_Pin_4做的宏定义:
#defineGPIO_Pin_4((u16)0x0010)因此②的本质是将16位数0x0010赋给GPIO_InitStructure结构体中的成员GPIO_Pin。
③语句和②相似将GPIO_Speed_50MHz赋给GPIO_InitStructure结构体中的成员GPIO_Speed,但注意到此处GPIO_Speed_50MHz只是一个枚举变量,并非具体的某个值。
④语句亦和②语句类似,把GPIO_Mode_Out_PP赋给GPIO_InitStructure结构体中的成员GPIO_Mode,从上文可知GPIO_Mode_Out_PP的值为0x10。
⑤是一个函数调用,即调用GPIO_Init函数,并提供给该函数2个参数,分别为GPIOA和&GPIO_InitStructure,其中&GPIO_InitStructure表示结构体变量GPIO_InitStructure的地址,而GPIOA则在“stm32f10x_map.h”文件中找到定义:
#ifdef_GPIOA
#defineGPIOA((GPIO_TypeDef*)GPIOA_BASE)#endif
此三行代码是一个预编译结构,首先判断是否定义了宏_GPIOA。可以在“stm32f10x_conf.h”中发腔亮现对_GPIOA的定义为:#define_GPIOA。这表示编译器会将代
-3-
码中出现的GPIOA全部替换为((GPIO_TypeDef*)GPIOA_BASE)。从该句的C语言语法可以判断出((GPIO_TypeDef*)GPIOA_BASE)的功能为将GPIOA_BASE强制类型转换为指向GPIO_TypeDef类型的结构体指针变量。
如此则需要找出GPIO_TypeDef的原型及GPIOA_BASE的含义。同样GPIO_TypeDef的原型位于“stm32f10x_gpio.h”文件,原纳桐型如下:
typedefstruct{
vu32CRLvu32CRHvu32IDRvu32ODRvu32BSRRvu32BRRvu32LCKR}GPIO_TypeDef
这里把GPIO寄存器封装成结构体,即把GPIO的各寄存器类型设为结构体成员,结构体类型名定义为GPIO_TypeDef。
GPIOA_BASE的含义在“stm32f10x_map.h”文件中找到:
#defineGPIOA_BASE(APB2PERIPH_BASE+0x0800)#defineAPB2PERIPH_BASE(PERIPH_BASE+0x10000)#definePERIPH_BASE((u32)0x40000000)
明显GPIOA_BASE表示一个地址,通过将以上3个宏展开可以得到:
GPIOA_BASE=0x40000000+0x10000+0x0800;
此处的关键便在于0x40000000、0x10000和0x0800这三个数值的来历。读者应该通过宏名猜到了,这就是STM32微控制器的GPIOA的设备地址。通过查阅STM32微控制器参考手册可以得知,STM32的外设起始基地址为0x40000000,而APB2总线设备起始地址相对于外设基地址的偏移量为0x10000,GPIOA设备相对于APB2总线设备起始地址偏移量为0x0800。
对⑤句代码进行一个总结:调用GPIO_Init函数,并将STM32微控制器的GPIOA设备地址和所定义的结构体变量GPIO_InitStructure的地址传入。
以上是对GPIOA初始化库函数的剖析,现继续转移到函数内部分析,GPIO_Init函数原型如程序段二:
voidGPIO_Init(GPIO_TypeDef*GPIOx,GPIO_InitTypeDef*GPIO_InitStruct){
u32currentmode=0x00,currentpin=0x00,pinpos=0x00,pos=0x00u32tmpreg=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))
-4-
/*将工作模式暂存至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=0x00pinpos<0x08pinpos++){
/*获取将要配置的引脚号*/
pos=((u32)0x01)<<pinpos
currentpin=(GPIO_InitStruct->GPIO_Pin)&posif(currentpin==pos){
/*先清除对应引脚的配置字*/pos=pinpos<<2
pinmask=((u32)0x0F)<<postmpreg&=~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->CRHfor(pinpos=0x00
pinpos<0x08pinpos++){
pos=(((u32)0x01)<<(pinpos+0x08))
currentpin=((GPIO_InitStruct->GPIO_Pin)&pos)if(currentpin==pos){
pos=pinpos<<2
pinmask=((u32)0x0F)<<pos
-5-
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(仅列出低八位引脚寄存器描述,高八位引脚类同):
图1GPIO设备控制寄存器GPIOx_CRL描述
该寄存器为32位,其中分为8份,每份4位,对应低八位引脚的设置。每一个引脚的设置字
-6-
分为两部分,分别为CNF和MODE,各占两位空间。当MODE的设置字为0时,表示将对应引脚配置为输入模式,反之设置为输出模式,并有最大翻转速率限制。而当引脚配置为输出模式时,CNF配置字则决定引脚以哪种输出方式工作(通用推挽输出、通用开漏输出等)。通过对程序的阅读和分析不难发现,本文最初程序段中GPIO_InitStructure所传入参数的对寄存器的作用如下:
GPIO_Pin_4被宏替换为0x0010,对应图1可看出为用于选择配置GPIOx_CRL的[19:16]位,分别为CNF4[1:0]、MODE4[1:0]。
GPIO_Speed_50MHz为枚举类型,包含值0x03,被用于将GPIOx_CRL位中的MODE4[1:0]配置为b11(此处b意指二进制)。
GPIO_Mode亦为枚举类型,包含值0x10,被用于将GPIOx_CRL位中的CNF4[1:0]配置为b00。事实上GPIO_Mode的值直接影响寄存器的只有低四位,而高四位的作用可以从程序段二中看出,是用于判断此参数是否用于GPIO引脚输出模式的配置。
至此应不难知道STM32的固件库最后是怎样影响最底层的寄存器的。总结起来就是总结起来就是:
:固件库首先将各个设备所有寄存器的配置字进行预先定义,然后封装在结构或枚举变量中,待用户调用对应的固件库函数时,会根据用户传入的参数从这些封装好的结构或枚举变量中取出对应的配置字,最后写入寄存器中,完成对底层寄存器的配置。
可以看到,STM32的固件库函数对于程序开发人员来说是十分便利的存在,只需要填写言简意赅的参数就可以在完全不关心底层寄存器的前提下完成相关寄存器的配置,具有相当不错的通用性和易用性,也采取了一定措施保证库函数的安全性(主要引入了参数检查函数assert_param)。但同时也应该知道,通用性、易用性和安全性的代价是加大了代码量,同时增加了一些逻辑判断代码造成了一定的时间消耗,在对时间要求比较苛刻的应用场合需要评估使用固件库函数对程序运行时间所带来的影响。读者在使用STM32的固件库函数进行程序开发时,应该意识到这些问题
MF RC500的卡可以通过射频信号来读取区域内灶衫的卡号,卡号隐兆腔是一个人的序列号,您可以对卡的序列号,以统计数据来确定不同的卡猜氏的数量代表了不同的人,与卡号重复没有xyk统计数字。 MF RC500的节目蛮多的,如果你需要可以留下您的E-mail地址或联系方式,我可以帮你。欢迎分享,转载请注明来源:内存溢出
评论列表(0条)