说明:

  平台设备:正点原子IMX6ULL

第一部分:

  修改设备树: 

pinctrl_pwm3: pwm3grp {
  fsl,pins = <
    MX6UL_PAD_GPIO1_IO04__PWM3_OUT 0x110b0
  >;
};

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

led_breath {
  compatible = "led-breath";
  pwms = <&pwm3 0 5000000>;
  status = "okay";
};

第二部分:

  led_breath.c 驱动文件:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/leds.h>
#include <linux/err.h>
#include <linux/pwm.h>
#include <linux/leds_pwm.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define DEV_CNT 1 //设备数
#define DEV_NAME "led-breath" //设备名称

#define LED_BREATH_MAGIC 0xEF
#define LED_BREATH_MAX_NR 6

#define LED_PWM_SET_PERIOD _IOW(LED_BREATH_MAGIC, 0, int)
#define LED_PWM_GET_PERIOD _IOR(LED_BREATH_MAGIC, 1, int)
#define LED_PWM_SET_DUTY _IOW(LED_BREATH_MAGIC, 2, int)
#define LED_PWM_GET_DUTY _IOR(LED_BREATH_MAGIC, 3, int)
#define LED_PWM_ENABLE _IO(LED_BREATH_MAGIC, 4)
#define LED_PWM_DISABLE _IO(LED_BREATH_MAGIC, 5)


struct led_breath_dev
{
  struct cdev cdev;
  struct class *class;
  struct device *device;
  struct device_node *node;
  dev_t devid;
  int major;
  int minor;
  struct pwm_device *pwm_led;
  int pwmperiod;
  int pwmduty;
};

struct led_breath_dev *led;

static int led_breath_open(struct inode *inode, struct file *filp)
{
  filp->private_data = led; //设置私有数据
  return 0;
}

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

  if(_IOC_TYPE(cmd) != LED_BREATH_MAGIC) //检查幻数是否正确
  {
    return -EINVAL;
  }
  if(_IOC_NR(cmd) > LED_BREATH_MAX_NR) //检查序数是否有效
  {
    return -EINVAL;
  }

  switch(cmd)
  {
    case LED_PWM_SET_PERIOD:
    ret = copy_from_user(&led->pwmperiod, (int *)arg, sizeof(int));
    if (ret) {
      printk("kernal copy from user period failed \r\n");
    }
    printk("kernal led->pwmperiod = %d \r\n", led->pwmperiod);
    ret = pwm_config(led->pwm_led, led->pwmduty, led->pwmperiod);
    if (ret) {
      printk("kernal set period failed \r\n");
    }
    break;

    case LED_PWM_GET_PERIOD:
    ret = copy_to_user((int *)arg, &led->pwmperiod, sizeof(int));
    if (ret) {
      printk("kernal get period failed \r\n");
    }
    break;

    case LED_PWM_SET_DUTY:
    ret = copy_from_user(&led->pwmduty, (int *)arg, sizeof(int));
    if (ret) {
      printk("kernal copy from user duty failed \r\n");
    }
    printk("kernal led->pwmduty = %d \r\n", led->pwmduty);
    ret = pwm_config(led->pwm_led, led->pwmduty, led->pwmperiod);
    if (ret) {
      printk("kernal set duty failed \r\n");
    }
    break;

    case LED_PWM_GET_DUTY:
    ret = copy_to_user((int *)arg, &led->pwmduty, sizeof(int));
    if (ret) {
      printk("kernal get duty failed \r\n");
    }
    break;

    case LED_PWM_ENABLE:
    ret = pwm_enable(led->pwm_led);
    if (ret) {
      printk("kernal Failed to enable PWM \r\n");
    return ret;
    }
    break;

    case LED_PWM_DISABLE:
    pwm_disable(led->pwm_led);
    break;

    default: break;
  }

  return 0;
}

static struct file_operations led_breath_fops = {
  .owner = THIS_MODULE,
  .open = led_breath_open,
  .unlocked_ioctl = led_breath_ioctl,
};

static int led_breath_driver_probe(struct platform_device *pdev)
{
  printk("led probe...... \r\n");

  led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
  if(led == NULL)
  {
    printk("devm kzalloc failed \n");
    return -1;
  }

  led->pwm_led = devm_of_pwm_get(&pdev->dev, pdev->dev.of_node, NULL);
  if(IS_ERR(led->pwm_led))
  {
    printk("devm of pwm get failed \n");
    return -1;
  }

  if(led->major) //申请设备号
  {
    led->devid = MKDEV(led->major, 0);
    register_chrdev_region(led->devid, DEV_CNT, DEV_NAME);
  }
  else
  {
    alloc_chrdev_region(&led->devid, 0, DEV_CNT, DEV_NAME);
    led->major = MAJOR(led->devid);
    led->minor = MINOR(led->devid);
  }

  cdev_init(&led->cdev, &led_breath_fops); //初始化字符设备, 并向内核进行添加
  cdev_add(&led->cdev, led->devid, DEV_CNT);

  led->class = class_create(THIS_MODULE, DEV_NAME); //创建设备类
  if (IS_ERR(led->class))
  {
    printk("class create failed \r\n");
    return PTR_ERR(led->class);
  }

  led->device = device_create(led->class, NULL, led->devid, NULL, DEV_NAME); //创建设备
  if (IS_ERR(led->device))
  {
    printk("device create failed \r\n");
    return PTR_ERR(led->device);
  }

  return 0;
}

static int led_breath_driver_remove(struct platform_device *dev)
{
  pwm_config(led->pwm_led, 0, led->pwmperiod);
  pwm_free(led->pwm_led);

  cdev_del(&led->cdev); //注销设字符设备
  unregister_chrdev_region(led->devid, DEV_CNT);

  device_destroy(led->class, led->devid); //注销设备类
  class_destroy(led->class); //注销设备
  return 0;
}

static const struct of_device_id pwm_of_match[] = {
  { .compatible = "led-breath" },
  { }
};

static struct platform_driver led_breath_driver = {
  .probe = led_breath_driver_probe,
  .remove = led_breath_driver_remove,

  .driver = {
  .name = "led-breath-drv",
  .of_match_table = pwm_of_match,
  },
};

static int __init led_breath_init(void)
{
  return platform_driver_register(&led_breath_driver);
}

static void __exit led_breath_exit(void)
{
  platform_driver_unregister(&led_breath_driver);
}

module_init(led_breath_init);
module_exit(led_breath_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("lianmeng");

第三部分:

  Makefile 文件:

KERNELDIR := /home/lmeng/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek

CURRENT_PATH := $(shell pwd)

obj-m := led_breath.o

build : kernel_modules

kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

第四部分:

  ledbreathApp.c 文件:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "linux/ioctl.h"

#define LED_BREATH_MAGIC 0xEF
#define LED_BREATH_MAX_NR 6

#define LED_PWM_SET_PERIOD _IOW(LED_BREATH_MAGIC, 0, int)
#define LED_PWM_GET_PERIOD _IOR(LED_BREATH_MAGIC, 1, int)
#define LED_PWM_SET_DUTY _IOW(LED_BREATH_MAGIC, 2, int)
#define LED_PWM_GET_DUTY _IOR(LED_BREATH_MAGIC, 3, int)
#define LED_PWM_ENABLE _IO(LED_BREATH_MAGIC, 4)
#define LED_PWM_DISABLE _IO(LED_BREATH_MAGIC, 5)

int main(int argc, char *argv[])
{
  int fd;
  int i = 0;
  int period = 500, duty = 100, arg = 0;

  fd = open("/dev/led-breath", O_RDWR);
  if (fd < 0) {
    printf("Can't open file /dev/led-breath \r\n");
    return -1;
  }

  while (1)
  {
    ioctl(fd, LED_PWM_SET_PERIOD, &period); //设置周期
    ioctl(fd, LED_PWM_GET_PERIOD, &arg); //获取周期
    printf("period arg = %d \r\n", arg);

    ioctl(fd, LED_PWM_SET_DUTY, &duty); //设置占空比
    ioctl(fd, LED_PWM_GET_DUTY, &arg); //获取占空比
    printf("duty arg = %d \r\n", arg);

    ioctl(fd, LED_PWM_ENABLE); //开启PWM

    sleep(500);
  }

  close(fd);
}