您的位置:首页 > 编程语言

mini2440 pwm蜂鸣器设备驱动开发源代码(宋宝华框架)

2012-11-02 23:25 507 查看
/*======================================================================

mini2440 pwm蜂鸣器设备驱动开发源代码(宋宝华框架)

======================================================================*/

#include <linux/module.h>

#include <linux/types.h>

#include <linux/fs.h>

#include <linux/errno.h>

#include <linux/mm.h>

#include <linux/sched.h>

#include <linux/init.h>

#include <linux/cdev.h>

#include <linux/kernel.h>

#include <linux/device.h>

#include <asm/io.h>

#include <asm/system.h>

#include <asm/uaccess.h>

static void __iomem *pwm_base;

static void __iomem *tout0_base;

#define rGPBCON (*(volatile unsigned long *)(pwm_base + 0x00))

#define rGPBDAT (*(volatile unsigned long *)(pwm_base + 0x04))

#define rGPBUP (*(volatile unsigned long *)(pwm_base + 0x08))

#define rTCFG0 (*(volatile unsigned long *)(tout0_base + 0x00))

#define rTCFG1 (*(volatile unsigned long *)(tout0_base + 0x04))

#define rTCON (*(volatile unsigned long *)(tout0_base + 0x08))

#define rTCNTB0 (*(volatile unsigned long *)(tout0_base + 0x0c))

#define rTCMPB0 (*(volatile unsigned long *)(tout0_base + 0x10))

#define GLOBALMEM_SIZE 0x1000 /*全局内存最大4K字节*/

#define MEM_CLEAR 0x1 /*清0全局内存*/

#define GLOBALMEM_MAJOR 0 /*预设的pwm的主设备号*/

static int pwm_major = GLOBALMEM_MAJOR;//pwm_major

/*pwm设备结构体*/

struct pwm_dev

{

struct cdev cdev; /*cdev结构体*/

unsigned char mem[GLOBALMEM_SIZE]; /*全局内存*/// 划分一段内存把这段内存当作设备来处理

};

/*

//以上这个结构体代表一种设备。

//一个结构体变量代表一个设备(内核中用struct inode 来记录)

//一个打开的设备用struct file记录

//一种设备可以操作的函数用struct file_operations记录*/

struct class *pwm_class;

struct pwm_dev *pwm_devp; /*设备结构体指针*/

#if 0

void StartBuzzer(unsigned int fleg) //传参,波形的频率

{

/*设置GPB0 TOUT0,PWM OUTPUT*/

/*

rGPBCON&=~(3<<0);

rGPBCON|=(2<<0); //设置为TOUT0

rGPBUP|=1<<0; //禁用上拉电阻

//---1 确定频率和次数--定时时间

rTCFG0&=~(0XFF<<0);

rTCFG0|=0XFF<<0; //预分频值为255

rTCFG1&= ~(0XF<<20); //DMA模式,全部使能中断

rTCFG1&= ~(0XF<<0);

rTCFG1|= 1<<0; //分频为1/4

rTCNTB0=(50000000>>10)/freq;

rTCMPB0=rTCNTB0/20;//~0XFFFF;

//--2--设置手动更新,自动重载

rTCON &=~(7<<1);

rTCON |=(5<<1); //设置手动更新,自动重载,变换极性,全能死区操作

//--5 启动定时器并清除手动更新

rTCON &=~(3<<0); //20 21 位清0(手动更新0)

rTCON |=(1<<0); //设置1启动定时器4

*/

/* (1) 控制线 io GPBCON 相应位 配置成---> 中断模式*/

volatile unsigned int temp;

temp = rGPBCON;

temp &= ~(3<<0);

temp |= (2<<0);

rGPBCON = temp;

temp = rGPBUP;

temp &= ~(1<<0);

temp |= (1<<0);

rGPBUP = temp;

//(1),确定频率和次数----定时时间

//定时器配制寄存器0(TCFG0)

//定时器输入时钟频率 = PCLK / { 预分频值+1} / { 分频值}

//{预分频值} = 0~255

//{分频值} = 2, 4, 8, 16

rTCFG0 &= ~(0xFF<<0);

rTCFG0 |= (0xFF<<0);

//定时器配制寄存器1(TCFG 1 )

rTCFG1 &= ~(0xF<<20); //全部使能中断

rTCFG1 &= ~(0xF<<0);

rTCFG1 |= (1<<0); //选择PWM 定时器0 的选通输入(0001 = 1/4)

//定时器0 计数监视寄存器(TCNTO4)

rTCNTB0 = (50000000>>10)/fleg;

temp = rTCNTB0;

temp /= 9;

rTCMPB0 = temp;

// rTCMPB0 = 0;

//rTCNTB0 = 49;

//rTCMPB0 = 20;

//fleg_1 = fleg/2

/* rTCNTB0 = (50000000>>11)/fleg;

temp = rTCNTB0;

temp /= 9;

rTCMPB0 = temp;

pwm_rate_0=fleg;

cmp_count=((50000000>>10)/pwm_rate_0)/2 ;//Tcmpb的初始值

rTCMPB0=cmp_count ;

rTCNTB0=(50000000>>10)/pwm_rate_0 ;//定时器4的重装计数寄存器赋次值

rTCNTO0=(50000000>>10)/pwm_rate_0;//定时器4的计数寄存器赋次值*/

//定时器控制寄存器1(TCON)

rTCON &= ~(1<<3); //决定定时器4 的自动重载开启或关闭

rTCON |= (1<<3);

rTCON &= ~(1<<2);

//(2),设置manual update

rTCON &= ~(1<<1);

rTCON |= (1<<1);

/* (3),中断要配置的5个寄存器

rSRCPND &= ~(1<<10);

rSRCPND |= (1<<10); //INT_TIMER0设置为中断请求

rINTMOD &= ~(1<<10); //INT_TIMER0设置为IRQ;

rINTMSK &= ~(1<<10); //INT_TIMER0设置为中断服务可用;

rPRIORITY &= ~((3<<11) | (1<<2));

rPRIORITY |= (1<<2); //仲裁组第二组设置优先级为模式00

rINTPND |= (1<<10); //INT_TIMER0设置为中断源已声明中断请求;

//(4),安装中断处理函数

pISR_TIMER0 = (unsigned int )TIMER0 ;*/

//(5),启动定时器并clear manual update

rTCON &= ~(1<<0);

rTCON |= (1<<0);

rTCON &= ~(1<<1);

}

void StopBuzzer(void)

{

/*

rGPBCON&=~(3<<0);

rGPBCON|=(1<<0); //设置为为输出

rGPBDAT&=~(1<<0); //设置电平为0

rTCON &=~(1<<0); // 停止定时器并清除手动更新

*/

volatile unsigned int temp;

rTCON &= ~(1<<0);// 停止定时器并清除手动更新

temp = rGPBCON;

temp &= ~(3<<0);

temp |= (1<<0);//设置为为输出

rGPBCON = temp;

temp = rGPBDAT;

temp &= ~(1<<0);//设置电平为0

rGPBDAT = temp;

}

#endif

void StartBuzzer(unsigned int freq) //传参,波形的频率

{/*

//设置GPB0 TOUT0,PWM OUTPUT

rGPBCON&=~(3<<0);

rGPBCON|=(2<<0); //设置为TOUT0

rGPBUP|=1<<0; //禁用上拉电阻

//---1 确定频率和次数--定时时间

rTCFG0&=~(0XFF<<0);

rTCFG0|=0XFF<<0; //预分频值为255

rTCFG1&= ~(0XF<<20); //DMA模式,全部使能中断

rTCFG1&= ~(0XF<<0);

rTCFG1|= 1<<0; //0001,分频为1/4

rTCNTB0=(50000000>>10)/freq;

rTCMPB0=rTCNTB0/20;//~0XFFFF;

//--2--设置手动更新,自动重载

rTCON &=~(7<<1);

rTCON |=(5<<1); //设置手动更新,自动重载,变换极性,全能死区操作

//--5 启动定时器并清除手动更新

rTCON &=~(3<<0); //20 21 位清0(手动更新0)

rTCON |=(1<<0); //设置1启动定时器4

*/ //下面这些摘自老师,经测试可用

unsigned int temp;

temp=rTCFG0;

temp|=0xff;

rTCFG0=temp;

temp=rTCFG1;

temp&=~((0xf<<4)|(0xf<<20));

temp|=(0x2);

rTCFG1=temp;

temp=rGPBCON;

temp&=~0x3;

temp|=0x2;

rGPBCON = temp; //GPB0:OUTPUT

rGPBUP|=0X1;

temp=(50000000>>11)/freq;

rTCNTB0 = temp;

rTCMPB0 = temp/6;

rTCON|=1<<1; //开启手动更新

temp = rTCON;

temp&=~0x1f;

temp|=0x9;

rTCON = temp;

}

void StopBuzzer()

{/*

rGPBCON&=~(3<<0);

rGPBCON|=(1<<0); //设置为为输出 (因为关掉时默认电平为高还是会响)

rGPBDAT&=~(1<<0); //设置电平为0

rTCON &=~(1<<0); // 停止定时器并清除手动更新

*/

volatile unsigned int temp;

rTCON &= ~(1<<0);// 停止定时器并清除手动更新

temp = rGPBCON;

temp &= ~(3<<0);

temp |= (1<<0);//设置为为输出

rGPBCON = temp;

temp = rGPBDAT;

temp &= ~(1<<0);//设置电平为0

rGPBDAT = temp;

}

/*文件打开函数*/

int pwm_open(struct inode *inode, struct file *filp)

{ //应用程的节点传过来后struct inode *inode, struct file *filp 会自动关联赋值

/*将设备结构体指针赋值给文件私有数据指针*/

filp->private_data = pwm_devp; //private_data通常用来记录指向这个设备的指针pwm_devp

return 0;

}

/*文件释放函数*/

int pwm_release(struct inode *inode, struct file *filp)

{

return 0; //如果上面的open函数有申请资源在这里要释放

}

/* ioctl设备控制函数 */

static int pwm_ioctl(struct inode *inodep, struct file *filp, unsigned

int cmd, unsigned long arg)

{

switch (cmd)

{

case 1:

{

StartBuzzer(arg);

return 0;

}

case 0:

{

StopBuzzer();

return 0;

}

default:

return - EINVAL;/* Invalid argument */

}

return 0;

}

/*读函数*/ /*内核空间->用户空间*/

static ssize_t pwm_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)

{

return 0;

}

//10 byte 20

/*写函数*/ /*用户空间->内核空间*/

static ssize_t pwm_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)

{

return 0;

}

/* seek文件定位函数 */

static loff_t pwm_llseek(struct file *filp, loff_t offset, int orig)

{

return 0;

}

/*文件操作结构体*/

static const struct file_operations pwm_fops =

{ //除了 .owner 外其它都是函数指针

.owner =THIS_MODULE,

.llseek = pwm_llseek,

.read = pwm_read,

.write = pwm_write,

.ioctl = pwm_ioctl,

.open = pwm_open,

.release = pwm_release,

};

#if 1

/*初始化并注册cdev 并添加到内核中*/

static void pwm_setup_cdev(struct pwm_dev *dev, int index)

{

int err;

int devno = MKDEV(pwm_major, index);

/*③注册设备*/

cdev_init(&dev->cdev, &pwm_fops);

/*struct cdev 有一个拥有者成员,应当设置为 THIS_MODULE */

/*④初始化设备 */

dev->cdev.owner = THIS_MODULE; //属于本模块

dev->cdev.ops = &pwm_fops;

//dev->dev=devno;

/*⑤添加设备到内核中cdev_add*/

err = cdev_add(&dev->cdev, devno, 1);

if (err)

printk(KERN_NOTICE "Error %d adding LED%d", err, index);

}

/*设备驱动模块加载函数*/

int pwm_init(void)

{

int result;

dev_t devno = MKDEV(pwm_major, 0);/* 构造设备号devno*/

if (pwm_major)/* ①静态申请设备号*/

result = register_chrdev_region(devno, 1, "pwm_driver");

/* 动态申请设备号 */

else

{

result = alloc_chrdev_region(&devno, 0, 1, "pwm_driver");

pwm_major = MAJOR(devno);

}

if (result < 0)

return result;

printk(KERN_WARNING "pwm get major: %d\n", pwm_major); //打印主设备号

/*②分配空间. 动态申请设备结构体的内存*/ //kmalloc内核里的分配内存

pwm_devp = kmalloc(sizeof(struct pwm_dev), GFP_KERNEL);//GFP_KERNEL若暂时分配不成功则进入休眠

if (!pwm_devp) /*申请失败*/

{

result = - ENOMEM;

goto fail_malloc;

}

memset(pwm_devp, 0, sizeof(struct pwm_dev));

//③初始化并注册cdev

pwm_setup_cdev(pwm_devp, 0);

//方法1是手动创建#mknod /dev/test c 235 0

//创建节点方法2 使用下面函数自动创建

//但是自动加载时,在PC平台会有问题,因为PC平台CONFIG_DEVFS_FS没定义

//所以没创建节点,要用方法1创建

//但ARM平台(2.6.12)运行没问题,CONFIG_DEVFS_FS有定义。

/*

#ifdef CONFIG_DEVFS_FS

printk(KERN_INFO "CONFIG_DEVFS_FS\n");

devfs_mk_cdev(devno, S_IFCHR|S_IRUGO|S_IWUSR, "pwm"); //

#endif

*/

/* create your own class under /sysfs 2.6.32*/

/*⑥根据需要创建节点*/ // 2.6.18的内核需要自己创建节点 mknod

pwm_class = class_create(THIS_MODULE, "pwm_class"); //第一个pwm_class是指针

if(IS_ERR(pwm_class))

{

printk("Err: failed in creating class.\n");

return -1;

}

/* register your own device in sysfs, and this will cause udev to create corresponding device node */

device_create( pwm_class, NULL, MKDEV(pwm_major, 0), NULL, "pwm_driver");

pwm_base = (volatile unsigned *)ioremap((volatile unsigned *)0x56000010,12);

tout0_base = (volatile unsigned *)ioremap((volatile unsigned *)0x51000000,20);

return 0;

fail_malloc: unregister_chrdev_region(devno, 1);//注销设备号

return result;

}

/*模块卸载函数*/

void pwm_exit(void)

{

iounmap(pwm_base);

iounmap(tout0_base);

/*从内核中删除设备cdev_del*/

cdev_del(&pwm_devp->cdev); /*注销cdev*/

/*删除分配节点(如果有分配节点)*/

device_destroy(pwm_class, MKDEV(pwm_major, 0));

class_destroy(pwm_class); /*销毁pwm_class */

/*释放空间*/

kfree(pwm_devp); /*释放设备结构体内存*/

/*释放设备号*/

unregister_chrdev_region(MKDEV(pwm_major, 0), 1);

}

MODULE_AUTHOR("Song Baohua");

MODULE_LICENSE("Dual BSD/GPL");

module_param(pwm_major, int, S_IRUGO);

module_init(pwm_init);

module_exit(pwm_exit);

#endif

#if 0

/*初始化并注册cdev 并添加到内核中*/

static void pwm_setup_cdev(struct pwm_dev *dev, int index)

{

int err;

int devno = MKDEV(pwm_major, index);

cdev_init(&dev->cdev, &pwm_fops); /*③注册设备*/

dev->cdev.owner = THIS_MODULE; /*④初始化设备 */ //属于本模块 struct cdev 有一个拥有者成员,应当设置为 THIS_MODULE

dev->cdev.ops = &pwm_fops;

//dev->dev=devno;

err = cdev_add(&dev->cdev, devno, 1); /*⑤添加设备到内核中cdev_add*/

if (err)

printk(KERN_NOTICE "Error %d adding LED%d", err, index);

}

/*设备驱动模块加载函数*/

int pwm_init(void)

{

int result;

dev_t devno = MKDEV(pwm_major, 0);/* 构造设备号devno*/

if (pwm_major) /* ①静态申请设备号*/

result = register_chrdev_region(devno, 1, "pwm_driver");

else /* 动态申请设备号 */

{

result = alloc_chrdev_region(&devno, 0, 1, "pwm_driver");

pwm_major = MAJOR(devno);

}

if (result < 0)

return result;

printk(KERN_WARNING "pwm get major: %d\n", pwm_major); //打印主设备号

/*②分配空间. 动态申请设备结构体的内存*/ //kmalloc内核里的分配内存

pwm_devp = kmalloc(sizeof(struct pwm_dev), GFP_KERNEL);//GFP_KERNEL若暂时分配不成功则进入休眠

if (!pwm_devp) /*申请失败*/

{

result = - ENOMEM;

goto fail_malloc;

}

memset(pwm_devp, 0, sizeof(struct pwm_dev));

pwm_setup_cdev(pwm_devp, 0);//③初始化并注册cdev

//方法1是手动创建#mknod /dev/test c 235 0

//创建节点方法2 使用下面函数自动创建

//但是自动加载时,在PC平台会有问题,因为PC平台CONFIG_DEVFS_FS没定义

//所以没创建节点,要用方法1创建

//但ARM平台(2.6.12)运行没问题,CONFIG_DEVFS_FS有定义。

/*

#ifdef CONFIG_DEVFS_FS

printk(KERN_INFO "CONFIG_DEVFS_FS\n");

devfs_mk_cdev(devno, S_IFCHR|S_IRUGO|S_IWUSR, "pwm"); //

#endif

*/

/* create your own class under /sysfs 2.6.32*/

/*⑥根据需要创建节点*/ // 2.6.18的内核需要自己创建节点 mknod

pwm_class = class_create(THIS_MODULE, "pwm_class"); //第一个my_class是指针

if(IS_ERR(pwm_class))

{

printk("Err: failed in creating class.\n");

return -1;

}

/* register your own device in sysfs, and this will cause udev to create corresponding device node */

device_create( pwm_class, NULL, MKDEV(pwm_major, 0), NULL, "pwm_driver");

pwm_base = (volatile unsigned *)ioremap((volatile unsigned *)0x56000010,12);

tout0_base = (volatile unsigned *)ioremap((volatile unsigned *)0x51000000,20);

return 0;

fail_malloc: unregister_chrdev_region(devno, 1);//注销设备号

return result;

}

/*模块卸载函数*/

void pwm_exit(void)

{

iounmap(pwm_base);

iounmap(tout0_base);

cdev_del(&pwm_devp->cdev); /*注销cdev*//*从内核中删除设备cdev_del*/

device_destroy(pwm_class, MKDEV(pwm_major, 0)); /*删除分配节点(如果有分配节点)*/

class_destroy(pwm_class); /*销毁my_class */

kfree(pwm_devp); /*释放设备结构体内存*/

unregister_chrdev_region(MKDEV(pwm_major, 0), 1); /*释放设备号*/

}

MODULE_AUTHOR("Lin");

MODULE_LICENSE("Dual BSD/GPL");

module_param(pwm_major, int, S_IRUGO);

module_init(pwm_init);

module_exit(pwm_exit);

#endif

/*======================================================================

应用程序测试代码

======================================================================*/

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <stdio.h>

#include <stdlib.h>

#define MYPWM "/dev/pwm_driver"

#define PWM_ON 1

#define PWM_OFF 0

void Delay_MS( unsigned int time) //50 ns

{

unsigned int i,j;

for ( i=0; i<time; i++)

{

for(j=0;j<30000;j++)

{

}

}

}

int main(void)

{

int fd,i=0;

unsigned int tonefreq[7]={1046,1175,1318,1397,1569,1760,1976};

//unsigned short tonefreq[7]={523,578,659,698,784,880,988};

fd = open(MYPWM,O_RDWR,0666);

if (fd < 0)

{

perror("open device pwm_driver error\n");

exit(1);

}

printf("open /dev/pwm_driver success!\n");

while(i<5)

{

ioctl(fd, PWM_ON, tonefreq[0]);

Delay_MS(200);

ioctl(fd, PWM_OFF, 0);

Delay_MS(200);

ioctl(fd, PWM_ON, tonefreq[1]);

Delay_MS(200);

ioctl(fd, PWM_OFF,0);

Delay_MS(200);

ioctl(fd, PWM_ON, tonefreq[2]);

Delay_MS(200);

ioctl(fd, PWM_OFF, 0);

Delay_MS(200);

i++;

}

close(fd);

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐