STM32的学习之旅

上个文章已经介绍了keil5的工程介绍,接下来的便是GPIO口的操作了,关于GPIO口的开发,我们选择的是流水灯的闪烁,这样可以清楚看得到效果。
对于开发GPIO,有三种办法,一种是库函数开发(官方已经封装好底层的操作,你仅仅需要把参数传进去,就可以配置好相关的寄存器),另一种开发是寄存器的开发(需要懂得汇编)。本人技术有限,只能学习库函数版本了。
废话不多说,直接上代码:
STM32的学习之旅很简单的一个程序,点亮板子上的LED灯,只需要调用库函数的GPIO_Init函数,把初始化的结构体赋值后作为参数传入便可。而RCC_APB2PeriphClockCmd()函数是初始化GPIOB口的时钟。好了GPIO的使用便是如此,如果觉得结束了,那还是太早了,我决定把深入的思维也写出来:

GPIO_Init 库函数的解析:
源码:
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;
}

assert_param(expr)函数的定义如下:
#define assert_param(expr) ((void)0)
#define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)FILE, LINE))
理解:
三目运算符的使用:
如果expr为真,返回(void)0,否则返回assert_failed((uint8_t *)FILE, LINE))
接下来是IS_GPIO_ALL_PERIPH(GPIOx)的定义:
#define IS_GPIO_ALL_PERIPH(PERIPH) (((PERIPH) == GPIOA) ||
((PERIPH) == GPIOB) ||
((PERIPH) == GPIOC) ||
((PERIPH) == GPIOD) ||
((PERIPH) == GPIOE) ||
((PERIPH) == GPIOF) ||
((PERIPH) == GPIOG))

从定义中看出来:GPIOx作为参数传入,和GPIOA-G组做“或”比较,得出GPIOx是否存在
所以assert_param(IS_GPIO_ALL_PERIPH(GPIOx));函数的作用是遍历外围设备

/---------------------------- GPIO Mode Configuration -----------------------/
在这一块,主要的工作是初始化GPIO口,设置8种工作模式的其中一种,并设置其中的工作速率
此处需要查看其中的结构的:
GPIO_InitTypeDef结构体的定义:
typedef struct
{
uint16_t GPIO_Pin;
GPIOSpeed_TypeDef GPIO_Speed;
GPIOMode_TypeDef GPIO_Mode;

}GPIO_InitTypeDef;

GPIO_TypeDef结构体的定义:
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口的初始化定义,在stm32中文手册也可以找到相关的寄存器定义:
对应的8种工作模式定义为:
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;
STM32的学习之旅
对照寄存器的表,0x00便是MODE和CNF的初始化,此时0x00代表设置GPIO口模拟输入,但你会发现,定义的值0x14,0x10,0x1c,0x18这类的值已经超过寄存器的MODE和CNF的范围
一开始的 currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);便是与操作,0x0f的作用是屏蔽前一位的数,例如0x1c与操作后变为0x0c

对于GPIO_Init函数,传递进来的参数,先通过mode参数,设置了GPIO模式参数,再接着根据传递的mode的值,判断此GPIO设置为输入模式还是输出模式,如果是输出模式,才会进行设置GPIO的速率,如果是输入模式,忽略掉速率的设置。
GPIO的结构体:
typedef enum
{
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
接下来的·解析也是同样的道理,这里放一个大佬的博客:https://blog.****.net/PINLemon/article/details/72857922
说的很清楚了,对比工程一步一步的分析吧