本驱动程序经过测试,证明运行良好,下面附有驱动原码和测试程序,若有哪位引用了我的驱动,请注明出处。谢谢!
/**************************************************************** Copyright(c) 2009-2010,Newkoom Net Tech.Co,.Ltd 模块名称(Filename): beep_s3c2410.c 项目名称(Projectname): RPM(Remote Power Manager System) 版本号(Version): 1.0.0 创建日期(Date): 2009-11-22 作者(Author): ZMF(Zheng meifu) 功能描述(Description): buzzer(beep) driver for linux2.6.14.1 其他说明(Others): 修改记录(History): 调试成功:2009-11-23 2009-12-1:之前只能在定时任务结束后才释放锁,今天改为 设置完ioctl后立即释放,以便连续发声。 ****************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BEEPDEV_NAME "beep" /*设备名 beep*/ #define PWM_IOCTL_SET_FREQ 1 /*定义宏常量,用于后面的ioctl中的switch case*/ #define PWM_IOCTL_STOP 2 #define SET_RIGHT_ACTION_BEEP 3 // 此处宏数字不能改,和以下beep_const序号相关 #define SET_ERROR_ACTION_BEEP 4 // 同上 #define SET_KEY_PRESS_BEEP 5 // 同上 #define BEON (1<<(sizeof(int)*8-1)) #define BEOF 0 #define PRESET_FREQ 1000 #define KEY_DELAYMS(X) (HZ/(1000/(X))) static struct semaphore lock; /*定义信号量 lock*/ static struct semaphore ioctl_lock; /* freq: pclk/50/16/65536 ~ pclk/50/16 * if pclk = 50MHz, freq is 1Hz to 62500Hz * human ear : 20Hz~ 20000Hz */ const int beep_const[3][8]={ {500|BEON,100|BEOF,0,0,0,0,0,0}, // right {100|BEON,50|BEOF,100|BEON,50|BEOF,100|BEON,50|BEOF,0,0}, // error {100|BEON,50|BEOF,0,0,0,0,0,0}, // key }; struct timer_list beep_timer; static int beep_step; static void PWM_Set_Freq(unsigned long freq); static void PWM_Stop(void); static void beep_timer_handler(unsigned long data){ int onofftime=beep_const[data][beep_step++]; if(onofftime !=0){ beep_timer.data = data; mod_timer(&beep_timer, jiffies+ KEY_DELAYMS(onofftime&(~BEON))); if(onofftime&BEON) PWM_Set_Freq(PRESET_FREQ); else PWM_Stop(); }else{ PWM_Stop(); del_timer(&beep_timer); //up(&ioctl_lock); } } static void set_beep_type(int type){ int tmp_time= beep_const[type-3][0]&(~BEON); del_timer(&beep_timer); init_timer(&beep_timer); beep_timer.expires = jiffies + KEY_DELAYMS(tmp_time); beep_timer.function = beep_timer_handler; beep_timer.data = type-3; beep_step=1; add_timer(&beep_timer); PWM_Set_Freq(PRESET_FREQ); } static void PWM_Set_Freq( unsigned long freq ) /*设置pwm的频率,配置各个寄存器*/ { unsigned long tcon, tcnt, tcfg1, tcfg0,pclk; struct clk *clk_p; /*set GPB0 as tout0, pwm output 设置GPB0为tout0,pwm输出*/ s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_TOUT0); tcon = __raw_readl(S3C2410_TCON); /*读取寄存器TCON到tcon*/ tcfg1 = __raw_readl(S3C2410_TCFG1); /*读取寄存器TCFG1到tcfg1*/ tcfg0 = __raw_readl(S3C2410_TCFG0); /*读取寄存器TCFG0到tcfg0*/ //prescaler = 50 tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; /* S3C2410_TCFG_PRESCALER0_MASK定时器0和*/ /* 1的预分频值的掩码,TCFG[0~8]*/ tcfg0 |= (50 - 1); /* 预分频为50 */ //mux = 1/16 tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK; /*S3C2410_TCFG1_MUX0_MASK定时器0分割值的掩*/ /*码:TCFG1[0~3]*/ tcfg1 |= S3C2410_TCFG1_MUX0_DIV16; /*定时器0进行16分割*/ __raw_writel(tcfg1, S3C2410_TCFG1); /*把tcfg1的值写到分割寄存器S3C2410_TCFG1中*/ __raw_writel(tcfg0, S3C2410_TCFG0); /*把tcfg0的值写到预分频寄存器S3C2410_TCFG0中*/ clk_p = clk_get(NULL, "pclk"); /*得到pclk*/ pclk = clk_get_rate(clk_p); tcnt = (pclk/50/16)/freq; /*得到定时器的输入时钟,进而设置PWM的调制频率*/ __raw_writel(tcnt, S3C2410_TCNTB(0)); /*PWM脉宽调制的频率等于定时器的输入时钟 */ __raw_writel(tcnt/2, S3C2410_TCMPB(0)); /*占空比是50%*/ tcon &= ~0x1f; tcon |= 0xb; /*disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0*/ __raw_writel(tcon, S3C2410_TCON); tcon &= ~2; /*clear manual update bit*/ __raw_writel(tcon, S3C2410_TCON); /*把tcon写到计数器控制寄存器S3C2410_TCON中*/ } static void PWM_Stop( void ){ s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_OUTP); /*设置GPB0为输出*/ s3c2410_gpio_setpin(S3C2410_GPB0, 0); /*设置GPB0为低电平,使蜂鸣器停止*/ } static int s3c24xx_pwm_open(struct inode *inode, struct file *file){ if (!down_trylock(&lock)) /*是否获得信号量,是down_trylock(&lock)=0,否则非0*/ return 0; else return -EBUSY; /*返回错误信息:请求的资源不可用*/ } static int s3c24xx_pwm_close(struct inode *inode, struct file *file){ up(&lock); /*释放信号量lock*/ return 0; } /*cmd 是1,表示设置频率;cmd 是2 ,表示停止pwm*/ static int s3c24xx_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ if(!down_trylock(&ioctl_lock)){ 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=2 即进入case PWM_IOCTL_STOP*/ PWM_Stop(); /*停止蜂鸣器*/ break; case SET_RIGHT_ACTION_BEEP: case SET_ERROR_ACTION_BEEP: case SET_KEY_PRESS_BEEP: set_beep_type(cmd); break; default: break; } up(&ioctl_lock); return 0; /*成功返回*/ }else return -EBUSY; } /*初始化设备的文件操作的结构体*/ static struct file_operations dev_fops = { .owner = THIS_MODULE, .open = s3c24xx_pwm_open, .release = s3c24xx_pwm_close, .ioctl = s3c24xx_pwm_ioctl, }; static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR, .name = BEEPDEV_NAME, .fops = &dev_fops, .devfs_name[0] = '/0', }; static int __init dev_init(void){ int ret; init_MUTEX(&lock); /*初始化一个互斥锁*/ init_MUTEX(&ioctl_lock); misc.devfs_name[0]='/0'; // 这句使/dev/misc/下产生beep设备文件 ret = misc_register(&misc); /*注册一个misc设备*/ if(ret){ printk (BEEPDEV_NAME "/tinit error!!/n"); } else printk (BEEPDEV_NAME "/tinitialized by newkoom.com/n"); return ret; } static void __exit dev_exit(void){ del_timer(&beep_timer); misc_deregister(&misc); /*注销设备*/ } module_init(dev_init); module_exit(dev_exit); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("newkoom"); MODULE_DESCRIPTION("S3C2410 RPM or yls3c2410 devlp board beep driver"); /* 1 计数器控制寄存器 1)配置定时器输入时钟 TCFG0-时钟配置寄存器0,用于获得预分频值(1~255) TCFG1-时钟配置寄存器1,用于获得分割值(2,4,8,16,32) 定时器输入时钟频率=PLCK/{预分频+1}/{分割值} 2)配置PWM的占空比 TCNTB0-定时器0计数缓存寄存器 ,是由定时器的输入时钟分频得到, 是脉宽调制的频率 TCMTB0-定时器0比较缓存寄存器 ,用于设定PWM的占空比 ,寄存器值为高定平的 假设TCNTB0的频率是160,如果TCMTB0是110,则PWM在110个周期是高定平, 50周期是低电平,从而占空比为11:5 3)定时器控制寄存器TCON TCON[0~4]用于控制定时器0 2.__raw_readl和__raw_writel 读端口寄存器用__raw_readl(a ),该函数从端口a 返回一个32 位的值。 相关的定义在include/asm-arm/io.h 中。#define __raw_readl(a) (*(volatile unsigned int*)(a)), 写端口寄存器用__raw_writel(v,a),该函数将一个32 位的值写入端口a 中。 相关的定义在include/asm-arm/io.h 中。#define __raw_writel(v,a) (*(volatile unsigned int*)(a) = (v))。 此处设置功能控制寄存器,将相应的引脚设为输出状态。 3 .gpio操作 gpio_cfgpin 配置相应GPIO口的功能 gpio_setpin IO口为输出功能时,写引脚 4 基于信号量的Llinux 的并发控制 在驱动程序中,当多个线程同时访问相同的资源时,可能会引发“竞态”, 因此必须对共享资源进行并发控制。信号量(绝大多数作为互斥锁使用) 是一种进行并发控制的手段(还有自旋锁,它适合于保持时间非常短的时间)。 信号量只能在进程的上下文中使用。 void init_MUTEX(&lock)初始化一个互斥锁,即他把信号量lock设置为1 void up (&lock) 释放信号量,唤醒等待者 int down_trylock(&lock) 尝试获得信号量lock ,如果能够立刻获得,就获得信号量, 并返回为0.否则返回非0.并且它不会导致休眠,可以在中断上下文中使用。 在PWM中,当计数值溢出时,就会引发计数中断。所以在这里用这个函数来 获得信号。 */ /**************************************************************** Copyright(c) 2009-2010,Newkoom Net Tech.Co,.Ltd 模块名称(Filename): beep.cpp 项目名称(Projectname): RPM(Remote Power Manager System) 版本号(Version): 1.0.0 创建日期(Date): 2009-11-22 作者(Author): ZMF(Zheng meifu) 功能描述(Description): beep class define 其他说明(Others): 修改记录(History): 调试成功: ****************************************************************/ class Cbeep{ protected: int beep_fd; int freq; int cmd; public: Cbeep(); ~Cbeep(); int close_buzzer(void); int open_buzzer(void); //打开蜂鸣器 int stop_buzzer(void); int set_buzzer(int freq, int cmd); }; #include /*标准输入输出定义*/ #include /*POSIX终端控制定义*/ #include /*Unix 标准函数定义*/ #include /*标准函数库定义*/ #include #include #include #include #include "beep.h" Cbeep::Cbeep(){ beep_fd=-1; freq=1000; cmd = SET_BEEP_OFF; } Cbeep::~Cbeep(){ close_buzzer(); } int Cbeep::open_buzzer(void) //打开蜂鸣器 { beep_fd = open(BEEP_FILE, 0); //打开pwm设备驱动文件 if (beep_fd < 0) { perror("open /dev/misc/beep device"); exit(1); //打开错误,则终止进程。退出参数为1 } return beep_fd; // any function exit call will stop the buzzer // atexit(close_buzzer); //退出回调close_buzzer } int Cbeep::close_buzzer(void){ //关闭蜂鸣器 int ret=0; if (beep_fd >= 0) { close(beep_fd); //关闭设备驱动文件 beep_fd = -1; ret=ioctl(beep_fd, SET_BEEP_OFF); //停止蜂鸣器 if(ret<0) perror("close_buzzer ioctl"); } return ret; } int Cbeep::set_buzzer(int freqt, int cmdt){ // this IOCTL command is the key to set frequency int ret = ioctl(beep_fd, cmdt, freqt); //设置频率 if(ret < 0) { //如果输入的频率错误 perror("set buzzer freq & cmd"); // exit(1); //退出,返回1 } return ret; } int Cbeep::stop_buzzer(void){ int ret = ioctl(beep_fd, SET_BEEP_OFF); //关闭蜂鸣器 if(ret < 0) { //如果无法关闭蜂鸣器 perror("stop the buzzer"); exit(1); //退出返回1 } return ret; }