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

【IMX6ULL项目复现】sg90电机-pwm

1. 设备树的设置

我们使用pwm3进行实验,输出是GPIO1_IO04引脚,首先设置pinctrl子系统

//确定使用哪个物理引脚
pinctrl_pwm3: pwm3grp {fsl,pins = <MX6UL_PAD_GPIO1_IO04__PWM3_OUT	0x110B0>;
};

pwm3 控制器节点中引用引脚配置

&pwm3 {pinctrl-names = "default";pinctrl-0 = <&pinctrl_pwm3>;clocks = <&clks IMX6UL_CLK_PWM3>,<&clks IMX6UL_CLK_PWM3>;status = "okay";
};

某个设备向PWM控制器申请服务

hc_sg90 {compatible    =  "hc-sg90";pwms = <&pwm3 0 20000000>;status 		  =  "okay";};

其中,pwms = <&pwm3 0 20000000>;是指向pwm3控制器申请服务,0是逻辑通道的索引,对于 pwm3 来说固定为0,20000000表示频率20MHz

2. 驱动文件的编写

在基础的字符设备驱动结构上,增加以下内容

  1. 构建数据结构,存放驱动运行所需的所有数据
    struct sg90dev {····struct pwm_device *sg90_PWM;     /* PWM设备 */····};
    
  2. probe函数中获取pwm_device结构体变量(devm_pwm_get函数解析设备树节点的pwm属性,获得pwm_device结构体变量)
    sg90dev->sg90_PWM = devm_pwm_get(&pdev->dev, "pwm");
    
    并处初始化pwm参数
    pwm_config(sg90dev->sg90_PWM, 500000, 20000000); // 设置PWM参数,0度时,高电平0.5ms,周期:20000000ns = 20ms
    pwm_set_polarity(sg90dev->sg90_PWM, PWM_POLARITY_NORMAL); /* 设置输出极性:占空比为高电平 */
    pwm_enable(sg90dev->sg90_PWM); // 使能PWM
    
  3. write函数中设置占空比
    res = copy_from_user(&data, buf, size);// 从用户空间拷贝数据到内核空间
    pwm_config(sg90dev->sg90_PWM, 500000 + data * 100000 / 9, 20000000); // 设置PWM参数,占空比:0.5ms~2.5ms,对应0~180度,周期:20000000ns = 20ms
    
    write函数设置占空比:sg90舵机从0度到180度,对应高电平从0.5ms到2.5ms,对应每一度高电平时间增加100000/9 ns
  4. remove函数中关闭PWM
    pwm_config(sg90dev->sg90_PWM, 500000, 20000000); // 将舵机设置为0度状态
    pwm_disable(sg90dev->sg90_PWM); // 关闭PWM
    

3. 代码

#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>
#include <linux/cdev.h>#define DEVICE_CNT	1
#define DEVICE_NAME	"sg90"struct sg90_dev {struct cdev cdev;		/* cdev 	*/struct device *device;	/* 设备 	 */char name[32];           // 从设备树读取的设备名,用于创建设备节点void *private_data;	/* 私有数据 */int minor;				/* 次设备号 */struct pwm_device *sg90_PWM; // PWM设备结构体
};
static struct sg90_dev *sg90dev;static int major;           // 该驱动的主设备号
static dev_t my_device_num; // 存设备号
static struct class *my_dev_class; // 设备类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;if(size != 1) return -EINVAL;res = copy_from_user(&data, buf, size);// 从用户空间拷贝数据到内核空间pwm_config(sg90dev->sg90_PWM, 500000 + data * 100000 / 9, 20000000); // 设置PWM参数,占空比:0.5ms~2.5ms,对应0~180度,周期:20000000ns = 20msreturn size;
}static int sg90_release(struct inode *inode, struct file *filp)
{return 0;
}static struct file_operations my_dev_ops = {.owner	= 	THIS_MODULE,.open 	= 	sg90_open,.write 	= 	sg90_write,.release =	sg90_release,
};static const struct of_device_id sg90_dt_match[] = {{ .compatible = "hc-sg90" },{ }, 
};static int sg90_platform_probe(struct platform_device *pdev)
{const char *device_name;               // 保存设备名称int ret;                               // 函数调用返回值/* 步骤 1: 为设备私有数据结构分配内存 */sg90dev = kzalloc(sizeof(struct sg90_dev), GFP_KERNEL); // 为每一个物理设备分配私有数据结构if (!sg90dev){printk("kzalloc for device failed!\r\n");return -ENOMEM;}printk("sg90 driver and device was matched!\r\n");/* 步骤 2: 从设备树节点中解析硬件信息 */ret = of_property_read_string(pdev->dev.of_node, "my_name", &device_name); //将np节点中的my_name属性值读出,存入device_nameif (ret < 0) {printk("Property 'my_name' not found in device tree\n");goto err_free_mem; // 跳转到错误处理标签}strncpy(sg90dev->name, device_name, sizeof(sg90dev->name) - 1);//将设备树中读取到的设备名称拷贝到私有数据结构name,sizeof(sg90dev->name) - 1是防止溢出/* 步骤 3:在全局数组中分配次设备号,并完成字符设备注册 */cdev_init(&sg90dev->cdev, &my_dev_ops); // 初始化字符设备sg90dev->cdev.owner = THIS_MODULE;      // 标识该字符设备属于哪个内核模块,不是必须的sg90dev->minor = 0;   //分配次设备号my_device_num = MKDEV(major, sg90dev->minor); // 重新使用my_device_num,合成完整的设备号ret = cdev_add(&sg90dev->cdev, my_device_num, 1); // 注册字符设备if (ret < 0) {printk("Failed to add cdev for %s\n", sg90dev->name);goto err_free_mem;}/* 步骤 4: 创建设备节点 */sg90dev->device = device_create(my_dev_class, NULL, my_device_num, NULL, sg90dev->name); // 创建设备节点,设备名从设备树读取if (IS_ERR(sg90dev->device)){ret = PTR_ERR(sg90dev->device);printk("device_create for %s failed!\r\n", sg90dev->name);cdev_del(&sg90dev->cdev); // 注销字符设备goto err_free_mem;}/* 设置PWM属性 */sg90dev->sg90_PWM = devm_pwm_get(&pdev->dev, "pwm");if (IS_ERR(sg90dev->sg90_PWM)) {ret = PTR_ERR(sg90dev->sg90_PWM);printk("pwm_get for %s failed!\r\n", sg90dev->name);device_destroy(my_dev_class, MKDEV(major, sg90dev->minor));cdev_del(&sg90dev->cdev); // 注销字符设备goto err_free_mem;}pwm_config(sg90dev->sg90_PWM, 500000, 20000000); // 设置PWM参数,0度时,高电平0.5ms,周期:20000000ns = 20mspwm_set_polarity(sg90dev->sg90_PWM, PWM_POLARITY_NORMAL); /* 设置输出极性:占空比为高电平 */pwm_enable(sg90dev->sg90_PWM); // 使能PWMreturn 0;
err_free_mem:kfree(sg90dev); // 释放内存return ret;
}static int sg90_platform_remove(struct platform_device *pdev)
{// 销毁设备节点,从内核中删除字符设备实例device_destroy(my_dev_class, MKDEV(major, sg90dev->minor));cdev_del(&sg90dev->cdev);pwm_config(sg90dev->sg90_PWM, 500000, 20000000); // 设置PWM参数,0度时,高电平0.5ms,周期:20000000ns = 20mspwm_disable(sg90dev->sg90_PWM); // 关闭PWMkfree(sg90dev); // 释放内存return 0;
}static struct platform_driver sg90_platform_driver = {.driver = {.owner = THIS_MODULE,.name = "sg90",.of_match_table	= sg90_dt_match,},.probe = sg90_platform_probe,.remove = sg90_platform_remove,
};static int __init sg90_init(void)
{int ret;/* 申请主设备号 */ret = alloc_chrdev_region(&my_device_num, 0, DEVICE_CNT, DEVICE_NAME);if (ret < 0) {printk("Failed to allocate char device region\n");return ret;}major = MAJOR(my_device_num);         // 获取主设备号/* 创建设备类 */my_dev_class = class_create(THIS_MODULE, DEVICE_NAME);if (IS_ERR(my_dev_class)){printk("class_create failed\n");unregister_chrdev_region(my_device_num, 1); // 补充释放return PTR_ERR(my_dev_class); // 返回标准错误码}/* 注册platform驱动 */return platform_driver_register(&sg90_platform_driver);
}static void __exit sg90_exit(void)
{// 注销设备号unregister_chrdev_region(my_device_num, 1);// 删除设备类class_destroy(my_dev_class);/* 注销platform驱动 */platform_driver_unregister(&sg90_platform_driver);}module_init(sg90_init);
module_exit(sg90_exit);
MODULE_LICENSE("GPL");
http://www.dtcms.com/a/438511.html

相关文章:

  • 2025软件供应链安全实战:从漏洞修补到风险预测的转型指南
  • iOS26 打开开发者模式
  • 注销主体备案与网站备案沧州最火网站
  • AI智能体在研究分析中的仿真应用:AI驱动的复杂系统建模与“理论压缩”
  • 表格识别技术:突破传统OCR的局限,通过定位-解析-重建三步策略攻克无边框、合并单元格等视觉难题
  • 免费企业模板网站制作网页设计公司
  • 开放定址法的平均查找长度计算
  • MEVless,MEV解决之道
  • 网站 自适应文字大小怎么做响应式网站建设公司
  • 手写MyBatis第89弹:动态SQL解析与执行时机深度剖析
  • 解读172页“十五五”企业战略规划指导手册【附全文阅读】
  • 网站开发个人工作室网站推送怎么做的
  • 机器学习:逻辑回归
  • 机器学习——SVM支持向量机详解
  • app网站开发wordpress 自定义字段 调用
  • 互动网站建设什么意思wordpress 公众号插件
  • CUDA 13.0 中 CCCL 2.x 到 CCCL 3.0 迁移介绍
  • 临沂企业网站建设网站建设 技术方案
  • 《计算机视觉度量:从特征描述到深度学习》-- 基于MAE预训练模型DinoV3的图像特征分析
  • LabVIEW继电保护检测
  • 网站开发网公司营业执照查询
  • dedecms视频网站开发wordpress弃用react
  • Unity Profiler中的LogStringToConsole
  • 开源的intellij ide 的claude code 插件和codex 插件
  • 深度学习图像分类实战:从零构建ResNet50多类别分类系统
  • 网站建设报价选兴田德润专业的建站公司推广
  • springboot+vue图书借阅管理系统设计(源码+文档+调试+基础修改+答疑)
  • 大学生兼职网站设计论文做网站大公司有哪些
  • 四川省建设厅官方网站联系电话南京电商代运营
  • 打破信息差——miniQMT