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;
}
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;
}
相关文章推荐
- mini2440 ADC可调电阻设备驱动开发源代码(宋宝华设备驱动框架)
- mini2440 LED设备驱动开发源代码(宋宝华框架)
- mini2440 linux块设备驱动开发源代码(宋宝华框架)
- mini2440 KEY按键设备驱动(中断模式和查询模式)源代码--(宋宝华框架)
- mini2440 ADC可调电阻驱动程序开发源代码(杂项设备驱动框架)
- mini2440 rtc时钟设备驱动开发源代码(宋宝华框架)
- Linux-2.6.32.2内核在mini2440上的移植(十七)---移植PWM控制蜂鸣器驱动 .
- Linux设备驱动开发基础---字符设备驱动程序开发之mini2440_LED驱动
- Linux下PCI设备驱动程序开发 --- linux 驱动框架(二)
- Linux下PCI设备驱动程序开发 --- linux 驱动框架(二)
- 开源物联网框架ServerSuperIO 3.0正式发布(C#),跨平台:Win&Win10 Iot&Ubuntu&Ubuntu Mate,一套设备驱动跨平台挂载,附:开发套件和教程。
- 嵌入式Linux设备驱动开发课程-宋宝华
- 《连载 | 物联网框架ServerSuperIO教程》-4.如开发一套设备驱动,同时支持串口和网络通讯。附:将来支持Windows 10 IOT
- Linux设备驱动开发基础---字符设备驱动程序开发之mini2440_ADC驱动
- 设备驱动开发模拟框架
- Linux 网络设备驱动开发(三) —— 网络设备驱动基本原理和框架
- linux驱动开发之pwm蜂鸣器
- Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动
- ARM11 硬件 PWM驱动蜂鸣器设备代码
- 基于ARM-contexA9-蜂鸣器pwm驱动开发