回调函数与位运算

简介: 在C语言中回调函数是函数指针的高级应用。所谓回调函数,一个笼统简单的介绍就是一个被作为参数传递的函数。从字面上看,回调函数的意思是:一个回去调用的函数,如何理解这句话呢?从逻辑上分析,要“回去”,必然存在着一个已知的目的地,然后在某一个时刻去访问;那么回调函数就是存在一个已知的函数体A,将这个函数体A的地址即函数名“A”(函数名即是这个函数体的函数指针,指向这个函数的地告知给另外某个函数B,当那个函数B执行到某一步的时候就会去执行函数A。

1. 回调函数

在C语言中回调函数是函数指针的高级应用。所谓回调函数,一个笼统简单的介绍就是一个被作为参数传递的函数。从字面上看,回调函数的意思是:一个回去调用的函数,如何理解这句话呢?从逻辑上分析,要“回去”,必然存在着一个已知的目的地,然后在某一个时刻去访问;那么回调函数就是存在一个已知的函数体A,将这个函数体A的地址即函数名“A”(函数名即是这个函数体的函数指针,指向这个函数的地告知给另外某个函数B,当那个函数B执行到某一步的时候就会去执行函数A。

首先是GPIO的回调函数声明:

__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)

可以看到其函数名是:HAL_GPIO_EXTI_Callback,形参是GPIO_Pin表示引脚号(Px0~Px15,x=A,B,C,D,E,F,G),从这个函数的名称出发,可以大致明确这是一个引脚的外部中断(EXTI)的回调函数。


然后大家看到前面还有个“__weak”,这是“弱函数”的修饰符,告诉编译器如果用户在其它地方用void

HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)重新定义了此回调函数那么优先调用用户定义的,否则调用这个弱函数修饰的回调函数。紧接着我们来看此回调函数是在哪里被调用的:

void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{ 
 /* EXTI line interrupt detected */
 if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u) 
 { 
 __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin); 
 HAL_GPIO_EXTI_Callback(GPIO_Pin); 
 } 
} 
  • 可以看到是在GPIO的外部中断服务函数中被调用的,与前面所说的这是一个外部引脚中断回调函数印证一致了。

  • GPIO的回调函数到此就说完了。其实STM32的HAL库中其它大多数的外设的回调函数基本都是如此,用户如果设计需求,就自己重定义需求的回调函数,然后在中断中被调用。

2. 位运算

位运算是指二进制位之间的运算。在嵌入式系统设计中,常常要处理二进制的问题,例如将某个寄存器中的某一个位置1或者置0,将数据左移5位等。

7795c3c87b37479e9539124a3dc96028.png

1. 按位与运算符(&)

参与运算的两个操作数,每个二进制位进行“与”运算,若两个都为1,结果为1,否者为0。

例如,1011&1001,第一位都为1,结果为1;第二位都为0,结果为0;第三位一个为1,一个为0,结果为0;第四位都为1,结果为1。最后结果为1001。


2. 按位或运算符(|)

参与运算的两个操作数,每个二进制位进行“或”运算,若两个都为0,结果为1,否者为1。

例如,1011 | 1001,第一位都为1,结果为1;第二位都为0,结果为0;第三位一个为1,一个为0,结果为1;第四位都为1,结果为1。最后结果为1011。


3. 按位取反运算符(~)

按位取反运算符用于对一个二进制数按位取反。

例如,~1011,第一位为1,取反为0;第二位为0,取反为1;第三位为1,取反为0,结果为1;第四位为1,取反为0。最后结果为0100。


4. 左移(<<)和右移(>>)运算符

左移(<<)运算符用于将一个数左移若干位,右移(>>)运算符用于将一个数右移若干位。

例如,假设val为unsigned char型数据,对应的二进制数为10111001。若val=va<<3,表示val左移3位,然后赋值给val,左移过程中,高位移出去后被丢弃,低位补0,最后val结果为11001000;若val=val>>3,表示val右移3位,然后赋值给val,右移过程中,低位移出去后被丢弃,高位补0,最后val结果为00010111。


5. 清0或置1

在嵌入式中,经常使用位预算符实现清0或置1。

例如,MCU的ODR寄存器控制引脚的输出电平高低,寄存器为32位,每位控制一个引脚的电平。假设需要控制GPIOB的1号引脚输出电平的高低,设置该寄存器第0位为1,输出高电平,设置该寄存器第0位为0,输出低电平。

#define GPIOB_ODR (*(volatile unsigned int *)(0x40010C0C))
GPIOB_ODR &= ~(1<<0);
GPIOB_ODR |= (1<<0);

第一行:使用#define定义了GPIOB_ODR 对应的内存地址为0x40010C0C。该地址为MCU的ODR寄存器地址。


第三行:GPIOB_ODR &= ~(1<<0)实际是GPIOB_ODR = GPIOB_ODR &~(1<<0),先将GPIOB_ODR和 ~(1<<0)的进行与运算,运算结果赋值给GPIOB_ODR。1<<0的值为00000000

00000000 00000000 00000001, 再取反为11111111 11111111 11111111 11111110,则GPIO_ODR的第0位和0与运算,结果必为0,其它位和1运算,由GPIO_ODR原来的值决定结果。这就实现了,只将GPIO_ODR的第0位清0,其它位保持不变的效果,实现了单独控制对应引脚电平输出低。


第四行:GPIOB_ODR |= (1<<0)实际是GPIOB_ODR = GPIOB_ODR | (1<<0),先将GPIOB_ODR和(1<<0) 的进行或运算,运算结果赋值给GPIOB_ODR。1<<0的值为00000000

00000000 00000000 00000001,则 GPIO_ODR的第0位和0或运算,结果必为1,其它位和0运算,由GPIO_ODR原来的值决定结果。这就实现了,只将GPIO_ODR的第0位置1,其它位保持不变的效果,实现了单独控制对应引脚电平输出高。

内容来源:百问网


目录
打赏
0
0
0
0
2
分享
相关文章
一文彻底拿下,赶紧本地部署DeepSeek体验一下最牛的大模型
本文介绍如何本地化部署DeepSeek大模型(deepseek-r1)及open-webui的安装过程,包括命令行操作、版本兼容性处理等详细步骤。DeepSeek号称“国运级”大模型,性能媲美OpenAI,支持直接对话,降低使用门槛。通过本教程,读者可以快速上手体验这一强大的推理模型。
290 0
一文彻底拿下,赶紧本地部署DeepSeek体验一下最牛的大模型
基于阿里云MQTT服务,设计一个STM32的智能光伏控制系统
这篇文章详细介绍了利用STM32F103C8T6单片机实现光伏发电系统的关键技术。全文分为四章:第一章阐述了光伏发电的背景、意义及应用场景,强调其在绿色能源领域的重要性。第二章介绍了如何通过STM32F103C8T6及光敏电阻和伺服电机实现光线追踪系统,详细描述了硬件选择、连接及使用HAL库编写的单片机程序。第三章讲解了最大功率点追踪(MPPT)的原理,并展示了如何利用STM32F103C8T6和相关传感器、DC-DC转换器实现MPPT功能。第四章描述了如何通过STM32F103C8T6与SIM7600CE 4G模块连接到阿里云MQTT服务,实现设备状态数据的远程传输和控制。本文提供了全面的硬
17964 5
前端工程化:Webpack与Gulp的构建工具选择与配置优化
【10月更文挑战第27天】在现代前端开发中,构建工具的选择对项目的效率和可维护性至关重要。本文比较了Webpack和Gulp两个流行的构建工具,介绍了它们的特点和适用场景,并提供了配置优化的最佳实践。Webpack适合大型模块化项目,Gulp则适用于快速自动化构建流程。通过合理的配置优化,可以显著提升构建效率和性能。
147 2
深度学习中的卷积神经网络(CNN)及其在图像识别中的应用
【9月更文挑战第13天】本文将深入浅出地介绍卷积神经网络(CNN)的基本原理,并探讨其在图像识别领域的应用。通过实例演示如何利用Python和TensorFlow框架实现一个简单的CNN模型,我们将一步步从理论到实践,揭示CNN如何改变现代图像处理技术的面貌。无论你是深度学习新手还是希望深化理解,这篇文章都将为你提供价值。
云计算与网络安全:云服务、网络安全、信息安全等技术领域的深度探讨
【7月更文挑战第24天】本文旨在深入探讨云计算与网络安全之间的关系,包括云服务、网络安全、信息安全等技术领域。我们将从云计算的基本概念出发,分析其对网络安全的影响,以及如何通过技术手段保障云计算环境下的网络安全。同时,我们还将探讨网络安全在云计算环境下的重要性,以及如何通过信息安全技术保护用户数据和隐私。最后,我们将展望云计算与网络安全领域的未来发展趋势,为读者提供一些启示和建议。
385 5
非科班计算机专业的转码秋招记录
【2月更文挑战第26天】本文介绍地理信息科学(GIS)专业的2024届应届生,在研三上学期期间,寻找后端研发、软件开发等IT方向工作的非科班转码秋招情况~
216 3
非科班计算机专业的转码秋招记录
【亮剑】分布式锁是保证多服务实例同步的关键机制,常用于互斥访问共享资源、控制访问顺序和系统保护,如何使用注解来实现 Redis 分布式锁的功能?
【4月更文挑战第30天】分布式锁是保证多服务实例同步的关键机制,常用于互斥访问共享资源、控制访问顺序和系统保护。基于 Redis 的分布式锁利用 SETNX 或 SET 命令实现,并考虑自动过期、可重入及原子性以确保可靠性。在 Java Spring Boot 中,可通过 `@EnableCaching`、`@Cacheable` 和 `@CacheEvict` 注解轻松实现 Redis 分布式锁功能。
209 0
Centos7配置DHCP(简单模式)
Centos7配置DHCP(简单模式)
903 0
Centos7配置DHCP(简单模式)
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等