linux 驱动之PWM蜂鸣器驱动
程序员文章站
2022-07-14 10:54:00
...
Table of Contents
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代表正常极性
图中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、测试
上一篇: Matplotlib 绘图库从入门到精通
下一篇: Task2 数据分析