【IMX6ULL驱动开发学习】08.马达驱动实战:驱动编写、手动注册平台设备和设备树添加节点信息

news2025/1/17 3:42:27

目录

一、使用设备树

1.1 修改设备树流程

二、手动创建平台设备 

三、总结(附驱动程序)


前情提要:​​​​​​​【IMX6ULL驱动开发学习】07.驱动程序分离的思想之平台总线设备驱动模型和设备树_阿龙还在写代码的博客-CSDN博客

手动注册平台设备和设备树的目的都是为了构造platform_device结构体

一、使用设备树

之前的驱动编写方式,引脚信息在驱动程序里写死了,移植性差。而使用设备树,这样的程序编写方式便于在换了板子或引脚之后,只要修改设备树的节点信息即可,不需要看复杂的驱动程序,前提是有完备的驱动程序。

在驱动程序的入口函数里注册了gpio_platform_driver,

module_init(gpio_drv_init);
static int __init gpio_drv_init(void)
{
	/* 注册platform_driver */
	return platform_driver_register(&gpio_platform_driver);
}

gpio_platform_driver结构体中probe函数被调用,需要在设备树中找到与之匹配的设备节点信息,即设备节点信息也要含有这个语句.compatible = "100ask,gpiodemo"

static const struct of_device_id gpio_dt_ids[] = {
        { .compatible = "100ask,gpiodemo", },
        { /* sentinel */ }
};

static struct platform_driver gpio_platform_driver = {
	.driver		= {
		.name	= "100ask_gpio_plat_drv",
		.of_match_table = gpio_dt_ids,
	},
	.probe		= gpio_drv_probe,
	.remove		= gpio_drv_remove,
};

1.1 修改设备树流程

在内核目录下找到设备树文件,以我的路径为例,这里给出绝对路径

  • 修改设备树:vi /home/book/100ask_imx6ull-sdk/Linux-4.9.88/arch/arm/boot/dts/100ask_imx6ull-14x14.dts
  • 增加节点信息如下:
motor {
    compatible = "100ask,gpiodemo";
    gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>, 
            <&gpio4 20 GPIO_ACTIVE_HIGH>,
            <&gpio4 21 GPIO_ACTIVE_HIGH>,
			<&gpio4 22 GPIO_ACTIVE_HIGH>;
};
  • 一定要在/home/book/100ask_imx6ull-sdk/Linux-4.9.88目录下编译设备树:make dtbs
  • 复制到单板上,如下:
PC:
cp arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs/

开发板:
mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
cp /mnt/100ask_imx6ull-14x14.dtb  /boot
reboot

  • 查看设备树节点信息:ls /sys/firmware/devicetree/base/ 

  •  查看马达节点有没有被转换成平台设备(platform_device):ls /sys/bus/platform/devices/

 

 有这个motor文件表示有了对应的平台设备。

  • 进入motor文件查看,因为还没装驱动,所以没有显示驱动程序

 

  • 装载驱动后,查看motor文件: 说明驱动和设备都匹配上了

 

  • 最后就编写测试程序进行测试即可: 
./button_test /dev/motor ...

二、手动创建平台设备 

  • 编写dev.c文件
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>

static struct resource my_drv_resource[] = {
	{
        .start          = 115,
        .end            = 115,
        .flags          = IORESOURCE_IRQ,
	},
	{
        .start          = 116,
        .end            = 116,
        .flags          = IORESOURCE_IRQ,
	},
	{
        .start          = 117,
        .end            = 117,
        .flags          = IORESOURCE_IRQ,
	},
	{
        .start          = 118,
        .end            = 118,
        .flags          = IORESOURCE_IRQ,
	},
};

static struct platform_device gpio_platform_device = {
        .name           = "100ask_gpio_plat_drv",
        .id             = 0,
        .num_resources  = ARRAY_SIZE(my_drv_resource),// =4 宏来计算数组大小
        .resource       = my_drv_resource,
};

static int __init gpio_dev_init(void)
{
	/* 注册platform_driver */
	return platform_device_register(&gpio_platform_device);
}

static void __exit gpio_dev_exit(void)
{
	/* 反注册platform_driver */
	platform_device_unregister(&gpio_platform_device);
}

/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(gpio_dev_init);
module_exit(gpio_dev_exit);

MODULE_LICENSE("GPL");

 其中,由这个名字"100ask_gpio_plat_drv"找到与之匹配的平台驱动(platform_driver)

static struct platform_device gpio_platform_device = {
        .name           = "100ask_gpio_plat_drv",
        .id             = 0,
        .num_resources  = ARRAY_SIZE(my_drv_resource),// =4 宏来计算数组大小
        .resource       = my_drv_resource,
};
  •  修改Makefile文件
KERN_DIR =  /home/book/100ask_imx6ull-sdk/Linux-4.9.88 # 板子所用内核源码的目录

all:
	make -C $(KERN_DIR) M=`pwd` modules 
	$(CROSS_COMPILE)gcc -o button_test button_test.c
clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order  button_test

# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.o

obj-m += gpio_drv.o
obj-m += gpio_dev.o
  •  编译:make
  • 设备树节点信息取消:因为内核里有了设备树节点信息,需要改回去或者添加disabled状态
motor {
    compatible = "100ask,gpiodemo";
    gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>, 
            <&gpio4 20 GPIO_ACTIVE_HIGH>,
            <&gpio4 21 GPIO_ACTIVE_HIGH>,
			<&gpio4 22 GPIO_ACTIVE_HIGH>;
    status = "disabled";
};
  • 编译:make dtbs
  • 更新设备树:
PC:
cp arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs/

开发板:
mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
cp /mnt/100ask_imx6ull-14x14.dtb  /boot
reboot
  • 查看设备树节点信息:ls /sys/firmware/devicetree/base/

        状态为禁止状态,设备树节点信息被我们禁用了 

 

  • 查看马达节点有没有被转换成平台设备(platform_device):ls /sys/bus/platform/devices/,这里肯定也是没有的

 

  •  装载驱动程序、查看是否匹配上、查看设备节点

 这个平台驱动支持名为"100ask_gpio_plat_drv"的设备,设备节点也出来了

 

三、总结(附驱动程序)

这个驱动程序,在入口函数里注册了平台驱动,这个平台驱动可以支持来自设备树的平台设备.compatible = "100ask,gpiodemo",和来自自己手动创建的平台设备.name = "100ask_gpio_plat_drv"。匹配规则有4种,这里用的是最简单的。当平台driver匹配到平台device时,驱动程序里的probe函数就被调用,probe函数里可以从设备树里得到引脚信息,也可以从手动注册的平台device里得到所谓的资源。

static const struct of_device_id gpio_dt_ids[] = {
        { .compatible = "100ask,gpiodemo", },
        { /* sentinel */ }
};

static struct platform_driver gpio_platform_driver = {
	.driver		= {
		.name	= "100ask_gpio_plat_drv",
		.of_match_table = gpio_dt_ids,//设备树信息
	},
	.probe		= gpio_drv_probe,
	.remove		= gpio_drv_remove,
};

 驱动程序:

#include <linux/module.h>
#include <linux/poll.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>

struct gpio_desc{
	int gpio;
	int irq;
        char name[128];
        int key;
	struct timer_list key_timer;
} ;

static struct gpio_desc *gpios;
static int count;

/* 主设备号                                                                 */
static int major = 0;
static struct class *gpio_class;

/* 马达引脚设置数字 */
static int g_motor_pin_ctrl[8]= {0x2,0x3,0x1,0x9,0x8,0xc,0x4,0x6};
static int g_motor_index = 0;

void set_pins_for_motor(int index)
{
	int i;
	for (i = 0; i < 4; i++)
	{
		gpio_set_value(gpios[i].gpio, g_motor_pin_ctrl[index] & (1<<i) ? 1 : 0);
	}
}

void disable_motor(void)
{
	int i;
	for (i = 0; i < 4; i++)
	{
		gpio_set_value(gpios[i].gpio, 0);
	}
}

/* int buf[2];
 * buf[0] = 步进的次数, > 0 : 逆时针步进; < 0 : 顺时针步进
 * buf[1] = mdelay的时间
 */
static ssize_t motor_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
    int ker_buf[2];
    int err;
	int step;


    if (size != 8)
        return -EINVAL;

    err = copy_from_user(ker_buf, buf, size);


	if (ker_buf[0] > 0)
	{
		/* 逆时针旋转 */
		for (step = 0; step < ker_buf[0]; step++)
{
			set_pins_for_motor(g_motor_index);
			mdelay(ker_buf[1]);
			g_motor_index--;
			if (g_motor_index == -1)
				g_motor_index = 7;

}
	}
	else
	{
		/* 顺时针旋转 */
		ker_buf[0] = 0 - ker_buf[0];
		for (step = 0; step < ker_buf[0]; step++)
{
			set_pins_for_motor(g_motor_index);
			mdelay(ker_buf[1]);
			g_motor_index++;
			if (g_motor_index == 8)
				g_motor_index = 0;

		}
}

	/* 改进:旋转到位后让马达不再消耗电源 */
	disable_motor();

    return 8;    
}



/* 定义自己的file_operations结构体                                              */
static struct file_operations gpio_key_drv = {
	.owner	 = THIS_MODULE,
	.write   = motor_write,
};

/* 在入口函数 */
static int gpio_drv_probe(struct platform_device *pdev)
{
    int err = 0;
    int i;
	struct device_node *np = pdev->dev.of_node;
	struct resource *res;
    
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	

	/* 从platfrom_device获得引脚信息 
	 * 1. pdev来自c文件
     * 2. pdev来自设备树
	 */
	
	if (np)
	{
		/* pdev来自设备树 : 示例
        reg_usb_ltemodule: regulator@1 {
            compatible = "100ask,gpiodemo";
            gpios = <&gpio5 5 GPIO_ACTIVE_HIGH>, <&gpio5 3 GPIO_ACTIVE_HIGH>;
        };
		*/
		count = of_gpio_count(np);
		if (!count)
			return -EINVAL;

		gpios = kmalloc(count * sizeof(struct gpio_desc), GFP_KERNEL);
		for (i = 0; i < count; i++)
		{
			gpios[i].gpio = of_get_gpio(np, i);
			sprintf(gpios[i].name, "%s_pin_%d", np->name, i);
		}
	}
	else
	{
		/* pdev来自c文件 
		static struct resource omap16xx_gpio3_resources[] = {
			{
					.start  = 115,
					.end    = 115,
					.flags  = IORESOURCE_IRQ,
			},
			{
					.start  = 118,
					.end    = 118,
					.flags  = IORESOURCE_IRQ,
			},		};		
		*/
		count = 0;
		while (1)
		{
			res = platform_get_resource(pdev, IORESOURCE_IRQ, count);
			if (res)
			{
				count++;
			}
			else
			{
				break;
			}
		}

		if (!count)
			return -EINVAL;

		gpios = kmalloc(count * sizeof(struct gpio_desc), GFP_KERNEL);
		for (i = 0; i < count; i++)
		{
			res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
			gpios[i].gpio = res->start;
			sprintf(gpios[i].name, "%s_pin_%d", pdev->name, i);
		}

	}

	for (i = 0; i < count; i++)
	{
		err = gpio_request(gpios[i].gpio, gpios[i].name);
		gpio_direction_output(gpios[i].gpio, 0);
	}

	/* 注册file_operations 	*/
	major = register_chrdev(0, "100ask_gpio_key", &gpio_key_drv);  /* /dev/gpio_desc */

	gpio_class = class_create(THIS_MODULE, "100ask_gpio_key_class");
	if (IS_ERR(gpio_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "100ask_gpio_key");
		return PTR_ERR(gpio_class);
	}

	device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "motor"); /* /dev/motor */
	
	return err;
}

/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
 */
static int gpio_drv_remove(struct platform_device *pdev)
{
    int i;
    
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	device_destroy(gpio_class, MKDEV(major, 0));
	class_destroy(gpio_class);
	unregister_chrdev(major, "100ask_gpio_key");

	for (i = 0; i < count; i++)
	{
		gpio_free(gpios[i].gpio);
	}

	return 0;
}

static const struct of_device_id gpio_dt_ids[] = {
        { .compatible = "100ask,gpiodemo", },
        { /* sentinel */ }
};

static struct platform_driver gpio_platform_driver = {
	.driver		= {
		.name	= "100ask_gpio_plat_drv",
		.of_match_table = gpio_dt_ids,//设备树信息
	},
	.probe		= gpio_drv_probe,
	.remove		= gpio_drv_remove,
};

static int __init gpio_drv_init(void)
{
	/* 注册platform_driver */
	return platform_driver_register(&gpio_platform_driver);
}

static void __exit gpio_drv_exit(void)
{
	/* 反注册platform_driver */
	platform_driver_unregister(&gpio_platform_driver);
}

/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(gpio_drv_init);
module_exit(gpio_drv_exit);

MODULE_LICENSE("GPL");


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/896476.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringjDBCTemplate_spring25

1、首先导入两个包&#xff0c;里面有模板 2、transtion事务 jDbc操作对象&#xff0c;底层默认的是事务&#xff1a; 3、我们java一般对实体类进行操作。 4、第一步写好坐标。 创建一个Account表 数据修改用update 数据进去了

音频转换工具哪个好用?能解决音频格式转换问题吗?

大千世界中的语言自然存在差异&#xff0c;不同的音频格式也有着各自的方言&#xff0c;有时候我们需要一位翻译官来帮助我们更好地欣赏这些美妙的音符。幸运的是&#xff0c;现代的科技可以让音频格式转换变得轻而易举&#xff0c;就像是在不同乐章之间穿越。无论是将古典的FL…

arm:day6

实现UART通信&#xff1a; 1.键盘输入一个字符a,串口工具显示b 2.键盘输入一个字符串"nihao",串口工具显示"nihao" uart.h #ifndef __UART4_H__ #define __UART4_H__#include "stm32mp1xx_uart.h" #include "stm32mp1xx_gpio.h" #in…

Kubernetes的endpoint

简介 Kubernetes的endpoint&#xff08;终结点&#xff09;是用于将服务绑定到集群中其他组件的网络地址。Endpoint为服务提供了一个稳定的虚拟IP地址&#xff0c;它会负责将流量从Service路由到后端Pod。 下面是使用Kubernetes的endpoint的详细步骤&#xff1a; 创建一个Se…

超声波传感器(HC-SR04)按时序图手撕驱动

目录 1、简介 2、传感器介绍 2.1 引脚介绍 2.2 时序图介绍 3、 需求与接线 3.1 任务需求 3.2 接线 4、Cubemax配置 4.1 SYS配置 4.2 RCC配置 4.3 时钟树配置 4.4 GPIO初始化 4.5 定时器配置 4.6 生成代码 5、 keil端代码编写 5.1 微妙函数封装 5.2 超声波驱动封装…

机器学习---线性判别分析

1. 基本思想 线性判别分析(Linear Discriminant Analysis, LDA)&#xff0c;也叫做 Fisher 线性判别(Fisher Linear Discriminant ,FLD)&#xff0c;是模式识别的经典算法&#xff0c;1936年由Ronald Fisher⾸次提出&#xff0c;并在1996年由 Belhumeur引⼊模式识别和⼈⼯智能…

【探索C++】用实例教你理解面向对象编程(看不懂打我版)

(꒪ꇴ꒪ )&#xff0c;Hello我是祐言QAQ我的博客主页&#xff1a;C/C语言&#xff0c;Linux基础&#xff0c;ARM开发板&#xff0c;软件配置等领域博主&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff0c;让我们成为一个强大的攻城狮&#xff01;送给自己和读者的…

软件系统工具-架构师真题(六)

_____不属于可修改性考虑的内容。&#xff08;2016&#xff09; 可维护性可扩展性结构重构可变性 答案:D 解析&#xff1a; 可修改性指快速较高的性能价格进行系统优化&#xff0c;包括可维护性、可扩展性、结构重组和可移植性四个方面。 软件系统工具中&#xff0c;软件评…

Docker 常规软件安装

1. 总体安装步骤 1. 搜索镜像 search 2. 拉取镜像 pull 3. 查看镜像 images 4. 启动镜像 - 端口映射 run 5. 停止容器 stop 6. 移除容器 rm 2. 安装tomcat 1. 搜索 docker search tomcat 2. 拉取 docker pull tomcat 3. 查看本地镜像 docker images tomcat 4. 创建容器实…

两个List合并、去重、排序

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

打通应用“壁垒”,数据分类分级结果与安全策略自动匹配

《网络安全法》、《数据安全法》等法律法规&#xff0c;以及各行业各领域与数据安全相关的标准规范&#xff0c;几乎都涉及对数据进行分类分级保护的要求。数据安全始于分类分级&#xff0c;已成为毫无疑问的行业共识。 但现实中不少用户却止步在分类分级工作&#xff0c;“如…

linux虚拟机中各服务端口及配置文件路径

查询端口状况命令&#xff1a; netstat -an| grep 端口号 查询服务状态&#xff08;服务是否开启&#xff09;命令&#xff1a;systemctl status 服务名 开启服务命令&#xff1a;systemctl start 服务名 21端口&#xff1a;FTP 文件传输服务 22端口&#xff1a;SSH协议、…

无涯教程-Perl - wait函数

描述 该函数等待子进程终止,返回已故进程的进程ID。进程的退出状态包含在$?中。 语法 以下是此函数的简单语法- wait返回值 如果没有子进程,则此函数返回-1,否则将显示已故进程的进程ID Perl 中的 wait函数 - 无涯教程网无涯教程网提供描述该函数等待子进程终止,返回已故…

怎么把pdf压缩到5m以内?压缩办法非常多

怎么把pdf压缩到5m以内&#xff1f;PDF文件是我们办公过程中较为常用的文件格式&#xff0c;PDF文件所包含的内容通常较多&#xff0c;比如文本、图像以及音视频等等。这样的话&#xff0c;PDF文件占用内存也较大。如果需要对PDF文件进行使用、传输、分享等的话&#xff0c;可能…

中路对线发现正在攻防演练中投毒的红队大佬

背景 2023年8月14日晚&#xff0c;墨菲安全实验室发布《首起针对国内金融企业的开源组件投毒攻击事件》NPM投毒事件分析文章&#xff0c;紧接着我们在8月17日监控到一个新的npm投毒组件包 hreport-preview&#xff0c;该投毒组件用来下载木马文件的域名地址竟然是 img.murphys…

【TypeScript】任意类型

前置准备 在 powershell 安装 ts-node 包&#xff0c;可以先借助 xmzs 包切换淘宝镜像&#xff0c;加快安装速度。 在 vscode 终端执行 npm init -y 生成 package.json 文件 然后在 vscode 终端执行 npm i types/node -D 最后就可以在 vscode 终端执行ts-node&#xff0c;直接…

Typora 相对路径保存图片以及 Gitee 无法显示图片

目录 Typora 相对路径保存图片 Gitee 无法显示图片 Typora 相对路径保存图片 Step1&#xff1a;修改 Typora 的偏好设置 自动在当前目录创建名为 "./${filename}.assets" 的文件夹粘贴图片到 md 中时&#xff0c;图片会自动另存到 "./${filename}.assets&qu…

macOS Ventura 13.5.1(22G90)发布(附黑/白苹果系统镜像地址)

系统镜像下载&#xff1a;百度&#xff1a;黑果魏叔 系统介绍 黑果魏叔 8 月 18 日消息&#xff0c;苹果今日向 Mac 电脑用户推送了 macOS 13.5.1 更新&#xff08;内部版本号&#xff1a;22G90&#xff09;&#xff0c;本次更新距离上次发布隔了 24 天。 本次更新重点修复了…

ip2region(1):java通过Ip2region实现IP定位

1 Ip2region简介 Ip2region是一个开源工具库,是一种用于IP地址定位的工具。 它是基于B树数据结构的数据库&#xff0c;可用于将IP地址转换为国家、省、市、区县等信息。 使用Ip2region工具&#xff0c;您可以轻松地查找某个IP地址所在的区域信息&#xff0c;从而帮助您进行定位…

页面加载慢问题排查结论

遇到一个很奇怪的现象&#xff0c;已知A和B两个页面&#xff0c;都有调用同一个弹框&#xff0c;但是A页面很快&#xff0c;一切正常&#xff0c;B页面需要加载很久&#xff0c;就很奇怪。 后面又发现&#xff0c;B页面里面不同tab C和D&#xff0c;调用上面的弹框&#xff0c…