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

/*======================================================================
  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;
}

 


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值