回调函数与位运算

简介: 在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,其它位保持不变的效果,实现了单独控制对应引脚电平输出高。

内容来源:百问网


相关文章
|
JavaScript 前端开发 API
基于MediaPipe 和 TensorFlow.js的3D手势检测
最近看到Google发布了Tensorflow.js关于手势姿态检测模型的新版本,该模型改进了2D精度,支持3D,并具有同时预测双手关键点的新能力。晚上下班回来,把源码下载下来跑了一下demo,被这个精度惊艳到了。瞬间萌生了了一个想法,有了这个3D手势检测算法,那我是不是可以把佛山无影手的招式记录下来,然后把它教给机器人,然后让机器人给我当保镖!
基于MediaPipe 和 TensorFlow.js的3D手势检测
|
存储 移动开发 开发框架
【微信小程序 | 实战开发】常用小程序框架介绍
【微信小程序 | 实战开发】常用小程序框架介绍
4767 0
【微信小程序 | 实战开发】常用小程序框架介绍
|
20天前
|
缓存 人工智能 测试技术
Qwen3.8‑Flash多模态大模型全解析:MoE高效架构、百万上下文、Agent开发与API实操完整指南
在AI业务规模化落地的阶段,很多线上业务面临两难的现实困境:一方面线上服务需要高吞吐、低时延,能够支撑大量用户并发请求;另一方面又需要兼顾长文档解析、图文视频多模态理解、工具调用Agent等复杂能力。传统稠密大模型,想要获得强能力就要付出极高推理成本;轻量化小模型虽然速度快、价格低,但面对复杂逻辑、长文本、图表解析任务效果会明显下滑。
189 0
|
1月前
|
存储 监控 API
二手 ERP 系统对接闲鱼 API:落地方案、接口边界与业务踩坑
二手ERP对接闲鱼需分清两大接口:闲管家开放平台(仅管理自有店铺,支持商品/订单/物流双向操作)与第三方数据接口(仅读取全网商品,用于选品估价)。二者职责分明,混用将导致业务故障。务必通过正规渠道接入,避免逆向抓包。
259 0
|
1月前
|
数据采集 JSON 物联网
大模型微调入门教程:LoRA、QLoRA、全量微调在百炼平台的实操指南
大模型微调怎么做?本文详解LoRA、QLoRA、全量微调三种方法,并在百炼平台提供完整实操步骤,帮你快速掌握大模型微调技术。
472 0
|
5月前
|
存储 Java 数据库连接
ThreadLocal 深度剖析:底层实现、内存泄漏根因与生产环境避坑指南
ThreadLocal实现线程间数据隔离,但易引发内存泄漏。本文详解其核心原理(ThreadLocalMap、弱引用key/强引用value)、内存泄漏根因,并提供remove清理、try-finally保障、TransmittableThreadLocal等生产级避坑方案。
381 13
|
存储 监控 NoSQL
【干货满满】电商API数据抓取实战:从商品信息到订单管理全链路实现
本文详解构建电商API数据抓取系统,涵盖商品采集、订单管理、防封策略、数据存储与分析,适用于价格监控、供应链管理等场景。
|
传感器
stm32f407探索者开发板(二十二)——通用定时器基本原理讲解
stm32f407探索者开发板(二十二)——通用定时器基本原理讲解
2040 0
|
自然语言处理 安全 API
MCP:让大语言模型不只是聊天,更能"动手做事"的开放协议
MCP(Model Context Protocol,模型上下文协议)是一种开放协议,旨在标准化应用程序向大语言模型提供上下文的方式。通过 MCP,大模型不仅能聊天,还能执行如查数据库、写代码等任务。相比 Function Calling,MCP 解耦了工具调用,提升灵活性和复用性,实现“一次开发,多处调用”。其架构包括 MCP Hosts、Clients、Servers,以及本地数据源和远程服务,支持安全访问多种资源。相关资源可在 GitHub 和 mcp.so 获取。
2049 3
MCP:让大语言模型不只是聊天,更能"动手做事"的开放协议
|
移动开发 JavaScript 前端开发
前端(九)——探索微信小程序、Vue、React和Uniapp生命周期(下)
前端(九)——探索微信小程序、Vue、React和Uniapp生命周期(下)
754 0

热门文章

最新文章