linux c # ## __VA_ARGS__ 使用

本文介绍了C++预处理器宏中的特殊符号#、##和__VA_ARGS__的用途。#用于将参数转换为字符串,##用于连接两个标识符,__VA_ARGS__用于处理可变参数列表。示例展示了它们在宏定义和函数调用中的应用。

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

目录

 # 用来把参数转换成字符串

## 用于将带参数的宏定义中将两个子串(token)联接起来,从而形成一个新的子串;但它不可以是第一个或者最后一个子串。所谓的子串(token)就是指编译器能够识别的最小语法单元

__VA_ARGS__ 用于在宏替换部分中,表示可变参数列表

##__VA_ARGS__  宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用,否则会编译出错


使用打印信息的接口时,经常见到__VA_ARGS__和##__VA_ARGS__这两个字符串,花时间学习下这部分的知识,发现还有#和##这两个比较有意思的字符串,记下他们的用法:

 # 用来把参数转换成字符串

例:

#include <iostream>
 
#define LOG(x) do { printf("%s=%d\n",#x,x); }while(0)
 
int main()
{
    int score = 96;
    LOG(score);
    LOG(6);
    
    getchar();
    return 0;
}

输出:

## 用于将带参数的宏定义中将两个子串(token)联接起来,从而形成一个新的子串;但它不可以是第一个或者最后一个子串。所谓的子串(token)就是指编译器能够识别的最小语法单元

例:

#include <iostream>
 
#define LOG(x) log##x()
 
void logA(){
    printf("log func A \n");
}
 
void logB(){
    printf("log func B\n");
}
 
int main()
{
    LOG(A);
 
    getchar();
    return 0;
}

输出:

__VA_ARGS__ 用于在宏替换部分中,表示可变参数列表

例:

#include <iostream>
 
#define LOG(...)  printf(__VA_ARGS__);
 
int main()
{
    LOG("score is %d\n",96);
 
    getchar();
    return 0;
}

输出:

##__VA_ARGS__  宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用,否则会编译出错

#include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/gpio.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_gpio.h> #include <asm/uaccess.h> #include <asm/io.h> #include <linux/platform_device.h> struct led_driver_data { uint32_t nr; uint32_t ng; uint32_t nb; uint32_t nw; uint32_t s1; uint32_t s2; uint32_t s3; uint32_t nr_flag; uint32_t ng_flag; uint32_t nb_flag; uint32_t nw_flag; uint32_t s1_flag; uint32_t s2_flag; uint32_t s3_flag; struct device *dev; }; struct led_driver_data *g_data; #define SPRD_LEDS_OF_DEVICE_ID_NAME "sprd,led_cpe" #define SPRD_LEDS_SYSFS_GROUP_NAME "led_cpe" #define LED_LOGI(fmt, ...)\ pr_info("[LED] %s:" fmt "", __func__, ##__VA_ARGS__) #define LED_LOGD(fmt, ...)\ pr_debug("[LED] %s:" fmt "", __func__, ##__VA_ARGS__) #define LED_LOGE(fmt, ...)\ pr_err("[LED] %s:" fmt "", __func__, ##__VA_ARGS__) static ssize_t nr_show(struct device *dev, struct device_attribute *attr, char *buf) { static uint32_t gpio_nr = 0; gpio_nr = gpio_get_value(g_data->nr); LED_LOGI("get gpio_nr=%d", gpio_nr); return sprintf(buf, "%d", gpio_nr); } static ssize_t nr_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { static uint32_t gpio_nr = 0; if (kstrtouint(buf, 10, &gpio_nr) != 0) return -EINVAL; LED_LOGI("set gpio_nr=%d, count=%zu", gpio_nr, count); if(gpio_nr == 1) { gpio_set_value(g_data->nr, gpio_nr); } else if(gpio_nr == 0){ gpio_set_value(g_data->nr, gpio_nr); } return count; } static ssize_t ng_show(struct device *dev, struct device_attribute *attr, char *buf) { static uint32_t gpio_ng = 0; gpio_ng = g
03-22
<think>好的,用户想了解Linux内核驱动程序中关于GPIO操作的实现细节,特别是针对sprd平台和led_cpe的驱动。首先,我需要确认用户提到的sprd是Spreadtrum(展讯)平台,而led_cpe可能是指某种LED设备,可能用于客户前置设备(CPE)。接下来,我需要整理Linux内核中GPIO驱动的通用实现方式,然后结合sprd平台的具体情况进行分析。 首先,Linux内核中的GPIO驱动通常遵循一定的框架。用户提到的HAL_GPIO_ReadPin函数属于HAL库,常见于嵌入式系统如STM32,但Linux内核中的GPIO操作更倾向于使用GPIO子系统。GPIO子系统提供了一套统一的接口,包括gpiod_get、gpiod_direction_output、gpiod_set_value等函数,用于控制GPIO引脚。因此,需要向用户说明Linux内核与HAL库的不同之处,并引导用户理解内核的API。 接下来,针对sprd平台,我需要查找相关资料。展讯平台的Linux内核驱动可能位于drivers/gpio/gpio-sprd.c或类似的目录中。该驱动通常会实现平台特定的GPIO控制,包括寄存器操作、中断处理等。同时,需要提到设备树(Device Tree)的作用,因为在ARM架构中,设备树用于描述硬件配置,包括GPIO引脚的定义。 然后,关于led_cpe的驱动,LED类驱动在内核中通常使用leds子系统,对应的驱动可能位于drivers/leds/目录下。对于sprd平台上的LED驱动,可能会结合GPIO子系统和leds框架,例如使用GPIO来控制LED的亮灭。需要说明如何通过设备树配置LED设备,以及驱动中如何注册led_classdev结构体,并实现相应的控制函数。 此外,用户可能需要了解如何查找具体的代码实现。建议用户在内核源码中搜索相关关键字,如sprd_gpio、led_cpe,或者查看设备树文件中对应的节点。同时,可以指导用户使用git grep命令在内核源码库中查找相关代码,或者参考内核文档中关于GPIO和LED的部分。 还需要注意用户可能的实际需求,比如调试GPIO驱动、编写自定义的LED驱动,或者了解现有的实现以进行二次开发。因此,回答中应包含足够的细节和示例,帮助用户定位和理解相关代码。例如,设备树中的GPIO定义可能如下所示: ``` led_cpe: led { compatible = "gpio-leds"; gpios = <&soc_gpio 12 GPIO_ACTIVE_HIGH>; label = "cpe_led"; }; ``` 驱动代码可能会使用gpio-leds的兼容字符串,并自动匹配内核中的已有驱动,无需编写额外代码,除非有特殊需求。对于自定义行为,用户需要实现probe函数,配置GPIO并注册LED设备。 最后,需要确保回答的准确性,避免提供错误的信息。如果对sprd平台的具体实现细节不确定,应建议用户参考内核源码或官方文档,并指出可能的代码位置。同时,生成的相关问题应围绕GPIO子系统、设备树配置、LED驱动开发等,帮助用户进一步深入理解。</think>在Linux内核中,GPIO驱动的实现涉及多个关键子系统,以下是针对Spreadtrum(sprd)平台和led_cpe驱动的实现分析: ### 一、GPIO子系统实现层级 1. **硬件抽象层** sprd平台GPIO驱动位于`drivers/gpio/gpio-sprd.c`,通过`platform_driver`注册GPIO控制器,实现`gpio_chip`接口定义的操作方法: ```c static const struct gpio_chip sprd_gpio_chip = { .direction_input = sprd_gpio_direction_input, .get = sprd_gpio_get, .direction_output = sprd_gpio_direction_output, .set = sprd_gpio_set }; // [^1] ``` 2. **设备树配置** 在`.dts`文件中定义GPIO引脚映射: ``` gpio_ctrl: gpio@40280000 { compatible = "sprd,sc9860-gpio"; reg = <0x40280000 0x1000>; gpio-controller; #gpio-cells = <2>; }; ``` ### 二、LED驱动实现 1. **LED子系统集成** `led_cpe`驱动通常使用`leds-gpio`通用驱动框架,设备树节点示例如下: ``` leds { compatible = "gpio-leds"; cpe_led { label = "cpe_status"; gpios = <&gpio_ctrl 15 GPIO_ACTIVE_HIGH>; linux,default-trigger = "heartbeat"; }; }; ``` 2. **驱动关键操作** 在`drivers/leds/leds-gpio.c`中实现核心控制逻辑: ```c static int gpio_led_set(struct led_classdev *led_cdev, enum led_brightness value) { gpiod_set_value(led->gpiod, value); } ``` ### 三、关键调试方法 - **GPIO状态检查**:通过`/sys/kernel/debug/gpio`查看GPIO注册状态 - **LED控制测试**:直接写入亮度值到sysfs接口 ```bash echo 1 > /sys/class/leds/cpe_status/brightness ``` ### 四、平台特殊处理 sprd平台在`arch/arm/mach-sprd/`目录下的板级代码中,会通过`gpiochip_add_pin_range()`建立GPIO编号与物理引脚的映射关系,这对正确解析设备树中的GPIO参数至关重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值