个人觉得,今天要说的这个内容,还是有点儿技术含量。在分析代码之前,先谈点感想,我是不得不佩服写出这些代码的前辈(真乃神人也!),我只是理解这些代码就痛苦不堪,差点儿还放弃了,这些前辈却能够自己想出这些代码,我真的是佩服的五体投地。前辈在上,请接受我万分的景仰。
以下库函数均来自于STM32F1系列控制器的外设函数库。
/** * @brief 初始化控制LcD的IO * @param 无 * @retval 无 */ void LCD_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; /* 使能FSMC时钟*/ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE); // 开启FSMC时钟 /* 使能FSMC对应相应管脚时钟*/ // GPIOB 时钟开启 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /* 配置LCD背光控制管脚*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_Init(GPIOB, &GPIO_InitStructure); } /** * @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) // 相与结果为0x02,所以不为0,即条件符合 { tmpreg = GPIOx->CRL; // 这里的GPIOX=GPIOB,而GPIOB为一个结构体指针,可以看其定义就知道,而CRL为该结构体成员之一 for (pinpos = 0x00; pinpos < 0x08; pinpos++) { pos = ((uint32_t)0x01) << pinpos; /* Get the port pins position */ currentpin = (GPIO_InitStruct->GPIO_Pin) & pos; // 只有两数相等时相与的结果才不为0,且等于pos值 if (currentpin == pos) // 当上面结果不为0时,即符合判断条件 { pos = pinpos << 2; /* 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 << 2; /* 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; } }
我们从第一个函数开始分析,这是一个初始化LCD的函数,这里我们不讲太多其他的,主要是为了分析GPIO_Init()这个函数。首先定义一个结构体GPIO_InitTypeDef GPIO_InitStructure;
结构体的类型为GPIO_InitTypeDef,我们来看下这个类型的定义。
/** * @brief GPIO Init structure definition */ 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;
该结构体有3个成员:GPIO_Pin、GPIO_Speed、GPIO_Mode。成员GPIO_Speed、GPIO_Mode均为枚举类型,两者定义分别如下。
/** * @brief Output Maximum frequency selection */ typedef enum { GPIO_Speed_10MHz = 1, GPIO_Speed_2MHz, GPIO_Speed_50MHz }GPIOSpeed_TypeDef;
这是个枚举类型,表示GPIO_Speed_10MHz = 1,GPIO_Speed_2MHz = 2,GPIO_Speed_50MHz = 3
/** * @brief Configuration Mode enumeration */ 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_InitStructure后,开启GPIOB的时钟。接下来
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
由上面分析可知
GPIO_InitStructure.GPIO_Mode = 0x10;
GPIO_InitStructure.GPIO_Speed = 0x03;
接着往下执行代码
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_Pin_1的定义在<stm32f10x_gpio.h>头文件里面可以查到
#define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */
即 GPIO_InitStructure.GPIO_Pin = 0x0002;
GPIO_Init()函数
GPIO_Init(GPIOB, &GPIO_InitStructure);
由上述代码可知,函数传递的参数为GPIOB与GPIO_InitStructure的地址,GPIOB实质上为一个结构体指针,在<stm32f10x.h>头文件里面的GPIO内存映射部分找到其定义
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
可以看出GPIOB为一个(GPIO_TypeDef *)类型的指针,我们再来看一下GPIO_TypeDef类型的定义
/** * @brief General Purpose I/O */ 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;
可以看出GPIO_TypeDef为一个结构体,其成员名即为GPIO相关寄存器名的缩写,成员地址与寄存器地址一一对应,操作结构体的成员就是在操作相应的寄存器。
由此可知,GPIOB为一个结构体指针,其成员指向相应的寄存器。下面进入GPIO_Init()函数内部。
uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00; uint32_t tmpreg = 0x00, pinmask = 0x00;
首先定义了6个变量,全部初始化为0。
/* 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模式配置
第1行代码:
currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
即 currentmode = (0x10 & 0x0f) = 0x00;
第2行代码:
if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
即 if( (0x10 & 0x10) != 0x00),因为括号里面相与结果还是0x10,所以不等于0x00,即满足判断条件,继续往下执行。
第4行代码同样是参数检测,略过。
第5行代码:
currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
即 currentmode |= 0x03,即 currentmode = 0x00 | 0x03 = 0x03;
接下来是CRL寄存器配置
/*---------------------------- GPIO CRL Configuration ------------------------*/ /* Configure the eight low port pins */ if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00) // 相与结果为0x02,所以不为0,即条件符合 { tmpreg = GPIOx->CRL; // 这里的GPIOX=GPIOB,而GPIOB为一个结构体指针,可以看其定义就知道,而CRL为该结构体成员之一 for (pinpos = 0x00; pinpos < 0x08; pinpos++) { pos = ((uint32_t)0x01) << pinpos; /* Get the port pins position */ currentpin = (GPIO_InitStruct->GPIO_Pin) & pos; // 只有两数相等时相与的结果才不为0,且等于pos值 if (currentpin == pos) // 当上面结果不为0时,即符合判断条件 { pos = pinpos << 2; /* 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; // 哎,太叨了 }
第3行代码:
if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
即 if( (0x0002 & 0x00ff) != 0x00),因为括号里运算结果为0x0002,所以不等于0x00,即满足判断条件
第6行代码:
tmpreg = GPIOx->CRL;
由前面所述可知,GPIOB为一个结构体指针,而这里GPIOX即为GPIOB,此时CRL=0,即tmpreg = 0x00;
第8行代码:
for (pinpos = 0x00; pinpos < 0x08; pinpos++)
即 pinpos从0到7,整个for()循环执行8次
第10行代码:
pos = ((uint32_t)0x01) << pinpos;
第1次循环:pinpos = 0x00,即 pos = 0x01 << 0x00 = 0x01;
第2次循环:pinpos = 0x01,即 pos = 0x01 << 0x01 = 0x02;
第3次循环:pinpos = 0x02,即 pos = 0x01 << 0x02 = 0x04;
...
第一次循环时执行情况:
第12行代码:
currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
即 currentpin = 0x0002 & 0x01 = 0x0000;
第14行代码:
if (currentpin == pos)
即 if (0x0000 == 0x01),不符合判断条件,后面的语句也就不需要再执行下去了。
第二次循环时执行情况:
第12行代码:
currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
即 currentpin = 0x0002 & 0x02 = 0x0002;
第14行代码:
if (currentpin == pos)
即 if(0x0002 == 0x02),此时满足判断条件,由此可以看出,只有当两个对象相等时才能够执行 if()里面的语句,这里 GPIO_Pin = 0X0002,只有当pos = 0x02时才会执行后面的代码,其他情况全部跳过if()内语句。
下面我们进入条件语句内。
第16行代码:
pos = pinpos << 2;
即 pos = 0x01 << 2 = 0x04;(只有当 pinpos = 0x01 时 pos = 0x02 ,即此时才能进入条件语句内)
第18行代码:
pinmask = ((uint32_t)0x0F) << pos;
即 pinmask = 0x0f << 0x04 = 0xf0;
第19行代码:
tmpreg &= ~pinmask;
即 tmpreg &= ~0xf0 = 0x00;
第21行代码:
tmpreg |= (currentmode << pos);
即 tmpreg |= 0x03 << 0x04 = 0x30;
第23行代码:
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
很明显,这里不满足条件,跳过if内语句
第30行代码:
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
很明显,这里同样不满足条件,同样跳过。
于是只剩最后一条代码
GPIOx->CRL = tmpreg;
即GPIOB->CRL = 0x30;
因为不满足下述代码
if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
所以CRH寄存器就不用配置。
根据这个结果,由用户手册可知,如下图:
GPIOB被配置为通用推挽输出模式,最大速率为50MHz,引脚为1脚。现在我们回过头来看下配置代码是怎么写的。
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /* 配置LCD背光控制管脚*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_Init(GPIOB, &GPIO_InitStructure);
这几句代码的字面意思是GPIO模式配置为通用推挽输出、速率配置为50M、引脚配置为1脚,然后按这个配置初始化GPIOB。很明显,这个字面意思与上面代码执行的结果一致,所以它成功的按要求配置了GPIO,而且非常的直观。真的是非常的聪明。
因作者水平有限,有错误处还请批评指正。
原创作品