《嵌入式操作系统》_使用GPIOLIB编写驱动_添加驱动到内核中_20260328

1. 流程分析

1.使用gpio_request申请某个gpio

2.使用gpio_direction_input/gpio_direction_output设置模式

3.使用gpio_get_value/gpio_set_value操控gpio

2. 逐次申请gpio引脚

2.1 初始化函数

cpp 复制代码
#include <linux/gpio.h>	

#define LED_ON 0
#define LED_OFF 1

static struct led_classdev my_led_cdev1;	// 定义一个led_classdev结构体变量
static struct led_classdev my_led_cdev2;	// 定义一个led_classdev结构体变量
static struct led_classdev my_led_cdev3;	// 定义一个led_classdev结构体变量


static int lxy210_led_init(void)
{
	
	if(gpio_request(S5PV210_GPJ0(3), "LED1") < 0) {
		printk(KERN_ERR "Failed to request GPIO for LED1\n");
		return -EBUSY;
	}
	else{
		gpio_direction_output(S5PV210_GPJ0(3), 1); // 设置GPIO为输出,并默认关闭LED
	}

	if(gpio_request(S5PV210_GPJ0(4), "LED2") < 0) {
		printk(KERN_ERR "Failed to request GPIO for LED2\n");
		gpio_free(S5PV210_GPJ0(3));
		return -EBUSY;
	}
	else{
		gpio_direction_output(S5PV210_GPJ0(4), 1); // 设置GPIO为输出,并默认关闭LED
	}

	if(gpio_request(S5PV210_GPJ0(5), "LED3") < 0) {
		printk(KERN_ERR "Failed to request GPIO for LED3\n");
		gpio_free(S5PV210_GPJ0(3));
		gpio_free(S5PV210_GPJ0(4));
		return -EBUSY;
	}
	else{
		gpio_direction_output(S5PV210_GPJ0(5), 1); // 设置GPIO为输出,并默认关闭LED
	}
	int ret = -1;
	my_led_cdev1.name = "led1";	// 设置LED设备的名称
	my_led_cdev1.brightness_set = lxy_led1_set;	// 设置LED亮度设置函数指针(如果需要实现亮度控制功能,可以定义一个函数并将其地址赋值给这个成员)
	my_led_cdev1.brightness = LED_OFF; // 设置LED的初始亮度为关闭
	ret = led_classdev_register(NULL, &my_led_cdev1);
	if (ret < 0) {
		gpio_free(S5PV210_GPJ0(3));
		gpio_free(S5PV210_GPJ0(4));
		gpio_free(S5PV210_GPJ0(5));
		return ret;
	}

	my_led_cdev2.name = "led2";	// 设置LED设备的名称
	my_led_cdev2.brightness_set = lxy_led2_set;	// 设置LED亮度设置函数指针(如果需要实现亮度控制功能,可以定义一个函数并将其地址赋值给这个成员)
	my_led_cdev2.brightness = LED_OFF; // 设置LED的初始亮度
	ret = led_classdev_register(NULL, &my_led_cdev2);
	if (ret < 0) {
		gpio_free(S5PV210_GPJ0(3));
		gpio_free(S5PV210_GPJ0(4));
		gpio_free(S5PV210_GPJ0(5));
		led_classdev_unregister(&my_led_cdev1);
		return ret;
	}

	my_led_cdev3.name = "led3";	// 设置LED设备的名称
	my_led_cdev3.brightness_set = lxy_led3_set;	// 设置LED亮度设置函数指针(如果需要实现亮度控制功能,可以定义一个函数并将其地址赋值给这个成员)
	my_led_cdev3.brightness = LED_OFF; // 设置LED的初始亮度
	ret = led_classdev_register(NULL, &my_led_cdev3);
	if (ret < 0) {
		gpio_free(S5PV210_GPJ0(3));
		gpio_free(S5PV210_GPJ0(4));
		gpio_free(S5PV210_GPJ0(5));
		led_classdev_unregister(&my_led_cdev1);
		led_classdev_unregister(&my_led_cdev2);
		return ret;
	}
	
	return 0;
}

2.2 控制函数

cpp 复制代码
static void lxy_led1_set(struct led_classdev *led_cdev,enum led_brightness value)
{
	printk(KERN_INFO "LED1 brightness set to %d\n", value);
	//writel(0x11111111, S5PV210_GPJ0CON);
	if (value == LED_ON) {
		// 这里可以添加代码来控制LED亮起
		//writel((readl(S5PV210_GPJ0DAT) & ~(1<<3)), S5PV210_GPJ0DAT);	// 将GPH3DAT寄存器映射到内核地址空间,并写入0x1来点亮LED
		gpio_set_value(S5PV210_GPJ0(3), 1);
	} else {
		// 这里可以添加代码来控制LED熄灭
		//writel((readl(S5PV210_GPJ0DAT) | (1<<3)), S5PV210_GPJ0DAT);	// 将GPH3DAT寄存器映射到内核地址空间,并写入0x0来熄灭LED
		gpio_set_value(S5PV210_GPJ0(3), 0);
	}
}

2.3 驱动卸载函数

cpp 复制代码
static void lxy210_led_exit(void)
{
	gpio_free(S5PV210_GPJ0(3));
	gpio_free(S5PV210_GPJ0(4));
	gpio_free(S5PV210_GPJ0(5));
	led_classdev_unregister(&my_led_cdev1);
	led_classdev_unregister(&my_led_cdev2);
	led_classdev_unregister(&my_led_cdev3);

}

3. 一次申请多个gpio引脚

cpp 复制代码
static struct gpio leds[3] = {
	{.gpio = S5PV210_GPJ0(3), .label = "LED1"},
	{.gpio = S5PV210_GPJ0(4), .label = "LED2"},
	{.gpio = S5PV210_GPJ0(5), .label = "LED3"}
};

static int lxy210_led_init(void)
{
	
	if (gpio_request_array(leds, 3) < 0) {
		printk(KERN_ERR "Failed to request GPIOs for LEDs\n");
		return -EBUSY;
	}
	else{
		gpio_direction_output(S5PV210_GPJ0(3), 1); // 设置GPIO为输出,并默认关闭LED
		gpio_direction_output(S5PV210_GPJ0(4), 1); // 设置GPIO为输出,并默认关闭LED
		gpio_direction_output(S5PV210_GPJ0(5), 1); // 设置GPIO为输出,并默认关闭LED
	}
}

static void lxy210_led_exit(void)
{
	gpio_free_array(leds, 3);
	led_classdev_unregister(&my_led_cdev1);
	led_classdev_unregister(&my_led_cdev2);
	led_classdev_unregister(&my_led_cdev3);

}

4. debugfs

我不知道当前的驱动状态怎么办,使用命令:

bash 复制代码
mount -t debugfs debugfs /tmp

挂载文件系统仿真到/tmp文件夹

你就可以查看当前外设的状态

bash 复制代码
cat gpio

用完之后卸载debugfs

bash 复制代码
umount /tmp

5. 将驱动添加到内核中

现在我们写的驱动都是野生的,也就是说全是我们写在外面的。野生驱动好调试,写进内核的驱动可以在make menuconfig阶段决定是否编译,方便集成。

5.1 修改内容

kconfig、makefile、menuconfig

5.2 驱动存放位置

我们是按照内核提供的驱动框架写的驱动,那就放在/kernel/drivers/leds/目录下

但你单纯把写好的驱动源文件放在这里,肯定不会被编译。

5.3 修改makefile

bash 复制代码
obj-$(CONFIG_LEDS_LXY210)		+= leds-lxy210.o

该文件依赖于CONFIG_LEDS_LXY210这个配置,如果是y就是编译进内核,如果是m就是编译为模块,如果是n就是不必理会。

5.4 修改kconfig

直接添加选项,但注意名称要和makefile中的名称一模一样!!!

bash 复制代码
config LEDS_LXY210
	tristate "LED support by lxy"

5.5 修改menuconfig

出现驱动!!!选择y,然后保存就好了。配置文件中出现CONFIG_LEDS_LXY210=y

编译内核make -j16,复制到tftpboot,重启开发板

开发板自动添加驱动。完成实验!

5.6 那配置为-m会怎么样

.c驱动文件只是会在原路径被编译,并未被执行,需要自己安装模块。

相关推荐
charlie1145141918 小时前
嵌入式C++工程实践第16篇:第四次重构 —— LED模板,从通用GPIO到专用抽象
c语言·开发语言·c++·驱动开发·嵌入式硬件·重构
深圳市九鼎创展科技10 小时前
MT8883 vs RK3588 开发板全面对比:选型与场景落地指南
大数据·linux·人工智能·嵌入式硬件·ubuntu
三品吉他手会点灯13 小时前
STM32 VSCode 开发-C/C++的环境配置中,找不到C/C++: Edit Configurations选项
c语言·c++·vscode·stm32·单片机·嵌入式硬件·编辑器
yu859395815 小时前
STM32 智能红外循迹小车(含码盘测速 + 避障)
stm32·单片机·嵌入式硬件
三品吉他手会点灯15 小时前
STM32 DAP 烧录报错-最终解决方法的原理和操作逻辑
stm32·单片机·嵌入式硬件
fengfuyao98515 小时前
TFT 彩屏 GUI 开发
stm32·嵌入式硬件
海边夕阳200615 小时前
Vibe Coding与SDD规范驱动:从氛围编程到规范驱动开发
人工智能·驱动开发·经验分享
长安第一美人16 小时前
算能 BM1688 低延迟推流:Qt+WebSocket 直出 H5/HDMI
开发语言·网络·嵌入式硬件·websocket·交互
yongui4783416 小时前
STM32 三相电机FOC驱动方案(三电阻单电阻双模式)
stm32·单片机·嵌入式硬件
yong999017 小时前
基于 51 单片机配合霍尔传感器实现计数 + 转速测量
单片机·嵌入式硬件