STM32 HAL库GPIO初始化函数分析

函数代码及其注释

HAL库的封装非常完善,在可以使用HAL_GPIO_Init函数初始化端口用于外部中断,无需自己手动修改寄存器什么的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
{
uint32_t position = 0x00u; // 记录当前初始化的是第几个管脚
uint32_t ioposition;
uint32_t iocurrent;
uint32_t temp;
uint32_t config = 0x00u;
__IO uint32_t *configregister; /* Store the address of CRL or CRH register based on pin number */
uint32_t registeroffset; /* offset used during computation of CNF and MODE bits placement inside CRL or CRH register */

/* Check the parameters */
assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));
assert_param(IS_GPIO_PIN(GPIO_Init->Pin));
assert_param(IS_GPIO_MODE(GPIO_Init->Mode));

/* Configure the port pins */
while (((GPIO_Init->Pin) >> position) != 0x00u)
{
/* Get the IO position */
/* 记录当前初始化的是哪个管脚(二进制形式,1为初始化的管脚)*/
ioposition = (0x01uL << position);

/* Get the current IO position */
/* 中间变量,用于检查position指定的管脚是否被指定要初始化 */
iocurrent = (uint32_t)(GPIO_Init->Pin) & ioposition;

if (iocurrent == ioposition)
{
/* Check the Alternate function parameters */
assert_param(IS_GPIO_AF_INSTANCE(GPIOx));

/* Based on the required mode, filling config variable with MODEy[1:0] and CNFy[3:2] corresponding bits */
/* 根据输入变量生成对应寄存器的值 */
switch (GPIO_Init->Mode)
{
/* If we are configuring the pin in OUTPUT push-pull mode */
/* 推挽输出配置 */
case GPIO_MODE_OUTPUT_PP:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_PP;
break;

/* If we are configuring the pin in OUTPUT open-drain mode */
/* 开漏输出配置 */
case GPIO_MODE_OUTPUT_OD:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_OD;
break;

/* If we are configuring the pin in ALTERNATE FUNCTION push-pull mode */
/* 特殊功能推挽输出配置 */
case GPIO_MODE_AF_PP:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_AF_OUTPUT_PP;
break;

/* If we are configuring the pin in ALTERNATE FUNCTION open-drain mode */
/* 特殊功能开漏输出配置 */
case GPIO_MODE_AF_OD:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_AF_OUTPUT_OD;
break;

/* If we are configuring the pin in INPUT (also applicable to EVENT and IT mode) */
/* 普通输入、中断输入和事件输入 */
case GPIO_MODE_INPUT:
case GPIO_MODE_IT_RISING:
case GPIO_MODE_IT_FALLING:
case GPIO_MODE_IT_RISING_FALLING:
case GPIO_MODE_EVT_RISING:
case GPIO_MODE_EVT_FALLING:
case GPIO_MODE_EVT_RISING_FALLING:
/* Check the GPIO pull parameter */
assert_param(IS_GPIO_PULL(GPIO_Init->Pull));
if (GPIO_Init->Pull == GPIO_NOPULL)
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_FLOATING;
}
else if (GPIO_Init->Pull == GPIO_PULLUP)
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_PU_PD;

/* Set the corresponding ODR bit */
GPIOx->BSRR = ioposition;
}
else /* GPIO_PULLDOWN */
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_PU_PD;

/* Reset the corresponding ODR bit */
GPIOx->BRR = ioposition;
}
break;

/* If we are configuring the pin in INPUT analog mode */
case GPIO_MODE_ANALOG:
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_ANALOG;
break;

/* Parameters are checked with assert_param */
default:
break;
}

/* Check if the current bit belongs to first half or last half of the pin count number
in order to address CRH or CRL register*/
configregister = (iocurrent < GPIO_PIN_8) ? &GPIOx->CRL : &GPIOx->CRH;
registeroffset = (iocurrent < GPIO_PIN_8) ? (position << 2u) : ((position - 8u) << 2u);

/* Apply the new configuration of the pin to the register */
MODIFY_REG((*configregister), ((GPIO_CRL_MODE0 | GPIO_CRL_CNF0) << registeroffset), (config << registeroffset));

/*--------------------- EXTI Mode Configuration ------------------------*/
/* 外部中断配置 */
/* Configure the External Interrupt or event for the current IO */
if ((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE)
{
/* Enable AFIO Clock */
__HAL_RCC_AFIO_CLK_ENABLE();
/* EXTICR 16位 总共四个,每个EXITCR分别控制四个中断,总共 4 * 4 = 16个中断
position >> 2 ==> position / 4 ,把 0-15 转换为 0-4 即找到对应的寄存器
*/
temp = AFIO->EXTICR[position >> 2u];
/* position & 3 ==> position % 4 ,即找到在当前EXITCR寄存器中,position对应的是哪一个中断。
乘4是因为左移的单位是 “中断数”,一个中断对应4位,我要左移 n个中断 意味着要左移 n * 4位。
*/
CLEAR_BIT(temp, (0x0Fu) << (4u * (position & 0x03u)));
SET_BIT(temp, (GPIO_GET_INDEX(GPIOx)) << (4u * (position & 0x03u)));
AFIO->EXTICR[position >> 2u] = temp;


/* Enable or disable the rising trigger */
/* 设置上升沿触发 */
if ((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE)
{
SET_BIT(EXTI->RTSR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->RTSR, iocurrent);
}

/* Enable or disable the falling trigger */
/* 设置下降沿触发 */
if ((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE)
{
SET_BIT(EXTI->FTSR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->FTSR, iocurrent);
}

/* Configure the event mask */
if ((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT)
{
SET_BIT(EXTI->EMR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->EMR, iocurrent);
}

/* Configure the interrupt mask */
/* 设置中断屏蔽 为0时不允许对应中断 */
if ((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT)
{
SET_BIT(EXTI->IMR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->IMR, iocurrent);
}
}
}
// 配置下一位
position++;
}
}

附:各寄存器的结构

EXTICR 外部中断控制寄存器

总共有四个,结构都类似
EXTICR

EXTI_IMR 外部中断屏蔽寄存器

EXTICR

Author: LouisZ
Link: http://notes.louisz.top/2024/07/stm32-hal-gpio-init-comment/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.