基于 2.6 内核的 pwm 蜂鸣器驱动设计

分享到:
点击量: 216007

  基于 2.6 内核的 pwm 蜂鸣器驱动设计

  一、开发环境 二、PWM 怎样工作在 ARM Linux 中 1. 什么是 PWM? PWM(脉冲宽度调制)简单的讲是一种变频技术之一,是靠改变脉冲宽度 来控制输出电压,通过改变周期来控制其输出频率。如果还不是很清楚,好吧, 来看看我们实际生活中的例子,我们的电风扇为什么扭一下按扭,风扇的转速就 会发生变化;调一下收音机的声音按钮,声音的大小就会发生变化;还有待会儿 我们要讲的蜂鸣器也会根据不同的输入值而发出不同频率的叫声等等!!这些都 是 PWM 的应用,都是通过 PWM 输出的频率信号进行控制的。 2. ARM Linux 中的 PWM 根据 S3C2440 的手册介绍,S3C2440A 内部有 5 个 16 位的定时器,定 时器 0、1、2、3 都带有脉冲宽度调制功能(PWM),定时器 4 是一个没有输出引 脚的内部定时器,定时器 0 有一个用于大电流设备的死区生成器。看下图解释 吧!!

  由 S3C2440 的技术手册和上面这幅结构图,我们来总结一下 2440 内部定 时器模块的特性吧: 1) 5 个 16 位的定时器, 共 定时器 0、 2、 都带有脉冲宽度调制功能(PWM); 1、 3 2)每个定时器都有一个比较缓存寄存器(TCMPB)和一个计数缓存寄存器 (TCNTB); 3)定时器 0、1 共享一个 8 位的预分频器(预定标器),定时器 2、3、4 共享 另一个 8 位的预分频器(预定标器),其值范围是 0~255; 4)定时器 0、1 共享一个时钟分频器,定时器 2、3、4 共享另一个时钟分 频器,这两个时钟分频器都能产生 5 种不同的分频信号值(即:1/2、1/4、1/8、 1/16 和 TCLK); 5)两个 8 位的预分频器是可编程的且根据装载的值来对 PCLK 进行分频, 预分频器和钟分频器的值分别存储在定时器配置寄存器 TCFG0 和 TCFG1 中; 6)有一个 TCON 控制寄存器控制着所有定时器的属性和状态,TCON 的第 0~7 位控制着定时器 0、第 8~11 位控制着定时器 1、第 12~15 位控制着定时器 2、第 16~19 位控制着定时器 3、第 20~22 位控制着定时器 4。 还是根据 S3C2440 手册的描述和上图的结构,要开始一个 PWM 定时器功 能的步骤如下(假设使用的是**个定时器):

  1)分别设置定时器 0 的预分频器值和时钟分频值,以供定时器 0 的比较缓 存寄存器和计数缓存寄存器用; 2)设置比较缓存寄存器 TCMPB0 和计数缓存寄存器 TCNTB0 的初始值(即 定时器 0 的输出时钟频率); 3)关闭定时器 0 的死区生成器(设置 TCON 的第 4 位); 4)开启定时器 0 的自动重载(设置 TCON 的第 3 位); 5)关闭定时器 0 的反相器(设置 TCON 的第 2 位); 6)开启定时器 0 的手动更新 TCNTB0&TCMPB0 功能(设置 TCON 的第 1 位); 7)启动定时器 0(设置 TCON 的第 0 位); 8)**定时器 0 的手动更新 TCNTB0&TCMPB0 功能(设置 TCON 的第 1 位)。 由此可以看到,PWM 的输出频率跟比较缓存寄存器和计数缓存寄存器的取 值有关, 而比较缓存寄存器和计数缓存寄存器的值又跟预分频器和时钟分频器的 值有关;要使用 PWM 功能其实也就是对定时器的相关寄存器进行操作。手册上 也有一个公式:定时器输出频率 = PCLK / {预分频器值 + 1} / 时钟分频值。下 面我们来通过一个蜂鸣器的实例来说明 PWM 功能的使用。 三、蜂鸣器驱动实例 1. 蜂鸣器的种类和工作原理

  蜂鸣器主要分为压电式蜂鸣器和电磁式蜂鸣器两种类型。 压电式蜂鸣器主要由多谐振荡器、压电蜂鸣片、阻抗匹配器及共鸣箱、外 壳等组成。有的压电式蜂鸣器外壳上还装有二极管" href="http://www.dz-z.com/product/detail/1414762.html" target="_blank">发光二极管。多谐振荡器由晶体管或 集成电路构成。当接通电源后(1.5~15V 直流工作电压),多谐振荡器起振,输 出 1.5~2.5kHZ 的音频信号,阻抗匹配器推动压电蜂鸣片发声。 电磁式蜂鸣器由振荡器、电磁线圈、磁铁、振动膜片及外壳等组成。接通 电源后,振荡器产生的音频信号电流通过电磁线圈,使电磁线圈产生磁场。振动 膜片在电磁线圈和磁铁的相互作用下,周期性地振动发声。 有源蜂鸣器和无源蜂鸣器的区别:这个“源”字是不是指电源,而是指震荡 源,即有源蜂鸣器内有振荡源而无源蜂鸣器内部没有振荡源。有振荡源的通电就 可以发声,没有振荡源的需要脉冲信号驱动才能发声。

  2. 开发板上蜂鸣器原理图分析

  由原理图可以得知, 蜂鸣器是通过 GPB0 IO 口使用 PWM 信号驱动工作的, 而 GPB0 口是一个复用的 IO 口,要使用它得先把他设置成 TOUT0 PWM 输出 模式。 3. 编写合适开发板的蜂鸣器驱动程序,文件名:my2440_pwm.c ================================================ Name Author Date Copyright : my2440_pwm.c : Huang Gang : 25/11/09 : GPL

  Description : my2440 pwm driver ================================================ */ #include#include#include#include

  #include#include#include#include #include #include #include#define PWM_MAJOR 0 //主设备号 //设备名称

  #define PWM_NAME "my2440_pwm"

  static int device_major = PWM_MAJOR; //系统动态生成的主设备号 //打开设备 static int pwm_open(struct inode *inode, struct file *file) { //对 GPB0 复用口进行复用功能设置,设置为 TOUT0 PWM 输出 s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_TOUT0); return 0; } //关闭设备 static int pwm_close(struct inode *inode, struct file *file) { return 0; } //对设备进行控制 static int pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)

  {if(cmd <= 0)//如果输入的参数小于或等于 0 的话,就让蜂鸣器停止工作 { //这里又恢复 GPB0 口为 IO 口输出功能, 由原理图可知直接给低电平 可让蜂鸣器停止工作 s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_OUTP); s3c2410_gpio_setpin(S3C2410_GPB0, 0); } else//如果输入的参数大于 0,就让蜂鸣器开始工作,不同的参数,蜂鸣 器的频率也不一样 { //定义一些局部变量 unsigned long tcon; unsigned long tcnt; unsigned long tcfg1; unsigned long tcfg0; struct clk *clk_p; unsigned long pclk; //以下对各寄存器的操作结合上面讲的开始一个 PWM 定时器的步骤 和 2440 手册 PWM 寄存器操作部分来看就比较容易理解 tcfg1 = __raw_readl(S3C2410_TCFG1); 1 的值 tcfg0 = __raw_readl(S3C2410_TCFG0); 0 的值 tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; tcfg0 |= (50 - 1); //设置 tcfg0 的值为 49 //读取定时器配置寄存器 //读取定时器配置寄存器

  tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK;

  tcfg1 |= S3C2410_TCFG1_MUX0_DIV16; 0x0011 即:1/16 __raw_writel(tcfg1, S3C2410_TCFG1); 置寄存器 1 中 __raw_writel(tcfg0, S3C2410_TCFG0); 置寄存器 0 中 clk_p = clk_get(NULL, "pclk"); pclk = clk_get_rate(clk_p); 钟频率,在 include/linux/clk.h 中定义 tcnt = (pclk/50/16)/cmd; (pclk/{prescaler0 + 1}/divider value)

  //设置 tcfg1 的值为

  //将值 tcfg1 写入定时器配

  //将值 tcfg0 写入定时器配

  //从系统平台时钟队列中获取 pclk 的时

  //计算定时器 0 的输出时钟频率

  __raw_writel(tcnt, S3C2410_TCNTB(0)); 寄存器的值

  //设置定时器 0 计数缓存

  __raw_writel(tcnt/2, S3C2410_TCMPB(0)); //设置定时器 0 比较缓存 寄存器的值 tcon = __raw_readl(S3C2410_TCON); 的值 tcon &= ~0x1f; tcon |= 0xb; //关闭死区、自动重载、关反相器、更新 TCNTB0&TCMPB0、启动定时器 0 __raw_writel(tcon, S3C2410_TCON); 0-4 位,即对定时器 0 进行控制 tcon &= ~2; __raw_writel(tcon, S3C2410_TCON); //**定时器 0 的手动更新位 } return 0; } //设备操作结构体 static struct file_operations pwm_fops = //设置定时器控制寄存器的 //读取定时器控制寄存器

  { .owner .open = THIS_MODULE, = pwm_open,

  .release = pwm_close, .ioctl }; //定义一个设备类 static struct class *pwm_class; static int __init pwm_init(void) { //注册为字符设备,主设备号为 0 让系统自动分配,设备名为 my2440_pwm,注册成功返回动态生成的主设备号 device_major = register_chrdev(PWM_MAJOR, PWM_NAME, &pwm_fops); if(device_major < 0) { printk(PWM_NAME " register falid!\n"); return device_major; } //注册一个设备类,使 mdev 可以在/dev/目录下自动建立设备节点 pwm_class = class_create(THIS_MODULE, PWM_NAME); if(IS_ERR(pwm_class)) { printk(PWM_NAME " register class falid!\n"); return -1; } = pwm_ioctl,

  //创建一个设备节点,设备名为 PWM_NAME,即:my2440_pwm device_create(pwm_class, NULL, MKDEV(device_major, 0), NULL, PWM_NAME); return 0; } static void __exit pwm_exit(void) { //注销设备 unregister_chrdev(device_major, PWM_NAME); //删除设备节点 device_destroy(pwm_class, MKDEV(device_major, 0)); //注销设备类 class_destroy(pwm_class); } module_init(pwm_init); module_exit(pwm_exit); MODULE_LICENSE("PGL"); MODULE_AUTHOR("Huang Gang"); MODULE_DESCRIPTION("my2440 pwm driver"); 4. 将 PWM 蜂鸣器驱动代码部署到内核中。

  tristate "My2440 PWM Beep Device" depends on ARCH_S3C2440 default y ---help---

  My2440 PWM Beep

  5.配置内核,选择 PWM 蜂鸣器设备选项 Character devices ---> <*> My2440 PWM Beep Device (NEW) 6. 编译内核并下载到开发板上。这里要注意,现在我们不需要手动的在开 发板上创建设备的节点了,因为我们现在使用了 mdev 进行管理了(使用方法请 看:设备文件系统剖析与使用),在驱动程序中也添加了对类设备接口的支持。 之前讲的一些驱动都没有,以后我们都使用这种方法。现在可以查看到/dev 目 录下自动创建好的 my2440_pwm 设备节点,就直接可以使用它了。 7. 编写 PWM 蜂鸣器驱动的测试程序。文件名:pwm_test.c ============================================== Name Author Date Copyright : pwm_test.c : Huang Gang : 25/11/2009 : GPL

  Description : my2440 pwm driver test ============================================== */ #include #include #include #include int main(int argc, char **argv) { int tmp;

  int fd; int i; //打开蜂鸣器设备 fd = open("/dev/my2440_pwm", O_RDWR); if(fd < 0) { printf("Open PWM Device Faild!\n"); exit(1); } //提示用户输入一个参数来对蜂鸣器进行调频,0 表示停止 工作 printf("please enter the times number(0 is stop):\n"); while(1) { //输入参数 scanf("%d", &tmp); printf("times = %d\n", tmp); //IO 控制 ioctl(fd, tmp); if(tmp <= 0) { break; } } //关闭设备

  close(fd); return 0; } 8. 在开发主机上交叉编译测试应用程序,并复制到文件系统的/usr/sbin 目 录下,然后重新编译文件系统下载到开发板上。 9. 在开发板上运行测试程序。可以看到根据你输入参数的大小,蜂鸣器也 会发生不同频率的叫声,输入 0 蜂鸣器停止鸣叫。

  来自: http://hi.baidu.com/shentuhongfeng/blog/item/cf7c0b36d76c22d0a2cc2b22.html

  转:基于 EasyARM2103 的交流蜂鸣器音乐播放设计 133331457 1.1 蜂鸣器简介 蜂鸣器是一种一体化结构的电子讯响器,采用直流或者交流供电,广泛应用于计算机、打印 机、复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中的发声器件。 蜂鸣器在电路中用字母“H”或“HA”(旧标准用“FM”、“LB”、“JD”等)表示。蜂鸣器的外观如 下图所示。 图 1.1 蜂鸣器 根据发声材料、 结构和驱动方式的不同, 蜂鸣器可以分为压电式、 电磁式等, 如表 1.1 所示。

  1.1.1 驱动电路分析与参数计算 根据上述的几个蜂鸣器驱动电路分析发现,蜂鸣器驱动电路无一例外都包含以下几个部分: 一个三极管、一个蜂鸣器、一个续流二极管和一个电源滤波电容。驱动电路如图 1.2 所示。

  图 1.2 蜂鸣器驱动电路 蜂鸣器驱动电路分析如下: 1.蜂鸣器 发声元件,在其两端施加直流电压(有源蜂鸣器)或者方波(无源蜂鸣器)就可以发声,其 主要参数是外形尺寸、发声方向、工作电压、工作频率、工作电流、驱动方式(直流/方波) 等。这些都可以根据需要来选择。 2.续流二极管 蜂鸣器本质上是一个感性元件,其电流不能瞬变,因此必须有一个续流二极管提供续流。否 则,在蜂鸣器两端会产生几十伏的尖峰电压,可能损坏驱动三极管,并干扰整个电路系统的 其它部分。 3.滤波电容 滤波电容C1 的作用是滤波, 滤除蜂鸣器电流对其它部分的影响, 也可改善电源的交流阻抗, 如果可能,*好是再并联一个 220uF 的电解电容。 4.三极管 三极管 Q1 起开关作用,其基极的高电平使三极管饱和导通,使蜂鸣器发声;而基极低电平 则使三极管关闭,蜂鸣器停止发声。 1.1.2 驱动程序设计 1. 直流蜂鸣器驱动程序 直流蜂鸣器的驱动是非常简单的, 只要在其两端施加额定工作电压, 蜂鸣器就发声。 NPN 以 三极管驱动电路为例,只要在三极管的基极接入高电平,蜂鸣器��能发声。例如:蜂鸣器每 秒钟发声 100mS 时,三极管基极的驱动波形如图 1.3 所示。

  图 1.3 NPN 管驱动直流蜂鸣器 2. 交流蜂鸣器驱动程序 交流蜂鸣器的驱动相对复杂一点, 要在蜂鸣器两端施加额定电压的方波。 蜂鸣器的工作频率 范围通常是很窄的, 这意味着一个蜂鸣器通常只能工作在其额定频率才会有良好的发声效果

  (包括声压和音色等)。有些蜂鸣器的工作频率范围是比较宽的,这样就可以通过调整驱动 方波的频率而使蜂鸣器发出音乐,演奏歌曲。例如:蜂鸣器每秒钟发声 100mS 时,三极管 基极的驱动波形如下图 1.4 所示。

  图 1.4 驱动交流蜂鸣器 1.2 设计原理 本实例采用 LPC2103 的定时器 1 产生 PWM 脉冲控制 8050 导通与闭合,使交流蜂鸣器两 端产生方波信号,驱动蜂鸣器发声。三个 LED 分别显示高、中和低音的状态。电路原理如 图 1.5 所示。

  图 1.5 原理图 1.3 电路制作 1.3.1 元器件选择

  本文电路制作中需要用到的元件如表 1.3 所列。

  表 1.3 元件列表 1.3.2 焊接 按照原理图连接电路,要注意蜂鸣器、三极管、二极管和 LED 的管脚极性。硬件电路焊接 完成后,实物如图 1.6 所示。

  图 1.6 实物图

  系统实物如图 1.7 所示。

  图 1.7 整体实物图 1.4 程序设计 本实例通过 LPC2103 的定时器 1 产生 PWM 脉冲来控制交流蜂鸣器发声。 根据不同的音频, LPC2103 产生不同频率的 PWM 脉冲, 使交流蜂鸣器发出不同频率的声音。 并通过三个 LED 分别显示高、中和低音的状态。以下为部分程序,详细程序参见程序源码。 蜂鸣器初始化程序:设置蜂鸣器控制引脚为 GPIO 输出低电平。蜂鸣器不发声。见程序清单 1.1。

  程序清单 1.1 蜂鸣器初始化程序 蜂鸣器指定频率发声程序: 根据指定频率设定定时器 1 的 PWM 的输出周期, 控制蜂鸣器发 出指定频率的声音。见程序清单 1.2。

  程序清单 1.2 蜂鸣器指定频率发声程序 蜂鸣器停止发声程序:复位定时器 1,设置蜂鸣器控制引脚为 GPIO 输出低电平,蜂鸣器停 止发声。见程序清单 1.3。

  程序清单 1.3 蜂鸣器停止发声程序 LED 初始化程序:分别设置 LED1、LED2 和 LED3 的控制引脚为 GPIO 输出,并熄灭。见 程序清单 1.4。

  程序清单 1.4 LED 初始化程序 点亮指定 LED 程序:根据入口参数点亮指定的 LED,见程序清单 1.5。

  程序清单 1.5 点亮指定 LED 程序

  熄灭指定 LED 程序:根据入口参数熄灭指定的 LED。见程序清单 1.6。

  程序清单 1.6 熄灭指定 LED 程序

  来自: http://hi.baidu.com/largeji/blog/item/c7cb683ee02386f0828b1351.html

  view plaincopy to clipboardprint? ·····10········20········30········40········50········60········70········80········90········100·······110·······12 ···· 0·······130·······140·······150 /**************************************************************** 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; 时器 0 分割值的掩*/

  /*S3C2410_TCFG1_MUX0_MASK 定 /*

  码: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 #include #include #include #include #include #include #include "beep.h" /*标准输入输出定义*/ /*POSIX 终端控制定义*/ /*Unix 标准函数定义*/ /*标准函数库定义*/

  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); 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); } 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"); }

  //打开蜂鸣器 //打开 pwm 设备驱动文件

  //打开错误, 则终止

  //退出回调 close_buzzer //关闭蜂鸣器

  //关闭

  //停止蜂鸣器

  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 < { 入的频率错误 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; } 本 文 来 自 CSDN 博 客 , 转 载 请 http://blog.csdn.net/zhengmeifu/archive/2009/12/14/5002900.aspx 标 明

  0) //如果输

  //

  //如果无法关

  //退出返

  出

  处

  :