标签:
转载地址
http://wenku.baidu.com/link?url=F6eRvhkMYfdoKSxI_hStOGIzLc-1fsLPlUKXaLBpEmGtWcPg8KDIzJOUJn6MLCimLWY5gdW0LvW2BLZsyHDiJQGR3sx7W64ELU8pVejYFdq
选择DK固件库快速组织代码技巧
温州大学 成林俞
用最简单的GPIO初始化函数为例。
现在我们要初始化某个GPIO端口,我们要怎样快速操作呢?在头文件 stm32f10x_gpio.h
头文件中,定义GPIO初始化函数为:
voidGPIO_Init(GPIO_TypeDef*GPIOx, GPIO_InitTypeDef*GPIO_InitStruct);
现在我们想写初始化函数,那么我们在不参考其他代码的前提下,怎么组织代码呢?
首先,我们可以看出,函数的入口参数是 GPIO_TypeDef类型指针和GPIO_InitTypeDef
类型指针,因为 GPIO_TypeDef 入口参数比较简单,所以我们通过第二个入口参数
GPIO_InitTypeDef类型指针来讲解。双击GPIO_InitTypeDef后右键选择“Goto definition
of …”,如下图:
于是定位到stm32f10x_gpio.h中GPIO_InitTypeDef的定义处:
typedefstruct
{
uint16_t GPIO_Pin;
GPIOSpeed_TypeDefGPIO_Speed;
GPIOMode_TypeDefGPIO_Mode;
}GPIO_InitTypeDef;
可以看到这个结构体有3个成员变量,这也告诉我们一个信息,一个 GPIO口的状态是由
速度(Speed)和模式(Mode)来决定的。
我们首先要定义一个结构体变量,下面我们定义:
GPIO_InitTypeDef GPIO_InitStructure;
//voidGPIO_Init(GPIO_TypeDef*GPIOx,GPIO_InitTypeDef* GPIO_InitStruct);
接着我们要初始化结构体变量GPIO_InitStructure。首先我们要初始化成员变量
GPIO_Pin ( GPIO_InitTypeDef的第一个成员),这个时候我们就有点迷糊了, 这个变量到底
可以设置哪些值呢?这些值的范围有什么规定吗?
这里我们就要找到GPIO_Init()函数的实现处, 同样, 双击GPIO_Init, 右键点击“Goto
definitionof…”,这样光标定位到stm32f10x_gpio.c文件中的GPIO_Init函数体开始处,我
们可以看到在函数的开始处有如下几行:
voidGPIO_Init(GPIO_TypeDef*GPIOx, GPIO_InitTypeDef*GPIO_InitStruct)
{
……
/*Checktheparameters*/
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));
……
assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
……
}
顾名思义, assert_param函数式对入口参数的有效性进行判断,所以我们可以从这个函数
入手,确定我们的入口参数的范围。第一行是对第一个参数GPIOx进行有效性判断,双击
“IS_GPIO_ALL_PERIPH”右键点击“gotodefitionof…”定位到了下面的定义:
#defineIS_GPIO_ALL_PERIPH(PERIPH)(((PERIPH)== GPIOA) || \
((PERIPH)==GPIOB)|| \
((PERIPH)==GPIOC)|| \
((PERIPH)==GPIOD)|| \
((PERIPH)==GPIOE)|| \
((PERIPH)==GPIOF)|| \
((PERIPH)==GPIOG))
很明显可以看出, GPIOx的取值规定只允许是GPIOA~GPIOG。
同样的办法,我们双击“IS_GPIO_MODE”右键点击“gotodefitionof…”,定位到下面的定
义:
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;
#defineIS_GPIO_MODE(MODE) (((MODE) == GPIO_Mode_AIN) || \
((MODE) ==GPIO_Mode_IN_FLOATING) ||\
((MODE) ==GPIO_Mode_IPD) ||\
((MODE) ==GPIO_Mode_IPU) ||\
((MODE) ==GPIO_Mode_Out_OD) || \
((MODE) ==GPIO_Mode_Out_PP) || \
((MODE) ==GPIO_Mode_AF_OD)||\
((MODE) ==GPIO_Mode_AF_PP))
所以GPIO_InitStruct->GPIO_Mode成员的取值范围只能是上面定义的8种。 这8中模式是通
过一个枚举类型组织在一起的。
同样的方法可以找出GPIO_Speed的参数限制:
typedefenum
{
GPIO_Speed_10MHz =1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
#defineIS_GPIO_SPEED(SPEED) (((SPEED) ==GPIO_Speed_10MHz)|| \
((SPEED) == GPIO_Speed_2MHz) || \
((SPEED) == GPIO_Speed_50MHz))
同样的方法我们双击“IS_GPIO_PIN”右键点击“go todefitionof…”,定位到下面的定义:
#define IS_GPIO_PIN(PIN) ((((PIN) & (uint16_t)0x00) == 0x00) && ((PIN) !=
(uint16_t)0x00))
可以看出, GPIO_Pin成员变量的取值范围为0x0000到0xffff, 那么是不是我们写代码初始化
就是直接给一个16位的数字呢?这也是可以的,但是大多数情况下, MDK不会让你直接在
入口参数处设置一个简单的数字,因为这样代码的可读性太差, MDK会将这些数字的意思
通过宏定义定义出来, 这样可读性大大增强。 我们可以看到在IS_GPIO_PIN(PIN)宏定义的上面还有数行宏定义:#defineGPIO_Pin_0 ((uint16_t)0x0001) /*!<Pin 0 selected*/#defineGPIO_Pin_1 ((uint16_t)0x0002) /*!<Pin 1 selected*/#defineGPIO_Pin_2 ((uint16_t)0x0004) /*!<Pin 2 selected*/#defineGPIO_Pin_3 ((uint16_t)0x0008) /*!<Pin 3 selected*/#defineGPIO_Pin_4 ((uint16_t)0x0010) /*!<Pin 4 selected*/……#defineGPIO_Pin_14 ((uint16_t)0x4000) /*!<Pin 14selected*/#defineGPIO_Pin_15 ((uint16_t)0x8000) /*!<Pin 15selected*/#defineGPIO_Pin_All ((uint16_t)0xFFFF) /*!<All pinsselected*/#defineIS_GPIO_PIN(PIN) ((((PIN) &(uint16_t)0x00) == 0x00)&& ((PIN) !=(uint16_t)0x00))这些宏定义GPIO_Pin_0~GPIO_Pin_All就是MDK事先定义好的, 我们写代码的时候初始化GPIO_Pin的时候入口参数可以是这些宏定义。 对于这种情况, MDK一般把取值范围的宏定义放在判断有效性语句的上方,这样是为了方便大家查找。讲到这里,我们基本对GPIO_Init的入口参数有比较详细的了解了。于是我们可以组织起来下面的代码:GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;//GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStructure);接着又有一个问题会被提出来,这个初始化函数一次只能初始化一个IO口吗?我要同时初始化很多个IO口,是不是要复制很多次这样的初始化代码呢?这里又有一个小技巧了。 从上面的GPIO_Pin_x的宏定义我们可以看出, 这些值是0,1,2,4这样的数字,所以每个IO口选定都是对应着一个位, 16位的数据一共对应16个IO口。这个位为0那么这个对应的IO口不选定,这个位为1对应的IO口选定。如果多个IO口,他们都是对应同一个GPIOx,那么我们可以通过|(或)的方式同时初始化多个IO口。这样操作的前提是, 他们的Mode和Speed参数相同, 因为Mode和Speed参数并不能一次定义多种。 所以初始化多个IO口的方式可以是如下:GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;//指定端口GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//端口模式:推挽输出GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//速度GPIO_Init(GPIOB,&GPIO_InitStructure);//初始化对于那些参数可以通过|(或)的方式连接, 这既有章可循, 同时也靠大家在开发过程中不断积累。有客户经常问到,我每次使能时钟的时候都要去查看时钟树看那些外设是挂载在那个总线之下的,这好麻烦。学到这里我相信大家就可以很快速的解决这个问题了。在stm32f10x.h文件里面我们可以看到如下的宏定义:#defineRCC_APB2Periph_GPIOA ((uint32_t)0x00000004)#defineRCC_APB2Periph_GPIOB ((uint32_t)0x00000008)#defineRCC_APB2Periph_GPIOC ((uint32_t)0x00000010)#defineRCC_APB1Periph_TIM2 ((uint32_t)0x00000001)#defineRCC_APB1Periph_TIM3 ((uint32_t)0x00000002)#defineRCC_APB1Periph_TIM4 ((uint32_t)0x00000004)#defineRCC_AHBPeriph_DMA1 ((uint32_t)0x00000001)#defineRCC_AHBPeriph_DMA2 ((uint32_t)0x00000002)从上图可以很明显的看出GPIOA~GPIOC是挂载在APB2下面, TIM2~TIM4是挂载在APB1下面, DMA是挂载在AHB下面。所以在使能DMA的时候记住要调用的是RCC_AHBPeriphClock() 函数使能,在使能 GPIO 的时候调用的是RCC_APB2PeriphResetCmd()函数使能。大家会觉得上面讲解有点麻烦, 每次要去查找assert_param()这个函数去寻找, 那么有没有更好的办法呢?大家可以打开GPIO_InitTypeDef结构体定义:typedefstruct{uint16_tGPIO_Pin; /*!<SpecifiestheGPIOpinstobeconfigured.Thisparametercanbeanyvalueof@refGPIO_pins_define */GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selectedpins.Thisparametercanbe avalue of@refGPIOSpeed_TypeDef*/GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for theselectedpins.Thisparameter canbea value of@refGPIOMode_TypeDef*/}GPIO_InitTypeDef;从上图的结构体成员后面的注释我们可以看出GPIO_Mode的意思是“Specifies theoperating mode for the selected pins.This parameter can be a value of @refGPIOMode_TypeDef”。从这段注释可以看出GPIO_Mode的取值为GPIOMode_TypeDef枚举类型的枚举值, 大家同样可以用之前讲解的方法右键双击“GPIOMode_TypeDef”
标签:
原文地址:http://www.cnblogs.com/eat-too-much/p/5339324.html