当前位置: 首页 > article >正文

I.MX6U嵌入式Linux Platform设备驱动开发(2)自带LED和杂项驱动

1、自带LED

要使用 Linux 内核自带的 LED 灯驱动首先得先配置 Linux 内核,使能自带的 LED 灯驱
动,进入Linux源码

/linux/IMX6ULL/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
make menuconfig

内核自带的驱动,都是通过图形化界面配置,选择使能或者不使能。使能驱动以后在.config里面就会存在:CONFIG_LEDS_GPIO=y
在linux内核源码里面一般驱动文件夹下Makefile会使用CONFIG_XXX来决定要编译哪个文件。
LED 灯驱动文件为/drivers/leds/leds-gpio.c

也可以通过leds-gpio.c来查找配置的地方。打开配置页面,输入/,可以进入查找页面,输入CONFIG_XXX开头的,可以找到,有时候可以找到很多条,需要哪一条就输入那条前面的数字,即可进入使用说明。

1.1、自带驱动分析

LED 驱动会采用 module_platform_driver 函数向 Linux 内核注册platform 驱动,module_platform_driver 定义在 include/linux/platform_device.h 文件中,为一个宏。
module_platform_driver 依赖 module_driver,module_driver 也是一个宏,定义在include/linux/device.h 文件中。
将:

module_platform_driver(gpio_led_driver)

展开以后就是:

static int __init gpio_led_driver_init(void) 
{ 
	return platform_driver_register (&(gpio_led_driver)); 
} 
module_init(gpio_led_driver_init); 

static void __exit gpio_led_driver_exit(void) 
{ 
	platform_driver_unregister (&(gpio_led_driver) ); 
} 
module_exit(gpio_led_driver_exit);

因此 module_platform_driver 函数的功能就是完成 platform 驱动的注册和删除。

当驱动和设备匹配以后 gpio_led_probe 函数就会执行,此函数主要是从设备树中获取 LED
灯的 GPIO 信息;
如果使用设备树的话,使用 gpio_leds_create 函数从设备树中提取设备信息,获取到的 LED 灯 GPIO 信息保存在返回值中。

1.2、内核自带LED驱动使用

(1)首先将驱动编译进内核里面;
(2)根据绑定文档在设备树里面添加对应的设备节点信息;
如果无设备树,那么就要使用platform_device_register向总线注册设备;
如果有设备树,那么直接修改设备树,添加指定的节点。

绑定文档所在位置:源码->Documentation->leds,参照文档来添加设备树节点。

在Ubuntu中,打开内核源码找到,imx6ul-alientek-emmc.dts,找到根节点,在根节点的最后面添加子节点,

dtsleds {
	compatible = "gpio-led";//一定要和驱动对应起来,找到gpio的匹配表,属性值为gpio-led
	led0 {
		lable = "red";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_gpioled>;
		gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
		default-state = "on";
	};
};

注意:别忘了检查gpio1 IO03有没有被其他节点占用。直接搜索 gpio1 3

make dtbs
cp arch/arm/boot/zImage /home/yang/linux/tftpboot/ -f
cp arch/arm/boot/dts/imx6uii-alientek-emmc.dtb /home/yang/linux/tftpboot/ -f

使用新的内核和设备树启动开发板。

还可以修改设备树子节点,灯默认打开,并作为心跳灯

dtsleds {
	compatible = "gpio-led";//一定要和驱动对应起来,找到gpio的匹配表,属性值为gpio-led
	led0 {
		lable = "red";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_gpioled>;
		gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
		default-state = "on";
		linux,default-trigger = "heartbeat";
	};
};

此时,开发板作为心跳灯在闪烁,也可以从控制台关闭心跳灯,打开或者关闭灯。
进入/sys/bus/platform/devices/dtsleds 这个目录。这个目录中有一个leds文件夹,进入到leds目录中,有一个red子目录,这个子目录的名字就是我们在设备树中第 5 行设置的 label 属性值。

/sys/devices/platform/dtsleds/leds # cd red/
/sys/devices/platform/dtsleds/leds/red/ # ls
/sys/devices/platform/dtsleds/leds/red/ # cat trigger
/sys/devices/platform/dtsleds/leds/red/ # echo none > trigger
/sys/devices/platform/dtsleds/leds/red/ # echo heartbeat > trigger
/sys/devices/platform/dtsleds/leds/red/ # ech0 1 > brightness

也可以参照用户体验的那个文档来操作。

2、MISC驱动

MISC 驱动也叫做杂项驱动,也就是当我们板子上的某些外设无法进行分类的时候就可以使用 MISC 驱动。MISC 驱动其实就是最简单的字符设备驱动,通常嵌套在 platform 总线驱动中。

2.1、MISC设备驱动简介

所有的 MISC 设备驱动的主设备号都为 10,不同的设备使用不同的从设备号。
MISC 设备会自动创建 cdev,不需要像我们以前那样手动创建。
需要向 Linux 注册一个 miscdevice 设备,miscdevice是一个结构体,定义在文件 include/linux/miscdevice.h 中。

在miscdevice这个结构体中,定义一个 MISC 设备,以后我们需要设置 minor、name 和 fops 这三个成员变量。
(1)minor 表示子设备号,MISC 设备的主设备号为 10,这个是固定的,需要用户指定子设备号,Linux 系统已经预定义了一些 MISC 设备的子设备号,这些预定义的子设备号定义在
include/linux/miscdevice.h 文件中,我们在使用的时候可以从这些预定义的子设备号中挑选一个,当然也可以自己定义,只要这个子设备号在***当前系统中***没有被其他设备使用接口。
(2)name 就是此 MISC 设备名字,当此设备注册成功以后就会在/dev 目录下生成一个名为 name的设备文件。
(3)fops 就是字符设备的操作集合,MISC 设备驱动最终是需要使用用户提供的 fops操作集合。
当设置好 miscdevice 以后就需要使用 misc_register 函数向系统中注册一个 MISC 设备。们卸载设备驱动模块的时候需要调用 misc_deregister 函数来注销掉 MISC 设备。

2.2、实验程序编写

本章实验我们采用 platform 加 misc 的方式编写 beep 驱动,这也是实际的 Linux 驱动中很常用的方法。采用 platform 来实现总线、设备和驱动,misc 主要负责完成字符设备的创建。

cd /liunx/IMX6ULL/Linux_Driver/
mkdir 19_miscbeep
cp 18_dtsplatform/ * 19_miscbeep/ -rf
cp 18_dtsplatform/.vscode 19_miscbeep/ -rf
cd 19_miscbeep/
ls
rm platledAPP 
rm dtsplatform.code-workspace
mv leddriver.c miscbeep.c

2.2.1、beep设备树

需要在 imx6ull-alientek-emmc.dts 文件中创建蜂鸣器设备节点,这里我们直接使用 前面创建的 beep 这个设备节点即可。

beep{
	compatible = "alientek,beep";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_beep>;
	beep-gpios = <&gpio5 1 GPIO_ACTIVE_HIGH>;
	status = "okay";
};

2.2.2、驱动

将Makefile中的.o文件名字修改一下:
打开miscbeep.c文件,只保留头文件,将剩余的删除。

编写驱动入口、驱动出口函数

/* 驱动入口函数 */
static int __init miscbeep_init(void)
{
	return 0;
}
/* 驱动出口函数 */
static void __exit miscbeep_exit(void)
{

}
module_init(miscbeep_init);
module_exit(miscbeep_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("yang");

编译一下,查看有没有出错。

在入口里面注册platform函数,函数的参数是结构体,因此要定义一个platform结构体。

/* platform */
static struct platform_driver miscbeep_driver = {
	
};
/* 驱动入口函数 */
static int __init miscbeep_init(void)
{
	return platform_driver_register(&miscbeep_driver);
}
/* 驱动出口函数 */
static void __exit miscbeep_exit(void)
{
	platform_driver_unregister(&miscbeep_driver);
}

下面完善platform结构体,里面的成员变量driver中的name的名字要跟设备树里面的是一样的,alientenk_beep;接下来是设备树匹配表,完成设备树匹配表.platform中的.compatible要和设备树里面的进行匹配。

/* platform匹配表 */
static const struct of_device_id beep_of_match[] = {
	{.compatible = "alientek,beep"},
	{/* Sentinel */},
};
/* platform */
static struct platform_driver miscbeep_driver = {
	.driver = {
		.name = "imx6ul-beep",
		.of_match_table = beep_of_match,/* 设备树匹配表 */
	},
	.probe = miscbeep_probe,
	.remove = miscbeep_remove,
};

下面实现miscbeep_probe和miscbeep_remove函数。

/* probe函数 */
static int miscbeep_probe(struct platform_device *dev)
{
	return 0;
}

/* remove函数 */
static int miscbeep_remove(struct platform_device *dev)
{
	return 0;
}

编译一下:

在probe函数中需要的工作:
(1)初始化蜂鸣器IO
(2)misc驱动注册

先定义一个miscbeep设备结构体

struct miscbeep_dev {
	struct device_node *nd;/* 设备节点 */
	int beep_gpio;/* beep gpio */
};
struct miscbeep_dev miscbeep;

因此在probe函数中,找到node,初始化IO,gpio属性名字是:beep-gpio,索引是0,只有一个;

/* probe函数 */
static int miscbeep_probe(struct platform_device *dev)
{
	int ret = 0;
	//初始化蜂鸣器IO
	miscbeep.nd = dev->dev.of_node;
	miscbeep.beep_gpio = of_get_named_gpio(miscbeep.nd, "beep-gpio", 0);
	if(miscbeep.beep_gpio < 0){
		ret = -EINVAL;
		goto fail_findgpio; 
	}
	ret = gpio_request(miscbeep.beep_gpio, "beep-gpio");
	if(ret){
		printk("can't request %d gpio!\r\n",miscbeep.beep_gpio);
		ret = -EINVAL;
		goto fail_findgpio;
	}
	ret = gpio_direction_output(miscbeep.beep_gpio, 1);//输出,默认高电平
	if(ret < 0){
		goto fail_setoutput;
	}
	//misc驱动注册

	return 0;
fail_setoutput://失败的时候释放掉gpio
	gpio_free(miscbeep.beep_gpio);	
fail_findgpio:
	return ret;
}

/* remove函数 */
static int miscbeep_remove(struct platform_device *dev)
{
	gpio_set_value(miscbeep.beep_gpio,1);//拉高,关闭BEEP
	gpio_free(miscbeep.beep_gpio);//释放gpio
	return 0;
}

编译测试一下:

make
sudo cp miscbeep.ko /home/yang/nfs/rootfs/lib/modules/4.1.15/ -f

开发板加载驱动,卸载驱动:

编写misc驱动注册部分的代码:使用misc_register(),注册的内容是:struct miscdevice。将结构体放到上面:

#define MISCBEEP_NAME "miscbeep"
#define MISCBEEP_MINOR 144

/* miscdevice 结构体 */
static struct miscdevice beep_miscdev = {
	.minor = MISCBEEP_MINOR,
	.name = MISCBEEP_NAME,
	.fops = &miscbeep_fops, 
};

下面完成字符设备操作集:

static int miscbeep_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &miscbeep;
	return 0;
}
static int miscbeep_release(struct inode *inode, struct file *filp)
{	
	return 0;
}
static ssiez_t miscbeep_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
	
}

/* 字符设备操作集 */
struct file_operations miscbeep_fops = {
	.owner = THIS_MODULE,
	.open = miscbeep_open,
	.release = miscbeep_release,
	.write = miscbeep_write,
};

构建好了,就可以misc驱动注册了。

//misc驱动注册
ret = misc_register(&beep_miscdev);
if(ret < 0){
	goto fail_setoutput;
}

当卸载的时候,把MISC卸载掉,

/* remove函数 */
static int miscbeep_remove(struct platform_device *dev)
{
	......
	misc_deregister(&beep_miscdev);
	return 0;
}

编译报错:缺少头文件

#define <linux/miscdevice.h>

至此,驱动框架已经生成,加载测试:

make
sudo cp miscbeep.ko /home/yang/nfs/rootfs/lib/modules/4.1.15/ -f

开发板测试一下:

/lib/modules/4.1.15 # modprobe miscbeep.ko
/lib/modules/4.1.15 # ls /dev/miscbeep -l

接下来实现write函数。

#define BEEP_OFF	0
#define BEEP_ON		1

static ssiez_t miscbeep_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
	int ret;
	unsigned char databuf[1];
	struct miscbeep_dev *dev = filp->private_data;

	ret = copy_from_user(databuf, buf, count);
	if(ret < 0){
		return -EINVAL;
	}
	if(databuf[0] == BEEPON){
		gpio_set_value(dev->beep_gpio, 0);/* 打开蜂鸣器 */
	} else if(databuf[0] == BEEPOFF){
		gpio_set_value(dev->beep_gpio, 0);/* 关闭蜂鸣器 */
	}
}

2.2.3、应用

将上一节的应用程序测试APP复制过来

mv platledAPP.c beepAPP.c
arm-linux-gnueabihf-gcc beepAPP.c -o beepAPP
sudo cp miscbeep.ko beepAPP /home/yang/nfs/rootfs/lib/modules/4.1.15/ -f

开发板上电:

/lib/modules/4.1.15 # lsmod
/lib/modules/4.1.15 # rmmod miscbeep.ko
/lib/modules/4.1.15 # modprobe miscbeep.ko
/lib/modules/4.1.15 # ls
/lib/modules/4.1.15 # ./beepAPP /dev/miscbeep 0
/lib/modules/4.1.15 # ./beepAPP /dev/miscbeep 1

http://www.kler.cn/a/291042.html

相关文章:

  • 投标心态:如何在“标海战术”中保持清醒的头脑?
  • CLION中运行远程的GUI程序
  • Hive解决数据倾斜
  • ios 混合开发应用白屏问题
  • 能否提供详细的海洋影视CMS安装步骤和指南?
  • 环境变量的知识
  • 乐凡三防平板高性能为稳定运行保驾护航
  • Python和JAX及MATLAB小波分析导图
  • vue项目生成插件的LICENSE文件
  • 【Python机器学习】机器学习任务中常见的数据异质问题和模型异构问题是什么?解决策略是什么?
  • 驱动开发系列17 - PCI总线
  • 量化交易面试:什么是资本资产定价模型?
  • 千云物流 -低代码平台MySQL备份数据
  • 整形提升-C语言
  • 数学建模--皮尔逊相关系数、斯皮尔曼相关系数
  • 在移动应用程序中集成模糊方法的基于物联网的天气监测系统的实现
  • Linux虚拟机安装(CentOS9)
  • 数据结构排序之快排
  • 【C语言进阶】C语言进阶教程:利用结构体、联合体和枚举自定义数据类型
  • DigitalOcean Spaces 对象存储:新增伦敦节点
  • 【Linux网络编程八】实现最简单Http服务器(基于Tcp套接字)
  • SOLIDWORKS华北区供应商经销商:如何选择最适合您的合作伙伴?
  • C++:构造函数、析构函数
  • Python编程 - 深入面向对象
  • SSL解说与应用
  • Centos安装配置Gitea(Ubuntu等系统也可参考)