说明:
平台设备:正点原子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);
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步