Linux驱动开发:PWM子系统详解(以SG90舵机为例)

一、PWM子系统简介

PWM(Pulse Width Modulation,脉宽调制)是一种通过调节高电平持续时间占整个周期的比例(即占空比)来控制模拟信号的方法。在嵌入式系统中,PWM常用于电机控制、LED调光、舵机控制等场景。


二、设备树配置(Device Tree)

2.1 PWM控制器节点(SoC内部定义)

pwm2 为例,其定义位于 imx6ull.dtsi 文件中:

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>;
};

2.2 引脚复用(pinctrl配置)

PWM2_OUT 映射到引脚 GPIO1_IO09

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

	imx6ul-evk {
		pinctrl_pwm2: pwm2grp {
			fsl,pins = <
				MX6UL_PAD_GPIO1_IO09__PWM2_OUT 0x110b0
			>;
		};
	};
};

2.3 启用 PWM 控制器节点

pwm2 节点中启用 PWM,并引用引脚配置:

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

2.4 定义 SG90 伺服节点

在根节点添加 SG90 设备信息:

hc_sg90 {
	compatible = "hc-sg90";
	pwms = <&pwm2 0 20000000>; /* 周期为 20ms(20000000ns) */
	status = "okay";
};
注解:
  • pwms = <&pwm2 0 20000000>

    • &pwm2:引用pwm控制器;
    • 0:通道号;
    • 20000000:周期为20ms,SG90标准周期。
  • pwm-names:可选,用于命名。


三、驱动程序部分

3.1 驱动结构概述

  • 使用 platform 驱动机制;
  • 注册字符设备,提供 /dev/sg90 接口;
  • 利用 pwm_config() 等函数配置PWM波形。

3.2 驱动源码

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/pwm.h>
#include <linux/uaccess.h>
#include <linux/device.h>

static int major;
static struct class *sg90_class;
static struct pwm_device *pwm_dev;

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

static ssize_t sg90_write(struct file *file, const char __user *buf, size_t size, loff_t *ppos)
{
	unsigned char angle;
	unsigned int duty_ns;

	if (size != 1)
		return -EINVAL;

	if (copy_from_user(&angle, buf, 1))
		return -EFAULT;

	duty_ns = 500000 + angle * (1000000 / 90);  // 0.5ms + angle * (1ms / 90)

	pwm_config(pwm_dev, duty_ns, 20000000);     // 周期 20ms

	return 1;
}

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

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

/* probe 函数:设备匹配后调用 */
static int sg90_probe(struct platform_device *pdev)
{
	struct device_node *node = pdev->dev.of_node;
	int ret;

	printk("%s %s line %d: sg90 matched\n", __FILE__, __FUNCTION__, __LINE__);

	/* 获取 PWM 设备 */
	pwm_dev = devm_of_pwm_get(&pdev->dev, node, NULL);
	if (IS_ERR(pwm_dev)) {
		printk(KERN_ERR "Failed to get PWM device\n");
		return PTR_ERR(pwm_dev);
	}

	/* 初始化 PWM */
	pwm_config(pwm_dev, 1500000, 20000000);  // 默认 90 度
	pwm_set_polarity(pwm_dev, PWM_POLARITY_NORMAL);
	pwm_enable(pwm_dev);

	/* 注册字符设备 */
	major = register_chrdev(0, "sg90", &sg90_fops);
	if (major < 0) {
		printk(KERN_ERR "register_chrdev failed\n");
		return major;
	}

	sg90_class = class_create(THIS_MODULE, "sg90_class");
	if (IS_ERR(sg90_class)) {
		unregister_chrdev(major, "sg90");
		return PTR_ERR(sg90_class);
	}

	device_create(sg90_class, NULL, MKDEV(major, 0), NULL, "sg90");

	return 0;
}

/* remove 函数:卸载时调用 */
static int sg90_remove(struct platform_device *pdev)
{
	pwm_disable(pwm_dev);

	device_destroy(sg90_class, MKDEV(major, 0));
	class_destroy(sg90_class);
	unregister_chrdev(major, "sg90");

	return 0;
}

/* 设备树匹配表 */
static const struct of_device_id sg90_of_match[] = {
	{ .compatible = "hc-sg90", },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sg90_of_match);

/* 平台驱动结构体 */
static struct platform_driver sg90_driver = {
	.driver = {
		.name = "my_sg90",
		.of_match_table = of_match_ptr(sg90_of_match),
	},
	.probe  = sg90_probe,
	.remove = sg90_remove,
};

/* 模块加载函数:仅注册 platform_driver */
static int __init sg90_init(void)
{
	printk("%s %s line %d: module init\n", __FILE__, __FUNCTION__, __LINE__);
	return platform_driver_register(&sg90_driver);
}
module_init(sg90_init);

/* 模块卸载函数 */
static void __exit sg90_exit(void)
{
	platform_driver_unregister(&sg90_driver);
}
module_exit(sg90_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("PWM Driver for SG90 Servo");


四、应用层测试程序

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
	int fd;
	unsigned char angle;

	if (argc != 2) {
		printf("Usage: %s <angle: 0~180>\n", argv[0]);
		return -1;
	}

	angle = atoi(argv[1]);
	if (angle > 180) angle = 180;

	fd = open("/dev/sg90", O_WRONLY);
	if (fd < 0) {
		perror("open");
		return -1;
	}

	write(fd, &angle, 1);
	close(fd);

	return 0;
}

运行方式:

./sg90_test 90

五、PWM控制SG90舵机的原理解析

SG90舵机接受周期为20ms的PWM信号:

  • 0度:高电平为0.5ms(500000ns)
  • 90度:高电平为1.5ms(1500000ns)
  • 180度:高电平为2.5ms(2500000ns)

占空比计算公式:

高电平时间(ns) = 500000 + angle * (1000000 / 90)

六、小结与拓展

驱动要点总结:

  • 利用 devm_of_pwm_get() 从设备树获取 PWM;
  • 使用 pwm_config() 配置占空比;
  • 使用 pwm_set_polarity() 设置极性;
  • 使用 pwm_enable() 开启 PWM 输出;
  • 使用字符设备接口接收用户空间控制命令。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值