/*****************************************************************************
* I.MX6 PWM buzzer driver hacking with Demo test
* 声明:
* 1. I.MX6和OK335xS实现PWM驱动函数是不一样的;
* 2. 通过分析PWM驱动,了解有哪些驱动函数可以用;
* 3. 使用I.MX6提供的PWM函数,编写测试用例buzzer驱动;
* 4. 使用C编写测试程序。
*
* 2015-10-20 晴 深圳 南山平山村 曾剑锋
****************************************************************************/ \\\\\\\\\\\\\-*- 目录 -*-/////////////
| 一、cat arch/arm/plat-mxc/pwm.c
| 二、cat driver/misc/pwm_buzzer.c
| 三、cat main.c (demo test)
-------------------------------------- 一、cat arch/arm/plat-mxc/pwm.c
/*
* simple driver for PWM (Pulse Width Modulator) controller
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Derived from pxa PWM driver by eric miao <eric.miao@marvell.com>
* Copyright 2009-2013 Freescale Semiconductor, Inc. All Rights Reserved.
*/ #include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/pwm.h>
#include <linux/fsl_devices.h>
#include <mach/hardware.h> /* i.MX1 and i.MX21 share the same PWM function block: */ #define MX1_PWMC 0x00 /* PWM Control Register */
#define MX1_PWMS 0x04 /* PWM Sample Register */
#define MX1_PWMP 0x08 /* PWM Period Register */ /* i.MX27, i.MX31, i.MX35 share the same PWM function block: */ #define MX3_PWMCR 0x00 /* PWM Control Register */
#define MX3_PWMSAR 0x0C /* PWM Sample Register */
#define MX3_PWMPR 0x10 /* PWM Period Register */
#define MX3_PWMCR_PRESCALER(x) (((x - 1) & 0xFFF) << 4)
#define MX3_PWMCR_DOZEEN (1 << 24)
#define MX3_PWMCR_WAITEN (1 << 23)
#define MX3_PWMCR_DBGEN (1 << 22)
#define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16)
#define MX3_PWMCR_CLKSRC_IPG (1 << 16)
#define MX3_PWMCR_SWR (1 << 3)
#define MX3_PWMCR_EN (1 << 0) #define MX3_PWMCR_STOPEN (1 << 25)
#define MX3_PWMCR_DOZEEN (1 << 24)
#define MX3_PWMCR_WAITEN (1 << 23)
#define MX3_PWMCR_DBGEN (1 << 22)
#define MX3_PWMCR_CLKSRC_IPG (1 << 16)
#define MX3_PWMCR_CLKSRC_IPG_32k (3 << 16) struct pwm_device {
struct list_head node;
struct platform_device *pdev; const char *label;
struct clk *clk; int clk_enabled;
void __iomem *mmio_base; unsigned int use_count;
unsigned int pwm_id;
int pwmo_invert;
void (*enable_pwm_pad)(void);
void (*disable_pwm_pad)(void);
}; /**
* 1. 操作PWM用到duty(duty_ns)、period(period_ns)2个参数;
* 2. period就是频率参数(周期时间),duty为占空比;
* 3. period和duty的参数单位为纳秒(ns);
* 4. 1s=1000ms=1000000us=1000000000ns;
* 5. period最大的取值范围为0—1000000000,而duty则取值0—period值之间;
* 6. 平时我们可能更喜欢使用频率(Hz)来表示period,占空比来表示duty;
*/
int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
{
if (pwm == NULL || period_ns == || duty_ns > period_ns)
return -EINVAL; if (!(cpu_is_mx1() || cpu_is_mx21())) {
unsigned long long c;
unsigned long period_cycles, duty_cycles, prescale;
u32 cr; if (pwm->pwmo_invert)
duty_ns = period_ns - duty_ns; c = clk_get_rate(pwm->clk);
c = c * period_ns;
do_div(c, );
period_cycles = c; prescale = period_cycles / 0x10000 + ; period_cycles /= prescale;
c = (unsigned long long)period_cycles * duty_ns;
do_div(c, period_ns);
duty_cycles = c; /*
* according to imx pwm RM, the real period value should be
* PERIOD value in PWMPR plus 2.
*/
if (period_cycles > )
period_cycles -= ;
else
period_cycles = ; writel(duty_cycles, pwm->mmio_base + MX3_PWMSAR);
writel(period_cycles, pwm->mmio_base + MX3_PWMPR); cr = MX3_PWMCR_PRESCALER(prescale) |
MX3_PWMCR_STOPEN | MX3_PWMCR_DOZEEN |
MX3_PWMCR_WAITEN | MX3_PWMCR_DBGEN; if (cpu_is_mx25())
cr |= MX3_PWMCR_CLKSRC_IPG;
else
cr |= MX3_PWMCR_CLKSRC_IPG_HIGH; writel(cr, pwm->mmio_base + MX3_PWMCR);
} else if (cpu_is_mx1() || cpu_is_mx21()) {
/* The PWM subsystem allows for exact frequencies. However,
* I cannot connect a scope on my device to the PWM line and
* thus cannot provide the program the PWM controller
* exactly. Instead, I'm relying on the fact that the
* Bootloader (u-boot or WinCE+haret) has programmed the PWM
* function group already. So I'll just modify the PWM sample
* register to follow the ratio of duty_ns vs. period_ns
* accordingly.
*
* This is good enough for programming the brightness of
* the LCD backlight.
*
* The real implementation would divide PERCLK[0] first by
* both the prescaler (/1 .. /128) and then by CLKSEL
* (/2 .. /16).
*/
u32 max = readl(pwm->mmio_base + MX1_PWMP);
u32 p;
if (pwm->pwmo_invert)
duty_ns = period_ns - duty_ns;
p = max * duty_ns / period_ns;
writel(max - p, pwm->mmio_base + MX1_PWMS);
} else {
BUG();
} return ;
}
EXPORT_SYMBOL(pwm_config); /**
* 使能pwm
*/
int pwm_enable(struct pwm_device *pwm)
{
unsigned long reg;
int rc = ; if (!pwm->clk_enabled) {
rc = clk_enable(pwm->clk);
if (!rc)
pwm->clk_enabled = ;
} reg = readl(pwm->mmio_base + MX3_PWMCR);
reg |= MX3_PWMCR_EN;
writel(reg, pwm->mmio_base + MX3_PWMCR); if (pwm->enable_pwm_pad)
pwm->enable_pwm_pad(); return rc;
}
EXPORT_SYMBOL(pwm_enable); /**
* 关闭pwm
*/
void pwm_disable(struct pwm_device *pwm)
{
if (pwm->disable_pwm_pad)
pwm->disable_pwm_pad(); writel(MX3_PWMCR_SWR, pwm->mmio_base + MX3_PWMCR);
while (readl(pwm->mmio_base + MX3_PWMCR) & MX3_PWMCR_SWR)
; if (pwm->clk_enabled) {
clk_disable(pwm->clk);
pwm->clk_enabled = ;
}
}
EXPORT_SYMBOL(pwm_disable); static DEFINE_MUTEX(pwm_lock);
static LIST_HEAD(pwm_list); /**
* 通过pwm的id来表示申请pwm通道,label只是表示其名字,名字可以随意取
*/
struct pwm_device *pwm_request(int pwm_id, const char *label)
{
struct pwm_device *pwm;
int found = ; mutex_lock(&pwm_lock); list_for_each_entry(pwm, &pwm_list, node) {
if (pwm->pwm_id == pwm_id) {
found = ;
break;
}
} if (found) {
if (pwm->use_count == ) {
pwm->use_count++;
pwm->label = label;
} else
pwm = ERR_PTR(-EBUSY);
} else
pwm = ERR_PTR(-ENOENT); mutex_unlock(&pwm_lock);
return pwm;
}
EXPORT_SYMBOL(pwm_request); /**
* 前面申请了pwm,通过这个函数来释放pwm
*/
void pwm_free(struct pwm_device *pwm)
{
mutex_lock(&pwm_lock); if (pwm->use_count) {
pwm->use_count--;
pwm->label = NULL;
} else
pr_warning("PWM device already freed\n"); mutex_unlock(&pwm_lock);
}
EXPORT_SYMBOL(pwm_free); /**
* 这里是pwm控制器注册函数
*/
static int __devinit mxc_pwm_probe(struct platform_device *pdev)
{
struct pwm_device *pwm;
struct resource *r;
struct mxc_pwm_platform_data *plat_data = pdev->dev.platform_data;
int ret = ; pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
if (pwm == NULL) {
dev_err(&pdev->dev, "failed to allocate memory\n");
return -ENOMEM;
} pwm->clk = clk_get(&pdev->dev, "pwm"); if (IS_ERR(pwm->clk)) {
ret = PTR_ERR(pwm->clk);
goto err_free;
} pwm->clk_enabled = ; pwm->use_count = ;
pwm->pwm_id = pdev->id;
pwm->pdev = pdev;
if (plat_data != NULL) {
pwm->pwmo_invert = plat_data->pwmo_invert;
pwm->enable_pwm_pad = plat_data->enable_pwm_pad;
pwm->disable_pwm_pad = plat_data->disable_pwm_pad;
} r = platform_get_resource(pdev, IORESOURCE_MEM, );
if (r == NULL) {
dev_err(&pdev->dev, "no memory resource defined\n");
ret = -ENODEV;
goto err_free_clk;
} r = request_mem_region(r->start, r->end - r->start + , pdev->name);
if (r == NULL) {
dev_err(&pdev->dev, "failed to request memory resource\n");
ret = -EBUSY;
goto err_free_clk;
} pwm->mmio_base = ioremap(r->start, r->end - r->start + );
if (pwm->mmio_base == NULL) {
dev_err(&pdev->dev, "failed to ioremap() registers\n");
ret = -ENODEV;
goto err_free_mem;
} mutex_lock(&pwm_lock);
list_add_tail(&pwm->node, &pwm_list);
mutex_unlock(&pwm_lock); platform_set_drvdata(pdev, pwm);
return ; err_free_mem:
release_mem_region(r->start, r->end - r->start + );
err_free_clk:
clk_put(pwm->clk);
err_free:
kfree(pwm);
return ret;
} static int __devexit mxc_pwm_remove(struct platform_device *pdev)
{
struct pwm_device *pwm;
struct resource *r; pwm = platform_get_drvdata(pdev);
if (pwm == NULL)
return -ENODEV; mutex_lock(&pwm_lock);
list_del(&pwm->node);
mutex_unlock(&pwm_lock); iounmap(pwm->mmio_base); r = platform_get_resource(pdev, IORESOURCE_MEM, );
release_mem_region(r->start, r->end - r->start + ); clk_put(pwm->clk); kfree(pwm);
return ;
} static struct platform_driver mxc_pwm_driver = {
.driver = {
.name = "mxc_pwm",
},
.probe = mxc_pwm_probe,
.remove = __devexit_p(mxc_pwm_remove),
}; static int __init mxc_pwm_init(void)
{
return platform_driver_register(&mxc_pwm_driver);
}
arch_initcall(mxc_pwm_init); static void __exit mxc_pwm_exit(void)
{
platform_driver_unregister(&mxc_pwm_driver);
}
module_exit(mxc_pwm_exit); MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); 二、cat driver/misc/pwm_buzzer.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/types.h>
#include <linux/io.h>
#include <linux/pwm.h>
#include <linux/fs.h> #define BUZZER_FREQENCY 1
#define DEV_NAME "buzzer" /*pwm for this buzzer*/
struct pwm_device *pwm = NULL; static int buzzer_open(struct inode *inode, struct file *filp)
{ if(pwm != NULL)
return -EBUSY; /**
* buzzer正好挂载在I.MX6的pwm4上,所以这里申请3号(从零开始算)PWM
*/
pwm = pwm_request(, "buzzer");
if ( pwm == NULL ) {
printk("buzzer open error.\n");
} return ;
} static int buzzer_release(struct inode *inode, struct file *filp)
{
pwm_disable(pwm); // 关闭pwm
pwm_free(pwm); // 释放pwm
pwm = NULL; return ;
} static long buzzer_ioctl(struct file *filp,
unsigned int cmd, unsigned long arg)
{
if(pwm == NULL)
return -EINVAL; if(arg > || arg < )
return -EINVAL; switch (cmd) {
case BUZZER_FREQENCY:
if(arg==)
pwm_disable(pwm);
else
{
pwm_config(pwm, /arg/, /arg);
pwm_enable(pwm);
}
break;
default:
break;
} return ;
} static struct file_operations buzzer_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = buzzer_ioctl,
.open = buzzer_open,
.release = buzzer_release,
}; static struct miscdevice buzzer_miscdev =
{
.minor = MISC_DYNAMIC_MINOR,
.name = DEV_NAME,
.fops = &buzzer_fops,
}; static int __init buzzer_init(void)
{
printk(" zengjf check buzzer init.\n");
misc_register(&buzzer_miscdev);
return ;
} static void __exit buzzer_exit(void)
{
misc_deregister(&buzzer_miscdev);
} module_init(buzzer_init);
module_exit(buzzer_exit); MODULE_DESCRIPTION("pwm_buzzer driver");
MODULE_LICENSE("GPL"); 三、cat main.c (demo test)
......
#define PWM_IOCTL_SET_FREQ 1
#define PWM_IOCTL_STOP 0
......
int fd = open("dev/buzzer", O_RDWR);
......
// 传入频率2000Hz
ioctl(fd, PWM_IOCTL_SET_FREQ, );
......
ioctl(fd, PWM_IOCTL_STOP);
......
close(fd);

I.MX6 PWM buzzer driver hacking with Demo test的更多相关文章

  1. OK335xS pwm buzzer Linux driver hacking

    /**************************************************************************** * OK335xS pwm buzzer L ...

  2. I.MX6 gpio-keys driver hacking

    /**************************************************************************** * I.MX6 gpio-keys driv ...

  3. I.MX6 bq27441 driver hacking

    /************************************************************************* * I.MX6 bq27441 driver ha ...

  4. I.MX6 ar1020 SPI device driver hacking

    /************************************************************************************ * I.MX6 ar1020 ...

  5. hacking a friend's Linux buzzer driver in OK335xS

    /**************************************************************************** * hacking a friend's L ...

  6. I.MX6 Linux I2C device& driver hacking

    /******************************************************************************************* * I.MX6 ...

  7. I.MX6 AD7606-4 device driver registe hacking

    /********************************************************************** * I.MX6 AD7606-4 device driv ...

  8. OK335xS pwm device register hacking

    /************************************************************************* * OK335xS pwm device regi ...

  9. I.MX6 Ar8031 device register hacking

    /***************************************************************************** * I.MX6 Ar8031 device ...

随机推荐

  1. (2)R中的数据类型和数据结构

    R中的数据结构主要面向<线性代数>中的一些概念,如向量.矩阵等.值得注意的是,R中其实没有简单数据(数值型.逻辑型.字符型等),对于简单类型会自动看做长度为1的向量.比如: > b= ...

  2. Check out our list of adidas NMD Singapore retailers

    The adidas NMD Singapore is confirmed to produce on The month of january 14th at select adidas Origi ...

  3. FILE 文件的使用 (VC、BCB、Qt)

    FILE * fp ;AnsiString filePath="";fp= fopen(filePath.c_str(),"wb");//第二个参数是文件打开方 ...

  4. Redis在Linux下的安装与配置

    Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API. Redis是 NoSQL技术阵营中的一员. 说到NoSQL, ...

  5. G.Finding the Radius for an Inserted Circle 2017 ACM-ICPC 亚洲区(南宁赛区)网络赛

    地址:https://nanti.jisuanke.com/t/17314 题目: Three circles C_{a}C​a​​, C_{b}C​b​​, and C_{c}C​c​​, all ...

  6. Sybase数据库常用函数

    Sybase数据库常用函数 一.字符串函数 1,ISNULL(EXP1,EXP2,EXP3,...) :返回第一个非空值,用法与COALESCE(exp1,exp2[,exp3...])相同: 2,T ...

  7. [Opencv]图像的梯度与边缘检测(转)

    文章来源:https://blog.csdn.net/on2way/article/details/46851451 梯度简单来说就是求导,在图像上表现出来的就是提取图像的边缘(不管是横向的.纵向的. ...

  8. python 查找

    class py_solution: def twoSum(self, nums, target): lookup = {} for i, num in enumerate(nums): if tar ...

  9. python 字典的合并

    d1 = {, } d2 = {, } d = d1.copy() d.update(d2) print(d)

  10. python 将类属性转为字典

    class dictObj(object): def __init__(self): self.x = 'red' self.y = 'Yellow' self.z = 'Green' def do_ ...