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



















