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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值