欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

linux 驱动之PWM蜂鸣器驱动

程序员文章站 2022-07-14 10:54:00
...

 

Table of Contents

1、常用pwm函数

2、驱动编程实例

3、应用编程

4、pwm设备树节点配置

5、内核配置

6、测试


1、常用pwm函数

//申请一个PWM设备
 pwm_device *pwm_get(struct device *dev, const char *con_id);

//配置PWM周期和占空比
 int pwm_config(struct pwm_device *pwm, int duty_ns,
			     int period_ns);

//使能pwm
 int pwm_enable(struct pwm_device *pwm);

关闭PWM
int pwm_enable(struct pwm_device *pwm);

2、驱动编程实例

#include <linux/input.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>

#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))


struct pwm_beeper {
/*	struct input_dev *input;*/
	struct pwm_device *pwm;
	unsigned long period;
};

struct pwm_beeper *beeper;


static int beep_open(struct inode *inode, struct file *fp)
{
   
  
 	printk("%s\n",__func__);

   return 0;



}


static ssize_t beep_read (struct file *fp, char __user *user_buf, 
								size_t count, loff_t *position)
{

   printk("beep_read\n");
   

   return 0;
}



static ssize_t beep_write(struct file *fp, const char __user *user_buf,
								size_t count, loff_t *position)
{
 
	int ret;
	struct input_event beepev;
   
   	printk("beep_write\n");

	if(beeper==NULL){
			printk("*beeper is null\n");
			return 0;
	}

  ret = copy_from_user(&beepev, user_buf, sizeof(struct input_event));
	
	if(ret<0){

		printk("%s copy_from_user filed\n",__func__);

	}

	if (beepev.type != EV_SND || beepev.value < 0)
		return -EINVAL;

	if (beepev.value == 0){
		pwm_disable(beeper->pwm);
	}
	else{

		beeper->period = HZ_TO_NANOSECONDS(beepev.value);
		  ret = pwm_config(beeper->pwm, beeper->period / 2, beeper->period); //占空比50%
         if (ret){

				printk("%s pwm_config error \n",__func__);
				 return ret;
			}
		 
          ret = pwm_enable(beeper->pwm);

		  if(ret){
				printk("%s pwm_enable error\n",__func__);
				return ret;
		  }
		  	
	}
   	
    return 0;
}

static int beep_close (struct inode *inode, struct file *fp)
{

	printk("beep_close \n");

	if(NULL!=beeper){
		
		pwm_disable(beeper->pwm);
	}

	return 0;
}


const struct file_operations beepfops ={
	.owner		= THIS_MODULE,
	.read		= beep_read,
	.open		= beep_open,
	.write 		= beep_write,
	.release	= beep_close,
};

struct miscdevice beepmisc={
	.name	= "mybeeper",
	.minor	= 10,
	.fops	= &beepfops,
};


static int pwm_beeper_probe(struct platform_device *pdev)
{

     struct device *dev = &pdev->dev;
	  
	//	 struct pwm_state state;
	   //u32 bell_frequency;
	   int error;
   
	   beeper = kzalloc(sizeof(struct pwm_beeper), GFP_KERNEL);
	   if (!beeper)
		   return -ENOMEM;
   
	   beeper->pwm = pwm_get(dev, NULL); //申请pwm设备
	   if (IS_ERR(beeper->pwm)) {
		   error = PTR_ERR(beeper->pwm);
		   if (error != -EPROBE_DEFER)
			   dev_err(dev, "Failed to request PWM device: %d\n",
				   error);
		   return error;
	   }
  	
	
	misc_register(&beepmisc);

	//platform_set_drvdata(pdev, beeper);

	

	return 0;


}

static int pwm_beeper_remove(struct platform_device *pdev)
{

	misc_deregister(&beepmisc);
	if(NULL!=beeper){
		pwm_disable(beeper->pwm);
		pwm_free(beeper->pwm);
	}
	
	kfree(beeper);

	return 0;
}

static int __maybe_unused pwm_beeper_suspend(struct device *dev)
{
	struct pwm_beeper *beeper = dev_get_drvdata(dev);

	if (beeper->period)
		pwm_disable(beeper->pwm);

	return 0;
}

static int __maybe_unused pwm_beeper_resume(struct device *dev)
{
	struct pwm_beeper *beeper = dev_get_drvdata(dev);

	if (beeper->period) {
		pwm_config(beeper->pwm, beeper->period / 2, beeper->period);
		pwm_enable(beeper->pwm);
	}

	return 0;
}

static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops,
			 pwm_beeper_suspend, pwm_beeper_resume);

#ifdef CONFIG_OF
static const struct of_device_id pwm_beeper_match[] = {
	{ .compatible = "mybeeper", },
	{ },
};
MODULE_DEVICE_TABLE(of, pwm_beeper_match);
#endif

static struct platform_driver pwm_beeper_driver = {
	.probe	= pwm_beeper_probe,
	.remove = pwm_beeper_remove,
	.driver = {
		.name	= "mybeeper",
		.pm	= &pwm_beeper_pm_ops,
		.of_match_table = of_match_ptr(pwm_beeper_match),
	},
};
module_platform_driver(pwm_beeper_driver);

MODULE_AUTHOR("Lars-Peter Clausen <aaa@qq.com>");
MODULE_DESCRIPTION("PWM beeper driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:pwm-beeper");

3、应用编程

#include<stdint.h>
#include<string.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdio.h>
#include<linux/input.h>
#include<unistd.h>



int main(int argc, char **argv)
{
	struct input_event beep_evn;
	int fd;
	char *file_name=argv[1];
	

	fd = open(file_name,O_RDWR);
	if(fd<0)
	{
		printf("open /dev/event0 filed\n");
		return 0;

	}
	
	beep_evn.type = EV_SND;
	beep_evn.code = SND_TONE;
	beep_evn.value = 2000;

	while(1)
	{
		beep_evn.value =0;
		write(fd,&beep_evn,sizeof(struct input_event));
		printf("beep_evn.value = 0\n");
		sleep(2);
		beep_evn.value = 1000;
		write(fd,&beep_evn,sizeof(struct input_event));
		printf("beep_evn.value = 1000\n");
		sleep(3);

	}

	close(fd);

	return 0;


}

4、pwm设备树节点配置

  beep {
        compatible = "mybeeper";
        pwms = <&pwm 0 4000000 0>;
    };  

其中pwms这个属性名字固定不能变,&pwm 0两项代表pwmOUT0同道,4000000代表初始化频率,0代表正常极性

linux 驱动之PWM蜂鸣器驱动

图中inverter off代表正常极性,正常极性表示定时器计数值大于比较值同道输出高电平,否则输出低电平,反向极性则相反。

 

&pwm {
    status = "okay";
    pinctrl-0 = <&pwm0_out>;
    pinctrl-names = "default";
    samsung,pwm-outputs = <0>;
};

利用pinctrl子系统初始化pwm引脚功能,选择pwm0_out

 

5、内核配置

 

 Device Drivers  --->

                    [*] Pulse-Width Modulation (PWM) Support  --->

                                    <*>   Samsung PWM support

6、测试

linux 驱动之PWM蜂鸣器驱动

 

 

 

 

 

 

 

 

 

 

相关标签: linux驱动开发