首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 嵌入开发 > 驱动开发 >

cortex-A8,s5pv210,pwm蜂鸣器.本人初学,对这个板材不熟悉

2013-08-04 
cortex-A8,s5pv210,pwm蜂鸣器.本人初学,对这个板子不熟悉各位大神,希望你门能不吝赐教,我现在在S5PV210开

cortex-A8,s5pv210,pwm蜂鸣器.本人初学,对这个板子不熟悉
各位大神,希望你门能不吝赐教,我现在在S5PV210开发板上编写PWM驱动,想先写一个PWM控制蜂鸣器的驱动做当实验,但不能成功,我把代码贴出来,希望有大神可以指导一下我这样的菜鸟,真的非常感谢。


/*操控PWM主要分以下四步:
1、把相应的引脚配置成TOUT输出。

2、设置定时器的输出时钟频率。

3、设置脉冲的具体宽度。

4、最后就是对PWM的控制,它是通过寄存器TCON来实现的,
   一般来说每个定时器主要有4个位要配置(定时器0多一个死区位):
1).启动/终止位
2).手动更新位
3).输出反转位
4).自动重载位

一般要掌握好两个因素:
1、占空比,控制其震动的强度
2、频率,控制震动的舒适度
*/

//#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/clk.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/miscdevice.h>

#include <asm/irq.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/mach/time.h>

#include <mach/hardware.h>
#include <mach/regs-gpio.h>
//#include <mach/regs-irg.h>

#include <plat/regs-timer.h>

#define DEVICE_NAME "pwm"// 设备名

#define PWM_IOCTL_SET_FREQ 1// 定义宏变量,用于后面的ioctl中的控制命令
#define PWM_IOCTL_STOP 0// 定义宏变量,用于后面的ioctl中的控制命令

static void __iomem *pwm_base_addr; 
static struct resource  *io_mem;

static unsigned long tcon;
static unsigned long tcfg1;
static unsigned long tcfg0;

#define REMAP_SIZE 0x14

#define PWMTIMER_BASE0xE2500000

#define TCFG0 (*(volatile unsigned long *)(pwm_base_addr + 0x00)) 
#define TCFG1 (*(volatile unsigned long *)(pwm_base_addr + 0x04)) 
#define TCON (*(volatile unsigned long *)(pwm_base_addr + 0x08)) // 控制寄存器
#define TCNTB0 (*(volatile unsigned long *)(pwm_base_addr + 0x0c)) // 计数缓冲寄存器
#define TCMPB0 (*(volatile unsigned long *)(pwm_base_addr + 0x10)) // 比较计数寄存器


/*定义信号量lock用于互斥,保证该驱动程序只能由一个进程使用*/
static struct semaphore lock;

/*配置pwm的频率,配置各个寄存器*/
static void pwm_Set_Freq(unsigned long freq)
{
unsigned long tcnt;
unsigned long pclk;
struct clk *clk_p;

tcon  = readl(TCON);//读取寄存器 TCON 到 tcon

    clk_p = clk_get(NULL, "pclk"); // 得到 pclk


    pclk = clk_get_rate(clk_p);
    tcnt = (pclk/66/16)/freq; // 得到定时器的输入时钟,进而设置 PWM 的调制频率
    printk("tcnt = %lu\n",tcnt);
   
writel(tcnt, TCNTB0);
writel(tcnt/2, TCMPB0);
    //TCNTB0 = tcnt; // 设置定时器0计数缓存寄存器的值,PWM脉宽调制的频率等于定时器的输入时钟,确定一个计数周期的时间长度
    //TCMPB0 = tcnt/2; // 设置定时器0比较缓存寄存器的值,占空比是 50%
    printk("TCNTB0 = %lu, TCMP0 = %lu\n", TCNTB0,TCMPB0);
/*清空低5位,其中:
TCON[4]--Dead zone enable, 
TCON[3]--Timer 0 auto reload on/off, 
TCON[2]--Timer 0 output inverter on/off,
TCON[1]--Timer 0 manual update,
TCON[0]--Timer 0 start/stop
*/

tcon &= ~0x1f;// ~0001 1111 = 1110 0000
    tcon |= 0xb;// 关闭死区,自动重载,关反相器,更新TCNTB0和TCMPB0、启动定时器0
    tcon &= ~2;   // clear manual update bit
writel(tcon, TCON);

    //TCON &= ~0x1f;// ~0001 1111 = 1110 0000
    //TCON |= 0xb;// 关闭死区,自动重载,关反相器,更新TCNTB0和TCMPB0、启动定时器0

    //TCON &= ~2;   // clear manual update bit
    printk("TCON = %lu\n", TCON);
}

static void pwm_stop(void)
{
    s3c_gpio_cfgpin(S5PV210_GPD0(1), 1); // 设置GPD01为输出
    s3c_gpio_setpin(S5PV210_GPD0(1), 0); // 设置GPD01为低电平,使蜂鸣器停止
}

static int tq_pwm_open(struct inode *inode, struct file *file)
{
    if (!down_trylock(&lock))// 是否获得信号量,是 down_trylock(&lock)=0,否则非 0
{
// 1.设置蜂鸣器接口为TOUT口
// 查手册得知蜂鸣器接口为GPD0_1接口,TOUT为0010.
s3c_gpio_cfgpin(S5PV210_GPD0(1), 0x02);

// 2.设置TCFG0寄存器,prescaler = 66
// 频率
tcfg0 = readl(TCFG0); //读取寄存器 TCFG0 到 tcfg0
tcfg0 &= ~0xFF;  // 清除TCFG0[0~7],将寄存器的后8位清零
writel(tcfg0, TCFG0);
tcfg0 |= (66 - 1); // 设置预分频为 66
writel(tcfg0, TCFG0);

//TCFG0 &= ~0xFF;  // 清除TCFG0[0~7],将寄存器的后8位清零
//TCFG0 |= (66 - 1); // 设置预分频为 66

// 3.设置TCFG1寄存器,mux = 1/16


// 占空比
tcfg1 = readl(TCFG1); //读取寄存器 TCFG1 到 tcfg1
tcfg1 &= ~0x0F; // 清除TCFG1[0~3],将寄存器的后4位清零
writel(tcfg1,TCFG1);
tcfg1 |= 4; // 定时器 0 进行 16 分割
writel(tcfg1,TCFG1);
//TCFG1 &= ~0x0F; // 清除TCFG1[0~3],将寄存器的后4位清零
//TCFG1 |= 4; // 定时器 0 进行 16 分割

return 0;
}
    else
        return -EBUSY; // 返回错误信息:请求的资源不可用
}

static int tq_pwm_close(struct inode *inode, struct file *file)
{
    pwm_stop();
    up(&lock); // 释放信号量 lock
    return 0;
}

/*cmd 是 1,表示设置频率;cmd 是 0 ,表示停止 pwm*/
static int tq_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
    switch (cmd) {
    case PWM_IOCTL_SET_FREQ: // if cmd=1 即进入 case PWM_IOCTL_SET_FREQ
        if (arg == 0) // 如果设置的频率参数是 0
            return -EINVAL; // 返回错误信息,表示向参数传递了无效的参数
        pwm_Set_Freq(arg); // 否则设置频率
        break;
    case PWM_IOCTL_STOP: // if cmd=0 即进入 case PWM_IOCTL_STOP
        pwm_stop(); // 停止蜂鸣器
        break;
    }
    return 0; //成功返回
}

/*初始化设备的文件操作的结构体*/
static struct file_operations dev_fops = {
    .owner= THIS_MODULE,
    .open= tq_pwm_open,
    .release= tq_pwm_close,
    .ioctl= tq_pwm_ioctl,
};

static struct miscdevice misc = {
    .minor= MISC_DYNAMIC_MINOR,
    .name= DEVICE_NAME,
    .fops= &dev_fops,
};

static int __init dev_init(void)
{
int ret;

//申请IO内存,不是必须的。
if (!request_mem_region(PWMTIMER_BASE, REMAP_SIZE, "pwm"))
{
printk("failed to get memory region\n");


ret = -EBUSY;
return ret;
}
printk("success to get memory region\n");

pwm_base_addr = ioremap(PWMTIMER_BASE, REMAP_SIZE); 
if (NULL == pwm_base_addr)
{
ret = -EIO;
printk("pwm_base_addr remap failed\n");
release_mem_region(PWMTIMER_BASE, REMAP_SIZE);
return ret;
}
printk("pwm_base_addr remap success\n");

    init_MUTEX(&lock); // 初始化一个互斥锁
    ret = misc_register(&misc); // 注册一个 misc 设备
    printk (DEVICE_NAME"\tinitialized\n");
    return ret;
}

static void __exit dev_exit(void)
{
iounmap(pwm_base_addr);// 取消映射
release_mem_region(PWMTIMER_BASE, REMAP_SIZE); // 释放I/O内存
    misc_deregister(&misc); // 注销设备
printk("<1>pwm_exit!\n");
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("HZJG_RLF");
MODULE_DESCRIPTION("TQ210 PWM Driver");
S5PV210 PWM 蜂鸣器 驱动
[解决办法]
这个是否还需要你开启PWM外设时钟呢?
[解决办法]
有可能是GPIO口的使能端没打开吧

热点排行