【IMX6ULL驱动开发学习】21.Linux驱动之PWM子系统(以SG90舵机为例)

news2024/9/24 13:24:52

1.设备树部分

首先在 imx6ull.dtsi 文件中已经帮我们定义好了一些pwm的设备树节点,这里以pwm2为例

pwm2: pwm@02084000 {
	compatible = "fsl,imx6ul-pwm", "fsl,imx27-pwm";
	reg = <0x02084000 0x4000>;
	interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
	clocks = <&clks IMX6UL_CLK_DUMMY>,
		 <&clks IMX6UL_CLK_DUMMY>;
	clock-names = "ipg", "per";
	#pwm-cells = <2>;
};

我们要在设备树(.dts)文件中引用和使能该节点,同时指定好pwm映射到的GPIO引脚(即pinctrl子系统,我这里映射到了GPIO1_9上)

&iomuxc {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_hog_1>;
	imx6ul-evk {
		......
		......

		/* SG90 PWM2 GPIO1_IO09 */
		pinctrl_pwm2: pwm2grp {
			fsl,pins = <
				MX6UL_PAD_GPIO1_IO09__PWM2_OUT   0x110b0
			>;
		};
		......
		......
}

......
......

&pwm2 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_pwm2>;
	clocks = <&clks IMX6UL_CLK_PWM2>,
			 <&clks IMX6UL_CLK_PWM2>;
	status = "okay";
};

使用pwm 只需要在设备树节点中添加两条属性信息,如下所示

pwms = <&PWMn id period_ns>;
pwm-names = "name";
  • pwms :属性是必须的,它共有三个属性值

  • &PWMn 指定使用哪个pwm,在imx6ull.dtsi文件中定义,总共有8个可选;

  • id :pwm的id通常设置为0。

  • period_ns :用于设置周期。单位是ns。

  • pwm-names :定义pwm设备名字。(可以不设置)

最后在根节点下添加自己定义的节点

hc_sg90 {
	compatible    =  "hc-sg90";
	pwms = <&pwm2 0 20000000>;    /* 使用pwm1  id为0   周期为20000000ns = 20ms */
	status 		  =  "okay";
};

2.驱动代码部分

老一套的字符设备驱动框架:

  • 驱动入口出口
  • 驱动入口定义注册字符设备、创建字符设备节点、注册platform设备;
  • 驱动出口反注册platfrom设备、删除字符设备节点、反注册字符设备
  • 构建file_operations结构体
  • 构建platform_device结构体,编写probe函数

如下代码所示:

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/timekeeping.h>
#include <linux/wait.h>
#include <linux/irqflags.h>
#include <linux/pwm.h>

static int major;
static struct class *class;

static struct pwm_device *pwm_test;

static int sg90_probe(struct platform_device *pdev)
{
    struct device_node *node = pdev->dev.of_node;

    printk("sg90 match success \n");
    if (node){
        /* 从子节点中获取PWM设备 */
        pwm_test = devm_of_pwm_get(&pdev->dev, node, NULL);  
        if (IS_ERR(pwm_test)){
            printk(KERN_ERR" pwm_test,get pwm  error!!\n");
            return -1;
        }
    }
    else{
        printk(KERN_ERR" pwm_test of_get_next_child  error!!\n");
        return -1;
    }

    pwm_config(pwm_test, 1500000, 20000000);   /* 配置PWM:1.5ms,90度,周期:20000000ns = 20ms */
    pwm_set_polarity(pwm_test, PWM_POLARITY_NORMAL); /* 设置输出极性:占空比为高电平 */
    pwm_enable(pwm_test);    /* 使能PWM输出 */

    return 0;
}

static int sg90_remove(struct platform_device *dev)
{
	pwm_config(pwm_test, 500000, 20000000);  /* 配置PWM:0.5ms,0度 */
	pwm_free(pwm_test);

	return 0;
}

static const struct of_device_id sg90_of_match[] = {
	{ .compatible = "hc-sg90" },
	{ }
};

static struct platform_driver sg90_platform_driver = {
	.driver = {
		.name		= "my_sg90",
		.of_match_table	= sg90_of_match,
	},
	.probe			= sg90_probe,
	.remove			= sg90_remove,
};


static int sg90_open (struct inode *node, struct file *filp)
{
	return 0;
}

static ssize_t sg90_write (struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
	int res;
	unsigned char data[1];
	if(size != 1)
		return 1;

	res = copy_from_user(data, buf, size);
	/* 配置PWM:旋转任意角度(单位1度) */
	pwm_config(pwm_test, 500000 + data[0] * 100000 / 9, 20000000);   
	return 1;
}

static int sg90_release (struct inode *node, struct file *filp)
{
	return 0;
}


static struct file_operations sg90_ops = {
	.owner		=	THIS_MODULE,
	.open 		= 	sg90_open,
	.write 		= 	sg90_write,
	.release 	=	sg90_release,
};

static int sg90_init(void)
{
	major = register_chrdev(0 , "sg90", &sg90_ops);
	class = class_create(THIS_MODULE, "sg90_class");
	device_create(class, NULL, MKDEV(major, 0), NULL, "sg90");

	platform_driver_register(&sg90_platform_driver);
	
	return 0;
}

static void sg90_exit(void)
{
	platform_driver_unregister(&sg90_platform_driver);
	
	device_destroy(class, MKDEV(major, 0));
	class_destroy(class);
	unregister_chrdev(major, "sg90");
}

module_init(sg90_init);
module_exit(sg90_exit);
MODULE_LICENSE("GPL");
  • 首先 struct device_node *node = pdev->dev.of_node; 获取子节点,在设备树插件中,我们把PWM相关信息保存在 hc_sg90 的子节点中, 所以这里首先获取子节点。

  • 在子节点获取成功后我们使用 devm_of_pwm_get 函数获取pwm, 由于节点内只有一个PWM 这里将最后一个参数直接设置为NULL,这样它将获取第一个PWM。

  • 依次调用 pwm_config、pwm_set_polarity、pwm_enable 函数配置**PWM、设置输出极性、 使能PWM输出,**需要注意的是这里设置的极性为正常极性, 这样pwm_config函数第二个参数设置的就是pwm波的一个周期内的高电平事件。

其中write函数中关于SG90的占空比计算就不多说了,根据如下图来计算吧

在这里插入图片描述
不难得出高电平时间每多出1ms(1000000ns) 对应角度多出9度的结论
则旋转到角度 1 度时,对应的高电平时间为 (500000 + 1000000)/9 ns(因为0度对应的高电平时间为0.5ms = 500000ns)
则旋转到角度 n 度时,高电平时间为 (500000 + n * 1000000)/9 ns


3.应用程序部分

运行示例: ./sg90_test 90 , 即转到90度的位置

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
	int fd;
	int res;
	unsigned char buf[1];

	fd = open("/dev/sg90", O_WRONLY);

	if(fd < 0)
	{
		printf("sg90 open failed\n");
		return 0;
	}

	buf[0] = atoi(argv[1]);
	write(fd, buf, 1);

	close(fd);
	return 0;
}

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

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

相关文章

2023年第四届“华数杯”数学建模思路 - 案例:FPTree-频繁模式树算法

## 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法&#xff0c;就是频繁模式树算法&#xff0c;他与Apriori算法一样也是用来挖掘频繁项集的&#xff0c…

Databend 开源周报第 104 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 从 Kafka 载入数…

内存“银行”

项目介绍 本项目实现的是一个内存银行&#xff0c;它的原型是Google的一个开源项目tcmalloc&#xff0c;tcmalloc全称Thread-Caching Malloc&#xff0c;即线程缓存的malloc&#xff0c;实现了高效的多线程内存管理&#xff0c;用于替换系统的内存分配相关函数malloc和free。 有…

Linux第四章之权限理解

一、Linux用户的概念 Linux下有两种用户&#xff1a;超级用户&#xff08;root&#xff09;、普通用户。 超级用户&#xff1a;可以再linux系统下做任何事情&#xff0c;不受限制普通用户&#xff1a;在linux下做有限的事情。 超级用户的命令提示符是“#”&#xff0c;普通用户…

2023年第四届“华数杯”数学建模思路 - 案例:最短时间生产计划安排

文章目录 0 赛题思路1 模型描述2 实例2.1 问题描述2.2 数学模型2.2.1 模型流程2.2.2 符号约定2.2.3 求解模型 2.3 相关代码2.4 模型求解结果 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; 最短时间生产计划模型 该模型出现在好几个竞赛赛题上&#x…

【React】搭建React项目

最近自己在尝试搭建react项目&#xff0c;其实react项目搭建没有想象中的那么复杂&#xff0c;我们只需要使用一个命令把React架子搭建好&#xff0c;其他的依赖可以根据具体的需求去安装&#xff0c;比如AntDesignMobile的UI框架&#xff0c;执行npm install antd-mobile --sa…

什么是注意力机制?注意力机制的计算规则

我们观察事物时&#xff0c;之所以能够快速判断一种事物(当然允许判断是错误的)&#xff0c;是因为我们大脑能够很快把注意力放在事物最具有辨识度的部分从而作出判断&#xff0c;而并非是从头到尾的观察一遍事物后&#xff0c;才能有判断结果&#xff0c;正是基于这样的理论&a…

Stable Diffusion VAE:改善图像质量的原理、选型与使用指南

VAE Stable Diffusion&#xff08;稳定扩散&#xff09;是一种用于生成模型的算法&#xff0c;结合了变分自编码器&#xff08;Variational Autoencoder&#xff0c;VAE&#xff09;和扩散生成网络&#xff08;Diffusion Generative Network&#xff09;的思想。它通过对变分自…

【贪心+01背包】Tower

这是个很经典的问题 Tower - 洛谷 题意&#xff1a; 思路&#xff1a; 首先可以确定是个背包而且肯定要排序&#xff0c;但是根据什么排序不确定 因为交换相邻两个箱子只有对自身产生影响&#xff0c;所以可以贪心地考虑这两个箱子如何摆最优&#xff0c;而又因为本身上面的…

无人机编队路径规划算法的Matlab实现

室内多智能体协同控制是指在密闭空间内的各个无人机及无人车在运动时能够相互之间保持一定的相对距离&#xff0c;并在速度及位置上按照预设路线或命令进行运动的过程。本平台的多智能体协同定位采用光学运动捕捉技术&#xff0c;并通过WiFi网络实现多机、多车间的通信&#xf…

13-1_Qt 5.9 C++开发指南_多线程及QThread 创建多线程程序_ThreadSignal

一个应用程序一般只有一个线程&#xff0c;一个线程内的操作是顺序执行的&#xff0c;如果有某个比较消耗时间的计算或操作&#xff0c;比如网络通信中的文件传输&#xff0c;在一个线程内操作时&#xff0c;用户界面就可能会冻结而不能及时响应。这种情况下&#xff0c;可以创…

2023年第四届“华数杯”数学建模思路 - 案例:感知机原理剖析及实现

# 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 一、感知机的直观理解 感知机应该属于机器学习算法中最简单的一种算法&#xff0c;其原理可以看下图&#xff1a; 比如说我们有一个坐标轴&#xff08;图中的…

火车头伪原创插件怎么用【php源码】

这篇文章主要介绍了儿童学python编程哪个学校好&#xff0c;具有一定借鉴价值&#xff0c;需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获&#xff0c;下面让小编带着大家一起了解一下。 1、python几岁学比较好 python建议8岁到10岁以上的孩子学习&#xff0c;详细介…

聊天系统登录后端实现

定义返回的数据格式 # Restful API from flask import jsonifyclass HttpCode(object):# 响应正常ok 200# 没有登陆错误unloginerror 401# 没有权限错误permissionerror 403# 客户端参数错误paramserror 400# 服务器错误servererror 500def _restful_result(code, messa…

线性代数基础一 行列式

前言 行列式在线性代数中具有非常重要的地位,很多线性代数的问题都可以转化为计算行列式来解决。 集合 集合的表示方法&#xff1a;常用的有列举法和描述法。 列举法&#xff1a;常用于表示有限集合&#xff0c;把集合中的所有元素一一列举出来&#xff0c;写在大括号内&am…

grid map学习笔记3之详解grid_map_pcl库实现point cloud点云转换成grid map栅格地图

文章目录 0 引言1 grid_map_pcl示例1.1 主要文件1.2 示例数据1.3 启动文件1.4 配置文件1.5 主要实现流程1.6 启动示例1.7 示例结果 2 D435i 点云生成栅格地图2.1 D435i 点云文件2.2 修改启动文件2.3 测试和结果2.4 修改配置文件2.5 重新测试和结果 0 引言 grid map学习笔记1已…

海外网红营销:如何利用故事打造独具魅力的品牌形象?

随着全球数字化时代的来临&#xff0c;品牌推广已经从传统的广告宣传方式逐渐转变为更加注重故事性和情感共鸣的营销手段。故事营销在品牌塑造和传播过程中发挥着重要作用&#xff0c;它能够吸引消费者的注意力&#xff0c;加深品牌与受众的情感连接&#xff0c;从而为品牌带来…

uniapp开发微信小程序--自定义顶部导航栏

一、实现效果&#xff1a; 二、代码实现&#xff1a; 1.在pages.json文件中&#xff0c;单页面定义导航栏&#xff0c;添加以下代码&#xff1a; "navigationStyle": "custom" //自定义导航栏如图所示&#xff1a; 2.在components文件夹下&#xff0c;…

用于毫米波天线的新型无卤素超低传输损耗多层电路板R-5410

3月3日消息&#xff0c;松下公司宣布&#xff0c;其工业解决方案公司已经实现了R-5410的商业化&#xff0c;这是一种无卤素、超低传输损耗的多层电路板&#xff08;MLCB&#xff09;材料&#xff0c;适用于毫米波天线。将于2021年3月开始量产。 毫米波雷达是汽车、通信等行业的…

uC-OS2 V2.93 STM32L476 移植:环境搭建篇

前言 uC-OS2 是比较经典的 RTOS&#xff0c;如今软件授权已经改为 Apache License Version 2.0&#xff0c;意味着可以免费商用了 当前 uC-OS2 的最新版本是&#xff1a; V2.93&#xff0c;打算研究一下 RTOS 的设计思想&#xff0c;所以想在已有的开发板&#xff1a;NUCLEO-L…