创建设备个人笔记
pinctrl(Pin Control)系统是嵌入式系统中的一个重要组成部分,用于管理和控制芯片上的引脚(或称为管脚、针脚)。这些引脚通常用于连接外部设备,如传感器、执行器、存储器等。Pinctrl系统允许软件动态地配置这些引脚的功能和属性,以满足特定的应用需求。在许多soc内部都包含有pin控制器,通过pin控制器的寄存器,我们可以配置一个或者一组引脚的功能和特性。在软件方面,Linux内核提供了pinctrl子系统,目的是为了统一各soc厂商的pin脚管理。
pinctrl 子系统重点是设置 PIN(有的 SOC 叫做 PAD)的复用和电气属性,如果 pinctrl 子系统将一个 PIN 复用为 GPIO 的话,那么接下来就要用到 gpio 子系统了。gpio 子系统顾名思义,就是用于初始化 GPIO 并且提供相应的 API 函数,比如设置 GPIO为输入输出,读取 GPIO 的值等。gpio 子系统的主要目的就是方便驱动开发者使用 gpio,驱动开发者在设备树中添加 gpio 相关信息,然后就可以在驱动程序中使用 gpio 子系统提供的 API函数来操作 GPIO,Linux 内核向驱动开发者屏蔽掉了 GPIO 的设置过程,极大的方便了驱动开发者使用 GPIO。
至此以后呢就不用通过寄存器来直接操控设备了,就是通过pinctrl系统来操控。(还是点灯)
首先,我们要在设备树上添加设备信息。先在设备树 iomuxc 节点上添加pinctrl信息
pinctrl_gpioled_1: gpioled-1{
fsl,pins = <
MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10b0
>;
};
MX6UL 是imx6ul-pinfunc.h头文件中定义的。
#define MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x0068 0x02F4 0x0000 0x5 0x0
这代表着你要将GPIO1_IO03 复用为输出端口
第一个 0x0068 代表mux_reg寄存器的偏移地址,GPIO1 控制器的基地址就是 0X0209C000。控值该引脚的地址就是 0X0209 C000 + 0x0000 0068 = 0X0209 C068
第二个 0x02F4 是conf_reg寄存器偏移地址。
第三个 0x0000 是input_reg寄存器的偏移地址
第四个 0x5 是 mux_reg 寄存器的值,也就是设置在 mux_reg 寄存器里面的值为 0x05 查数据手册能知道这是复用该端口作为 gpio 口。
第五个 是 input_reg 的值 这里设为 0 没有用到。
跟在 MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 后面的 0x10b0 就是电器属性的值 就是 conf_reg 的值。
注意 定义完着个 io 后你要看别的 io 有没有复用这个口,如果有那是用时就会失败,因为端口被别的设备复用了,我们写的这个就会使用不成功。所以要去把其他 MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 给屏蔽掉。
然后添加设备节点信息:
gpioled {
compatible = "lakzhu,gpioled";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpioled_1>;
led-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
status = "ok";
};
pinctrl-0 链接着上面的 io 信息 led-gpios 表示 是 gpio1 的 3 号口, 低电平有效。设备信息添加完成。
添加完成后 编译设备树写入开发板。
然后就是编写设备了。
首先写注册函数注册与卸载:
static int __init gpioled_init(void)
{
int ret = 0;
return 0;
}
static void __exit gpioled_exit(void)
{
}
//设备注册与卸载
module_init(gpioled_init);
module_exit(gpioled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lakzhu");
构建设备结构体
struct gpioled_dev{
dev_t devid;
struct cdev cdev;
int major;
int minor;
struct class *class;
struct device *device;
struct device_node *nd;
int led_gpio;
};
struct gpioled_dev gpioled;
//操作函数
static const struct file_operations filp = {
.owner = THIS_MODULE,
.open = gpioled_open,
.release = gpioled_release,
.write = gpioled_write,
};
注册字符设备:
int ret = 0;
//注册字符设备
gpioled.major = 0;
if(gpioled.major){ //自己给了设备号
gpioled.devid = MKDEV(gpioled.major, 0);
register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
}else{//没给系统自动分配
ret = alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);
gpioled.major = MAJOR(gpioled.devid);
gpioled.minor = MINOR(gpioled.devid);
}
printk("maj:%d min:%d\r\n", gpioled.major, gpioled.minor);
if(ret < 0){
goto fail_devid;
}
fail_devid:
return ret;
初始化字符设备
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev, &filp);
//cdev 添加到设备
ret = cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
if(ret < 0){
goto fail_cdev;
}
fail_cdev:
unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);//失败了要把之前注册的设备号删除
创建类
gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
if(IS_ERR(gpioled.device)){
ret = PTR_ERR(gpioled.device);
goto fail_device;
}
fail_class:
cdev_del(&gpioled.cdev);
创建设备
//创建设备
gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
if(IS_ERR(gpioled.device)){
ret = PTR_ERR(gpioled.device);
goto fail_device;
}
fail_device:
class_destroy(gpioled.class);
通过 of 函数获取设备节点信息
gpioled.nd = of_find_node_by_path("/gpioled");
if(gpioled.nd == NULL){
goto fail_node;
}
fail_node:
device_destroy(gpioled.class, gpioled.devid);
获取对应的 led 信息:
gpioled.led_gpio = of_get_named_gpio(gpioled.nd,"led-gpios",0);
if(gpioled.led_gpio < 0){
printk("find errir\r\n");
ret = -EINVAL;
goto fail_find_node;
}
printk("led gpio num = %d\r\n",gpioled.led_gpio);
fail_find_node:
申请io 申请了才能使用
ret = gpio_request(gpioled.led_gpio, "led-gpio");
if(ret){
printk("gpio_request error\r\n");
ret = -EINVAL;
goto fail_quest;
}
fail_quest:
使用io
ret = gpio_direction_output(gpioled.led_gpio, 1);//设置为灭 0 亮 1
if(ret){
goto fail_io;
}
gpio_direction_output(gpioled.led_gpio, 0);
return ret;
fail_io:
gpio_free(gpioled.led_gpio);
函数退出,要把注册过的都注销掉
static void __exit gpioled_exit(void)
{
gpio_direction_output(gpioled.led_gpio, 1);
//注销字符驱动
cdev_del(&gpioled.cdev);
//删除设备号
unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
//摧毁驱动
device_destroy(gpioled.class, gpioled.devid);
//摧毁类
class_destroy(gpioled.class);
//销毁io
gpio_free(gpioled.led_gpio);
}
文件操作函数
static int gpioled_open(struct inode *inode, struct file *filp)
{
filp->private_data = &gpioled;//这里获取到数据
return 0;
}
static int gpioled_release(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t gpioled_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
int ret;
unsigned char data[1];
struct gpioled_dev *dev = filp->private_data; //因为在 open 函数里获取过了 这里直接拿过
来用就可以了
ret = copy_from_user(data, buf, count);
if(ret < 0){
printk("write error\r\n");
return ret;
}
if(data[0] == LEDON){
gpio_set_value(dev->led_gpio, LEDON);
}else if(data[0] == LEDOFF){
gpio_set_value(dev->led_gpio, LEDOFF);
}
return 0;
}
完整 的文件
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.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/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define GPIOLED_CNT 1
#define GPIOLED_NAME "gpioled"
#define LEDOFF 1
#define LEDON 0
//gpio 设备结构体
struct gpioled_dev{
dev_t devid;
struct cdev cdev;
int major;
int minor;
struct class *class;
struct device *device;
struct device_node *nd;
int led_gpio;
};
struct gpioled_dev gpioled;
static int gpioled_open(struct inode *inode, struct file *filp)
{
filp->private_data = &gpioled;
return 0;
}
static int gpioled_release(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t gpioled_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
int ret;
unsigned char data[1];
struct gpioled_dev *dev = filp->private_data;
ret = copy_from_user(data, buf, count);
if(ret < 0){
printk("write error\r\n");
return ret;
}
if(data[0] == LEDON){
gpio_set_value(dev->led_gpio, LEDON);
}else if(data[0] == LEDOFF){
gpio_set_value(dev->led_gpio, LEDOFF);
}
return 0;
}
static const struct file_operations filp = {
.owner = THIS_MODULE,
.open = gpioled_open,
.release = gpioled_release,
.write = gpioled_write,
};
//入口出口
static int __init gpioled_init(void)
{
int ret = 0;
//注册字符设备
gpioled.major = 0;
if(gpioled.major){
gpioled.devid = MKDEV(gpioled.major, 0);
register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
}else{
ret = alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);
gpioled.major = MAJOR(gpioled.devid);
gpioled.minor = MINOR(gpioled.devid);
}
printk("maj:%d min:%d\r\n", gpioled.major, gpioled.minor);
if(ret < 0){
goto fail_devid;
}
//初始化 dev
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev, &filp);
//cdev 添加到设备
ret = cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
if(ret < 0){
goto fail_cdev;
}
//创建类
gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
if(IS_ERR(gpioled.class)){
ret = PTR_ERR(gpioled.class);
goto fail_class;
}
//创建设备
gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
if(IS_ERR(gpioled.device)){
ret = PTR_ERR(gpioled.device);
goto fail_device;
}
//获取设备节点
gpioled.nd = of_find_node_by_path("/gpioled");
if(gpioled.nd == NULL){
goto fail_node;
}
//获取led的对应的gpio
gpioled.led_gpio = of_get_named_gpio(gpioled.nd,"led-gpios",0);
if(gpioled.led_gpio < 0){
printk("find errir\r\n");
ret = -EINVAL;
goto fail_find_node;
}
printk("led gpio num = %d\r\n",gpioled.led_gpio);
//申请io 申请了才能使用
ret = gpio_request(gpioled.led_gpio, "led-gpio");
if(ret){
printk("gpio_request error\r\n");
ret = -EINVAL;
goto fail_quest;
}
//使用io
ret = gpio_direction_output(gpioled.led_gpio, 1);//设置为灭 0 亮 1
if(ret){
goto fail_io;
}
gpio_direction_output(gpioled.led_gpio, 0);
return ret;
fail_io:
gpio_free(gpioled.led_gpio);
fail_quest:
fail_find_node:
fail_node:
device_destroy(gpioled.class, gpioled.devid);
fail_device:
class_destroy(gpioled.class);
fail_class:
cdev_del(&gpioled.cdev);
fail_cdev:
unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
fail_devid:
return ret;
}
static void __exit gpioled_exit(void)
{
gpio_direction_output(gpioled.led_gpio, 1);
//注销字符驱动
cdev_del(&gpioled.cdev);
//删除设备号
unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
//摧毁驱动
device_destroy(gpioled.class, gpioled.devid);
//摧毁类
class_destroy(gpioled.class);
//销毁io
gpio_free(gpioled.led_gpio);
}
//设备注册与卸载
module_init(gpioled_init);
module_exit(gpioled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lakzhu");
编译下载到板子上就能使用了。
有新设备 要是用 先 depmod 然后才能modpure 函数