为何gpio_to_irq不能静态使用?【转】

本文探讨了函数与宏定义在结构体中静态和动态使用的区别。实验结果显示,函数仅支持动态调用,而宏定义则既支持静态也支持动态调用。通过对gpio_to_irq的分析,解释了为何某些情况下不能静态使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前在调试传感器模块的时候发现,在结构体声明的时候irq成员使用gpio_to_irq会报错,而动态使用的话就没有问题。就对gpio_to_irq为什么不能静态使用产生了疑问。恰巧最近又有朋友遇到了同样的问题,也就提醒了我,去找出原因。

转自:http://blog.youkuaiyun.com/airk000/article/details/23339257

开始测试

我写了一个简单的linux执行程序进行测试,因为在内核源码中发现不同平台对gpio_to_irq的定义不同,有的是宏定义,而更多的则直接是函数。所以在这个测试程序中我也以这一点作为切入点,进行测试。

 

函数


#include <stdio.h>

 static int plus_one(int x)
 {
     return (x + 1);
 }

 struct test {
     int num;
     char *name;
 };

 struct test test1 = { 
     .num = plus_one(5),
     .name = "test",
 };

 int main(void)
 {
     printf("%d %s\n", test1.num, test1.name);
     return 0;
 }

编译,果然出错了:

main.c:14:5: error: initializer element is not constant
     .num = plus_one(5),
     ^
main.c:14:5: error: (near initialization for ‘test1.num’)

可见,函数是不能作为结构体声明静态使用的。那么改为动态试一试看:

 struct test test1 = { 
     .name = "test",
 };

 int main(void)
 {
     test1.num = plus_one(5);

     printf("%d %s\n", test1.num, test1.name);
     return 0;
 }

编译,通过,能够输出想要的结果。

结论:函数不能在结构体声明等代码中静态使用,即使函数内容再简单。只能以动态方式使用函数。在Linux内核的omap2平台代码中也印证了这一点,许多设备资源都是在初始化函数中(即资源生效前)进行gpio_to_irq的动态赋值。

 

宏定义

使用宏定义代替上述代码中的plus_one函数

#define plus_one(x) ((x) + 1)

...

 struct test test1 = { 
     .num = plus_one(5),
     .name = "test",
 };

 int main(void)
 {
     printf("%d %s\n", test1.num, test1.name);
     return 0;
 }

编译,通过,输出我们希望的结果。这证明宏定义可以静态使用,那么动态呢?

 struct test test1 = { 
     .name = "test",
 };

 int main(void)
 {
     test1.num = plus_one(5);

     printf("%d %s\n", test1.num, test1.name);
     return 0;
 }

编译,通过,输出想要的结果。OK,这说明宏定义同样可以进行动态引用。

结论:宏定义在代码中无论是静态引用还是动态引用均可以。

 

总结

通过测试代码可以看出函数的使用有局限性:只能动态引用,而不能静态使用。宏定义就显得友好多了,静态、动态使用均可。回到开始的问题gpio_to_irq为什么不能静态使用?就是因为很多平台代码都将gpio_to_irq实现成为了函数,而非宏定义,这样就只能进行动态引用。但是,这在驱动编写中也不是什么问题,在上面已经说过,只要在设备资源生效前(设备注册前)将其irq动态赋值好就可以了,现有的很多成熟平台也是这样做的,并没有问题。这里我探究这个问题只是因为自己的好奇心而已。

【作者】 张昺华
【新浪微博】 张昺华--sky
【twitter】 @sky2030_
【facebook】 张昺华 zhangbinghua
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
#include "TCRT5000.h" #include "stm32f4xx_gpio.h" #include "stm32f4xx_exti.h" #include "stm32f4xx_syscfg.h" #include "misc.h" #define NULL 0 // 静态函数指针,用于中断回调 static GPIO_InterruptCallback interruptCallback = NULL; // 静态标志量 static uint8_t pick_stau=0; void GPIO_SetInterruptCallback(GPIO_InterruptCallback callback) { interruptCallback = callback; } // 获取EXTI线对应的IRQn static IRQn_Type Get_EXTI_IRQn(uint16_t EXTI_Line) { switch (EXTI_Line) { case EXTI_Line0: return EXTI0_IRQn; case EXTI_Line1: return EXTI1_IRQn; case EXTI_Line2: return EXTI2_IRQn; case EXTI_Line3: return EXTI3_IRQn; case EXTI_Line4: return EXTI4_IRQn; case EXTI_Line5: case EXTI_Line6: case EXTI_Line7: case EXTI_Line8: case EXTI_Line9: return EXTI9_5_IRQn; case EXTI_Line10: case EXTI_Line11: case EXTI_Line12: case EXTI_Line13: case EXTI_Line14: case EXTI_Line15: return EXTI15_10_IRQn; default: return (IRQn_Type)0; } } void GPIO_LowLevelInterrupt_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) { GPIO_InitTypeDef GPIO_InitStruct = {0}; EXTI_InitTypeDef EXTI_InitStruct = {0}; NVIC_InitTypeDef NVIC_InitStruct = {0}; uint8_t GPIO_PortSource = 0; uint8_t GPIO_PinSource = 0; int i; // 1. 确定GPIO所属时钟域 uint32_t RCC_AHB1Periph; if(GPIOx == GPIOA) RCC_AHB1Periph = RCC_AHB1Periph_GPIOA; else if(GPIOx == GPIOB) RCC_AHB1Periph = RCC_AHB1Periph_GPIOB; else if(GPIOx == GPIOC) RCC_AHB1Periph = RCC_AHB1Periph_GPIOC; else if(GPIOx == GPIOD) RCC_AHB1Periph = RCC_AHB1Periph_GPIOD; // 添加其他端口判断... // 2. 使能时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); // 3. 配置GPIO为输入模式(上拉) GPIO_InitStruct.GPIO_Pin = GPIO_Pin; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz; GPIO_Init(GPIOx, &GPIO_InitStruct); // 4. 确定EXTI线和端口源 if(GPIOx == GPIOA) GPIO_PortSource = EXTI_PortSourceGPIOA; else if(GPIOx == GPIOB) GPIO_PortSource = EXTI_PortSourceGPIOB; else if(GPIOx == GPIOC) GPIO_PortSource = EXTI_PortSourceGPIOC; else if(GPIOx == GPIOD) GPIO_PortSource = EXTI_PortSourceGPIOD; for(i=0; i<16; i++) { if(GPIO_Pin == (1 << i)) { GPIO_PinSource = i; break; } } // 5. 配置SYSCFG映射 SYSCFG_EXTILineConfig(GPIO_PortSource, GPIO_PinSource); // 6. 配置EXTI中断 EXTI_InitStruct.EXTI_Line = (1 << GPIO_PinSource); EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发(高→低) EXTI_InitStruct.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStruct); // 7. 配置NVIC NVIC_InitStruct.NVIC_IRQChannel = Get_EXTI_IRQn(1 << GPIO_PinSource); NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x0F; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x0F; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); } uint8_t GPIO_CheckPinLevel(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) { return GPIO_ReadInputDataBit(GPIOx, GPIO_Pin); } // 通用EXTI中断处理函数 void Handle_EXTI_IRQ(uint16_t EXTI_Line, uint32_t EXTI_Line_bits) { if(EXTI_GetITStatus(EXTI_Line) != RESET) { // 调用用户回调函数 if(interruptCallback != NULL) { interruptCallback(EXTI_Line); //****************************************************** pick_stau=1; //****************************************************** } EXTI_ClearITPendingBit(EXTI_Line); } } // EXTI中断处理函数模板 - 需在stm32f4xx_it.c中按需实现 void EXTI0_IRQHandler(void) { Handle_EXTI_IRQ(EXTI_Line0, 1<<0); } void EXTI1_IRQHandler(void) { Handle_EXTI_IRQ(EXTI_Line1, 1<<1); } // ...其他中断处理函数类似 //TCRT IO初始化 void TCRT_Init(void) { GPIO_LowLevelInterrupt_Init(GPIOC,GPIO_Pin_0 |GPIO_Pin_1 |GPIO_Pin_2 |GPIO_Pin_3 |GPIO_Pin_4 |GPIO_Pin_5 |GPIO_Pin_6 |GPIO_Pin_7 |GPIO_Pin_8 ); } 解释
最新发布
06-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值