左左右学堂 · 课程目录

第45期 HAL_DeInit()源码深度解析

观看B站原视频 · 文稿与高清画面

Hello,小伙伴们,大家好,这里是左左右。前面视频我们把hal_gpio.c这个文件中。关于GPIO 初始化、配置相关的函数都细细拆解完了。从这期开始。咱们继续啃这个文件里剩下的源码。今天的主角就是 HAL_GPIO_DeInit 这个函数。老规矩,咱们先找一个最直观的例子,把函数用起来,再跳进源码去读。

首先,我们打开按钮控制LED小灯亮灭的案例。在while循环中加入HAL_GPIO_DeInit(GPIOC,GPIO_Pin_13)这个函数,接着,跳转到定义这个函数的地方去看看。一进来你就会发现。它的整体结构其实和前面讲过的HAL_GPIO_Init()是十分相似的。

首先,定义变量。接着,检测传入参数的合法性。接下来就是一个while循环了。在循环里藏着两个 if 判断,当条件成立时进入判断语句,条件不成立时。执行position++的操作。观察完整体结构。咱们就从头开始。一行一行地把这个函数读懂。这里一共定义了5个变量。分别是position,iocurrent,tmp,registeroffset。

视频画面 01:29.8
视频画面 01:29.8 · 1080P 原图 · 查看对应视频

还有32位的指针configregister。由于这里要做位运算,所以,我们将GPIO_Pin_13转换为二进制数。随着position++,while循环中的条件持续向右移动。此时iocurrent中的值一直等于零。只有当position等于13时。iocurrent中的值才是一个非零的数。

视频画面 05:49.3
视频画面 05:49.3 · 1080P 原图 · 查看对应视频
视频画面 06:41.2
视频画面 06:41.2 · 1080P 原图 · 查看对应视频

if判断条件成立。接着,将这里的position也就是13转换为二进制数。将其右移两位。对应十进制数为3。此时tmp中存放的就是寄存器AFIO EXTICR4中的值。通过这里的运算就将EXTI13中的值提取出来了,也就是0010。接着,来看GPIO_GET_INDEX。

视频画面 03:13.6
视频画面 03:13.6 · 1080P 原图 · 查看对应视频

由于这里我们传入的是GPIOC,所以,返回值为2,将它转换为二进制数。左移4位。接着,来看CLEAR_BIT()这个函数。这里就是我们熟悉的将寄存器中某一位实现置零的操作。通过这几行代码就将EXTI IMR寄存器、EXTI EMR寄存器。EXTI FTSR寄存器、EXTI RTSR寄存器位13的值全部置零了。

接着,给变量tmp赋值。接着AFIO EXTICR4寄存器中EXTI13这四位就被置零了。这三行代码是大家熟悉的了。指针configregister存放的就是GPIOC CRH寄存器的地址了。registeroffset存放的是PC13对应的偏移值。MODIFY_REG这个函数就同时实现了复位和置位的操作。

此时可以发现PC13对应MODE位和CNF位中的值都是复位后的值。通过对HAL_GPIO_DeInit()这个函数的源码解析。我们也能够知道它要实现的主要功能就是关闭对应引脚的EXTI外部中断功能。将对应AFIO和EXTI寄存器中对应引脚全部复位。

同时,将对应引脚的输入输出模式设置为复位值,也就是浮空输入模式。这里的复位值指的就是我们不对引脚做任何设置。单片机上电后的初始状态,接着点击运行来验证一下。这里可以看到按键在第一次被按下时。小灯电平实现了翻转功能。后来再按按键就不起作用了。这是因为只有在第一次PC13引脚是具有外部中断的功能的。

视频画面 04:57.4
视频画面 04:57.4 · 1080P 原图 · 查看对应视频

之后PC13引脚就被复位了。也就失去了外部中断的功能了。所以,再按按键就不管用了。接下来就出几道思考题来考考大家吧。第一题:变量position在这个函数中的作用是什么?随着while循环的执行,position从零开始递增。position其实在遍历引脚号。用于指示当前正在处理哪一个引脚。

只有当对应引脚值为1时。判断条件才会成立。程序才会执行判断语句中的代码。它的值会用于计算 AFIO 的 EXTICR 数组索引和内部 4 位偏移值。也会通过移位运算定位到 GPIO 配置寄存器CRL或CRH中的对应位域。第二题,这里的iocurrent中的值和传入参数GPIO_Pin一模一样。

是不是有些多余?这里这样写有什么优点呢?这个函数其实可以同时处理多个引脚的,这里假设要同时处理PC13、PC14、PC15。在函数中我们只需要用"|"这个符号隔开就可以了。这时随着while循环的执行。PC13、PC14、PC15就会被执行相同的操作了。在前面的视频中,如果我们要将IMR位15置零,我们会在这里写入0b1 << 15。

这里的运算结果和iocurrent是一模一样的。这个固定的值也被称作掩码。英文常用mask。第三题,这里的position-8左移两位的结果为什么正好是传入引脚对应MODE位和CNF位的偏移值?这里首先假设将十进制数1。左移2位。相当于在原来的基础上乘以10的二次方。同理将二进制数左移2位。

相当于在原基础上乘以2的2次方,也就是4。由于在stm32中引脚0到7使用的是CRL寄存器,引脚8到15使用的是CRH寄存器,也就是说在CRH寄存器中。序号是从8开始的,所以,要用引脚号减8,而每个引脚占4位。所以(position - 8) * 4得到的正好是该引脚在CRH寄存器中的偏移值。

学而不思则罔,思而不学则殆。今天的源码看似不长。但里面藏着寄存器设计、位运算、多引脚遍历和 HAL 库的统一接口这么多知识点。只有多想、多练,才能真正变成自己的东西。好了,今天的视频就到这里。下期视频,我会继续围绕 EXTI 相关源码做更深入的解析。带你摸清外部中断从初始化到触发响应的完整链路。

一定要关注、点赞、收藏、评论。你们的支持是我这样的小博主持续更新下去的最大动力。期待下期视频再见。