野火i.MX6ULL通过ioctl接口控制多个pwm设备

前言:野火原本的镜像中已开放pwm值的使用,基础配置是与RGB灯关联,如果需要使用其他路的pwm值需要配置,并把原本与RGB灯关联的设备树关闭。本文是源于控制各路pwm值需要过多的单独指令,比较繁琐,现把各路pwm值整合到一个驱动中,使用一个驱动去驱动各路pwm值,驱动是通过 ioctl 接口去控制多个 PWM 设备(本文用到pwm3、pwm5、pwm6、pwm7、pwm8这几路pwm值)。

1.设备树编写

设备树的编写是仿照野火中pwm控制RGB灯的设备树编写,每个pwm的引脚设置必须保证没有其他引脚的复用,因为是控制多路pwm,需要在设备树中为各路pwm中设置编号,具体体现在:pwm-num。

把设备树编译完成后,放入开发板指定位置(具体参考野火开发文档),并在uEnv.txt文档中使能自己的设备树。

/*
 * Copyright (C) 2019 - All Rights Reserved by 
 * filename : imx-fire-mpu6050-overlay.dts
 * brief : Device Tree overlay for EBF6ull Pro mpu6050 device
 * author : embedfire
 * date : 2019-11-26
 * version : A001
 */
#include "../imx6ul-pinfunc.h"
#include "../imx6ull-pinfunc.h"
#include "../imx6ull-pinfunc-snvs.h"
#include "dt-bindings/input/linux-event-codes.h"
#include "dt-bindings/gpio/gpio.h"
#include <dt-bindings/clock/imx6ul-clock.h>
#include "dt-bindings/interrupt-controller/irq.h"
#include "dt-bindings/interrupt-controller/arm-gic.h"
/dts-v1/;
/plugin/;

 / {
     fragment@0 {
          target-path = "/";
         __overlay__ { 
	        sweep_pwm {
	        	compatible = "sweep_pwm";
	        	status = "okay";
			pwm3 {
				pwm-num = <3>;
                    		pwms = <&pwm3 0 50000 100000>;
                    		status = "okay";
                    	     };

                    	pwm5 {
                    		pwm-num = <5>;
                    		pwms = <&pwm5 0 50000 100000>;
                    		status = "okay";
                    	     };

                    	pwm6 {
                    		pwm-num = <6>;
				pwms = <&pwm6 0 50000 100000>;
				status = "okay";
                    	     };

                    	pwm7 {
                    		pwm-num = <7>;
				pwms = <&pwm7 0 50000 100000>;
				status = "okay";
                    	     };

                    	pwm8 {
                    		pwm-num = <8>;
				pwms = <&pwm8 0 50000 100000>;
				status = "okay";
            		    };
	        	};
	        };   
         };

     fragment@1 {
         target = <&iomuxc>;
         __overlay__ { 
		pinctrl_pwm3:pwm3grp{
		    fsl,pins = < MX6UL_PAD_GPIO1_IO04__PWM3_OUT 0x000010B1 >;
		};
		pinctrl_pwm5:pwm5grp{
		    fsl,pins = < MX6UL_PAD_ENET1_TX_DATA1__PWM5_OUT 0x000010B1 >;
		};
         	pinctrl_pwm6:pwm6grp{
		    fsl,pins = < MX6UL_PAD_ENET1_TX_EN__PWM6_OUT 0x000010B1 >;
		};
         	pinctrl_pwm7:pwm7grp{
		    fsl,pins = < MX6UL_PAD_CSI_VSYNC__PWM7_OUT 0x000010B1 >;
		};
         	pinctrl_pwm8:pwm8grp{
		    fsl,pins = < MX6UL_PAD_CSI_HSYNC__PWM8_OUT 0x000010B1 >;
		};
	   };
	
     };
		
	fragment@2 {
		target = <&pwm3>;
		__overlay__ {
			pinctrl-names = "default";
			pinctrl-0 = <&pinctrl_pwm3>;
			status = "okay";
		};
	};

	fragment@3 {
		target = <&pwm7>;
		__overlay__ {
			pinctrl-names = "default";
			pinctrl-0 = <&pinctrl_pwm7>;
			clocks = <&clks IMX6UL_CLK_PWM7>,
				<&clks IMX6UL_CLK_PWM7>;
			status = "okay";
		};
	};

	fragment@4 {
		target = <&pwm8>;
		__overlay__ {
			pinctrl-names = "default";
			pinctrl-0 = <&pinctrl_pwm8>;
			clocks = <&clks IMX6UL_CLK_PWM8>,
				<&clks IMX6UL_CLK_PWM8>;
			status = "okay";
		};
	};

	fragment@5 {
		target = <&pwm5>;
		__overlay__ {
			pinctrl-names = "default";
			pinctrl-0 = <&pinctrl_pwm5>;
			clocks = <&clks IMX6UL_CLK_PWM5>,
				<&clks IMX6UL_CLK_PWM5>;
			status = "okay";
		};
	};

	fragment@6 {
		target = <&pwm6>;
		__overlay__ {
			pinctrl-names = "default";
			pinctrl-0 = <&pinctrl_pwm6>;
			clocks = <&clks IMX6UL_CLK_PWM6>,
				<&clks IMX6UL_CLK_PWM6>;
			status = "okay";
		};
	};
 };

2.驱动的编写

驱动通过 ioctl 接口控制多个pwm设备。

注意:驱动的名字要与设备树中的匹配。

sweep_pwm.h

#ifndef INPUT_SWEEP_PWM_H
#define INPUT_SWEEP_PWM_H
#include <linux/types.h>
#endif

sweep_pwm.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/pwm.h>
#include <linux/err.h>
#include <linux/uaccess.h>
#include <linux/ioctl.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>

#define SWEEP_PWM_MAGIC 'p'
#define SET_PWM_PARAMS _IOW(SWEEP_PWM_MAGIC, 1, struct pwm_params)

struct pwm_params {
    int pwm_num;
    int duty_cycle;
    int period;
};

struct pwm_device *sweep_pwm[10]; // 假设最多支持10个PWM设备
static struct platform_device *pdev_global;

static int sweep_pwm_probe_new(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct device_node *child;
    int ret = 0;

    printk(KERN_INFO "match success\n");

    // 遍历子节点,获取 PWM 设备
    for_each_child_of_node(dev->of_node, child) {
        struct pwm_device *pwm;
        int pwm_num;

        if (of_property_read_u32(child, "pwm-num", &pwm_num)) {
            printk(KERN_ERR "Failed to read pwm-num property\n");
            continue;
        }

        pwm = devm_of_pwm_get(dev, child, NULL);
        if (IS_ERR(pwm)) {
            printk(KERN_ERR "Failed to get PWM device\n");
            continue;
        }

        if (pwm_num >= 0 && pwm_num < 10) {
            sweep_pwm[pwm_num] = pwm;
            printk(KERN_INFO "PWM device found: %pOF, pwm_num = %d\n", child, pwm_num);
        } else {
            printk(KERN_ERR "Invalid pwm_num: %d\n", pwm_num);
        }
    }

    pdev_global = pdev;
    return 0;
}

static int sweep_pwm_remove(struct platform_device *pdev)
{
    int i;

    for (i = 0; i < 10; i++) {
        if (sweep_pwm[i]) {
            pwm_config(sweep_pwm[i], 0, 5000);
            pwm_disable(sweep_pwm[i]);
        }
    }
    return 0;
}

static long sweep_pwm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct pwm_params params;
    int ret;

    if (cmd != SET_PWM_PARAMS) {
        return -ENOTTY;
    }

    if (copy_from_user(&params, (struct pwm_params *)arg, sizeof(params))) {
        return -EFAULT;
    }

    if (params.pwm_num < 0 || params.pwm_num >= 10 || !sweep_pwm[params.pwm_num]) {
        printk(KERN_ERR "Invalid PWM number\n");
        return -EINVAL;
    }

    ret = pwm_config(sweep_pwm[params.pwm_num], params.duty_cycle, params.period);
    if (ret) {
        printk(KERN_ERR "Failed to configure PWM\n");
        return ret;
    }

    pwm_enable(sweep_pwm[params.pwm_num]);
    return 0;
}

static const struct of_device_id of_pwm_sweep_match[] = {
    {.compatible = "sweep_pwm"},
    {},
};

static const struct file_operations sweep_pwm_fops = {
    .unlocked_ioctl = sweep_pwm_ioctl,
};

static struct miscdevice sweep_pwm_miscdev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "sweep_pwm",
    .fops = &sweep_pwm_fops,
};

static struct platform_driver sweep_pwm_driver = {
    .probe = sweep_pwm_probe_new,
    .remove = sweep_pwm_remove,
    .driver = {
        .name = "sweep_pwm",
        .of_match_table = of_pwm_sweep_match,
    },
};

static int __init sweep_pwm_platform_driver_init(void)
{
    int ret;

    ret = platform_driver_register(&sweep_pwm_driver);
    if (ret) {
        printk(KERN_ERR "Failed to register platform driver\n");
        return ret;
    }

    ret = misc_register(&sweep_pwm_miscdev);
    if (ret) {
        printk(KERN_ERR "Failed to register misc device\n");
        platform_driver_unregister(&sweep_pwm_driver);
        return ret;
    }

    return 0;
}

static void __exit sweep_pwm_platform_driver_exit(void)
{
    printk(KERN_INFO "sweep_pwm_exit\n");
    misc_deregister(&sweep_pwm_miscdev);
    platform_driver_unregister(&sweep_pwm_driver);
}

module_init(sweep_pwm_platform_driver_init);
module_exit(sweep_pwm_platform_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("DOUBAO");
MODULE_DESCRIPTION("Dynamic PWM Control Driver using ioctl");
MODULE_VERSION("1.0");

3.测试程序

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>

struct pwm_params {
    int pwm_num;
    int duty_cycle;
    int period;
};

#define SET_PWM_PARAMS _IOW('p', 1, struct pwm_params)

int main(int argc, char *argv[])
{
    int fd;
    struct pwm_params params;

    if (argc != 4) {
        fprintf(stderr, "Usage: %s <pwm_num> <duty_cycle> <period>\n", argv[0]);
        return -1;
    }

    fd = open("/dev/sweep_pwm", O_RDWR);
    if (fd < 0) {
        perror("Failed to open device file");
        return -1;
    }

    params.pwm_num = atoi(argv[1]);
    params.duty_cycle = atoi(argv[2]);
    params.period = atoi(argv[3]);

    if (ioctl(fd, SET_PWM_PARAMS, &params) < 0) {
        perror("Failed to set PWM parameters using ioctl");
        close(fd);
        return -1;
    }

    printf("PWM parameters set successfully\n");
    close(fd);
    return 0;
}

4.Makfile编写

KERNEL_DIR=../ebf_linux_kernel/build_image/build

ARCH=arm
CROSS_COMPILE=arm-linux-gnueabihf-
export  ARCH  CROSS_COMPILE

obj-m := sweep_pwm.o
out =  sweep_pwm_test

all:
	$(MAKE) EXTRA_CFLAGS=-fno-pic -C $(KERNEL_DIR) M=$(CURDIR) modules
	$(CROSS_COMPILE)gcc -o $(out) sweep_pwm_test.c
	
.PHONY:clean
clean:
	$(MAKE)  -C $(KERNEL_DIR) M=$(CURDIR) clean
	rm $(out)

5.测试

a.加载驱动

insmod sweep_pwm.ko

b.测试指令

(运行测试程序后面依次接上:pwm编号,占空比,周期)

./sweep_pwm_test 3 1000000 500000

具体的pwm值变化可以通过万用表或示波器体现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值