PWM在ARM Linux中的原理和蜂鸣器驱动实例开发

发布者:SereneSoul55最新更新时间:2016-06-15 来源: eefocus关键字:PWM  ARM  Linux中  蜂鸣器驱动 手机看文章 扫描二维码
随时随地手机看文章
1. 什么是PWM?

   PWM(脉冲宽度调制)简单的讲是一种变频技术之一,是靠改变脉冲宽度来控制输出电压,通过改变周期来控制其输出频率。如果还不是很清楚,好吧,来看看我们实际生活中的例子,我们的电风扇为什么扭一下按扭,风扇的转速就会发生变化;调一下收音机的声音按钮,声音的大小就会发生变化;还有待会儿我们要讲的蜂鸣器也会根据不同的输入值而发出不同频率的叫声等等!!这些都是PWM的应用,都是通过PWM输出的频率信号进行控制的。

2. ARM Linux中的PWM

   根据S3C2440的手册介绍,S3C2440A内部有5个16位的定时器,定时器0、1、2、3都带有脉冲宽度调制功能(PWM),定时器4是一个没有输出引脚的内部定时器,定时器0有一个用于大电流设备的死区生成器。看下图解释吧!!
PWM在ARM Linux中的原理和蜂鸣器驱动实例开发


由S3C2440的技术手册和上面这幅结构图,我们来总结一下2440内部定时器模块的特性吧:
 
1)共5个16位的定时器,定时器0、1、2、3都带有脉冲宽度调制功能(PWM);
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. 蜂鸣器的种类和工作原理
    
   蜂鸣器主要分为压电式蜂鸣器和电磁式蜂鸣器两种类型。
 
   压电式蜂鸣器主要由多谐振荡器、压电蜂鸣片、阻抗匹配器及共鸣箱、外壳等组成。有的压电式蜂鸣器外壳上还装有发光二极管。多谐振荡器由晶体管或集成电路构成。当接通电源后(1.5~15V直流工作电压),多谐振荡器起振,输出1.5~2.5kHZ的音频信号,阻抗匹配器推动压电蜂鸣片发声。
 
   电磁式蜂鸣器由振荡器、电磁线圈、磁铁、振动膜片及外壳等组成。接通电源后,振荡器产生的音频信号电流通过电磁线圈,使电磁线圈产生磁场。振动膜片在电磁线圈和磁铁的相互作用下,周期性地振动发声。
 
   有源蜂鸣器和无源蜂鸣器的区别:这个“源”字是不是指电源,而是指震荡源,即有源蜂鸣器内有振荡源而无源蜂鸣器内部没有振荡源。有振荡源的通电就可以发声,没有振荡源的需要脉冲信号驱动才能发声。

   额外知识:简单蜂鸣器的制作方法
   1)制备电磁铁M:在长约6厘米的铁螺栓上绕100圈导线,线端留下5厘米作引线,用透明胶布把线圈粘好,以免线圈松开,再用胶布把它粘在一个盒子上,电磁铁就做好了;
   2)制备弹片P:从铁罐头盒上剪下一条宽约2厘米的长铁片,弯成直角,把电磁铁的一条引线接在弹片上,再用胶布把弹片紧贴在木板上;
   3)用曲别针做触头Q,用书把曲别针垫高,用胶布粘牢,引出一条导线,如图连接好电路;
   4)调节M与P之间的距离(通过移动盒子),使电磁铁能吸引弹片,调节触点与弹片之间的距离,使它们能恰好接触,通电后就可以听到蜂鸣声。

2. 开发板上蜂鸣器原理图分析
 PWM在ARM Linux中的原理和蜂鸣器驱动实例开发

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

/*
 ================================================
 Name        : my2440_pwm.c
 Author      : Huang Gang
 Date        : 25/11/09
 Copyright   : 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;      //设置tcfg1的值为0x0011即:1/16

        __raw_writel(tcfg1, S3C2410_TCFG1);     //将值tcfg1写入定时器配置寄存器1中
        __raw_writel(tcfg0, S3C2410_TCFG0);     //将值tcfg0写入定时器配置寄存器0中

        clk_p = clk_get(NULL, "pclk"); 
        pclk = clk_get_rate(clk_p);   //从系统平台时钟队列中获取pclk的时钟频率,在include/linux/clk.h中定义
        tcnt = (pclk/50/16)/cmd;      //计算定时器0的输出时钟频率(pclk/{prescaler0 + 1}/divider value)

        __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   = THIS_MODULE,
    .open    = pwm_open,
    .release = pwm_close,
    .ioctl   = pwm_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_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蜂鸣器驱动代码部署到内核中。
 

#cp -f my2440_pwm.c /linux-2.6.30.4/drivers/char //把驱动源码到内核驱动的字符设备下

 

#gedit /linux-2.6.30.4/drivers/char/Kconfig //添加PWM蜂鸣器设备配置

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

 

#gedit /linux-2.6.30.4/drivers/char/Makefile //添加PWM蜂鸣器设备配置

obj-$(CONFIG_MY2440_PWM_BEEP) += my2440_pwm.o


5.配置内核,选择PWM蜂鸣器设备选项

#make menuconfig

Device Drivers --->
    Character devices ---> 
        <*> My2440 PWM Beep Device (NEW)


6. 编译内核并下载到开发板上。这里要注意,现在我们不需要手动的在开发板上创建设备的节点了,因为我们现在使用了mdev进行管理了(使用方法请看:设备文件系统剖析与使用),在驱动程序中也添加了对类设备接口的支持。之前讲的一些驱动都没有,以后我们都使用这种方法。现在可以查看到/dev目录下自动创建好的my2440_pwm设备节点,就直接可以使用它了。

7. 编写PWM蜂鸣器驱动的测试程序。文件名:pwm_test.c

/*
 ==============================================
 Name        : pwm_test.c
 Author      : Huang Gang
 Date        : 25/11/2009
 Copyright   : 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目录下,然后重新编译文件系统下载到开发板上。

#arm-linux-gcc -o pwm_test pwm_test.c


9. 在开发板上运行测试程序。可以看到根据你输入参数的大小,蜂鸣器也会发生不同频率的叫声,输入0蜂鸣器停止鸣叫。

关键字:PWM  ARM  Linux中  蜂鸣器驱动 引用地址:PWM在ARM Linux中的原理和蜂鸣器驱动实例开发

上一篇:uboot 在 ARM s3c2410 上移植过程
下一篇:zc301摄像头驱动以及在S3C2410中使用serfox和spcaview

推荐阅读最新更新时间:2024-03-16 14:57

ARM usb camera test 【usb摄像头视频图像采集】
luvcview是一个开源项目,专注于UVC摄像头的测试,只要您的摄像头支持UVC驱动,即可使用luvcview测试程序,如何知道自己的摄像头是不是支持UVC驱动呢?在这个网站上查一下,看看 自己摄像头的ID是不是在支持的列表中,http://www.ideasonboard.org/uvc/ 我的摄像头正好支持 。 接下来在源码中编译。 编译成功。但是在测试执行时出现  invalid instruction . 经过一翻尝试,晕死,原来是编译器版本问题 。 正确的版本是arm-linux-gcc 4.3.2版本。 接下来执行,又出现如下问题: unable to set format 22. 这就是
[单片机]
<font color='red'>ARM</font> usb camera test 【usb摄像头视频图像采集】
对51转到ARM的新人的一些建议
我以前一直用的是51,不过一直是C51,对C已经有10多年的经验,汇编用的很少。后来因为项目需要转到了arm。一开始对arm什么都不懂,看了本《arm体系结构与编程》也是云里雾里的。但是也许是因为无知者无畏吧,我直接就在mdk中建立一个工程,添加了自带的启动文件,然后做了个main函数,里面一个死循环没有操作任何硬件,居然跑起来了。然后以此为基础,慢慢的开始控制GPIO和串口通信,当时突然发现,我的项目不就是搞搞这些吗?其他的什么运行模式、什么链接寄存器、什么PC寄存器关我鸟事,我只要能控制外设就行了。至于它是怎么实现的,那是编译器和链接器的事情。 现在回头想想开发arm还是很简单的,特别是使用mdk的话,会c也就能做一些简单的
[单片机]
ARM映像文件及其地址映射
1、什么是arm的映像文件, arm映像文件其实就是可执行文件,包括bin或hex两种格式,可以直接烧到ROM里执行。在axd调试过程中,我们调试的是axf文件,其实这也是一种映像文件,它只是在bin文件中加了一个文件头和一些调试信息。 映像文件一般由域组成,域最多由三个输出段组成(RO,RW,ZI),输出段又由输入段组成。所谓域,指的就是整个bin映像文件所处在的区域,它又分为加载域和运行域。对于嵌入式系统而言,程序映象都是存储在Flash存储器等一些非易失性器件中的,而在运行时,程序中的RW段必须重新装载到可读写的RAM中。简单来说,程序的加载时域就是指程序烧入Flash中的状态,运行时域是指程序执行时的状态。一般来说flas
[单片机]
ATMEGA16单片机Timer1的OC1A脚输出占空比可调的PWM信号
采用快速PWM方式,通过按键设置OCR1A的值,从而改变占空比; 当数值超出界限时,以了LED和LCD的方式报警; 输出经过滤波可以得到直流信号,改变占空比,输出不一样。 仿真原理图如下 单片机源程序如下: #define MAIN_C #include includes.h /***************************/ /*PWM*/ /*晶振为4MHz*/ /*利用Timer1的OC1A脚输出占空比可调的信号*/ /*通过按键控制OCR1A的值*/ /***************************/ #define PwmOut PD5 //A通道的PWM输出 #define
[单片机]
ATMEGA16单片机Timer1的OC1A脚输出占空比可调的<font color='red'>PWM</font>信号
ARM的串口基本操作函数测试(基于LPC2134)
/**************************************************************************** # * 文件名称:UART0.h # * 功能:声明关于UART0的相关全局函数 # * 版本:1.0 # * 作者:jianqi2010 # ****************************************************************************/ void UART_Init(void); void UART_Send_Byte(char ch); void UART_Send_Stri
[单片机]
基于ARM的数字调压控制系统设计详解
内容摘要:随着电子信息技术和半导体技术的深入发展,嵌入式系统的应用日趋广泛,在控制领域之中更多的使用了高性能微处理器,以满足各方面越来越多的控制应用需求。基于ARM嵌入式平台的数字调压控制系统,克服了传统上以旋钮或滑变式变阻器对交流电压进行模拟控制的弊端。本系统以嵌入式技术为基础,在嵌入式平台上利用ARM微处理器实时控制数模信号的转换,以控制正弦波调压模块对交流电压的大小调节。本文中通过对本系统的实际测试,验证了数字调压控制系统的功能特性,并且定量测试得出了本系统可以实现对交流电压进行线性调节的结论。数字调压控制系统可作为对电压的智能调节装置应用于家庭、医疗及工业自动化等领域,并且具有调节精度高、调节线性度好,易于操作等特性
[单片机]
基于<font color='red'>ARM</font>的数字调压控制系统设计详解
利用ARM LPC1112设计LED电子胸牌
引言     随着LED技术的空前繁荣,LED胸牌备受关注。LED胸牌也叫LED工作牌,是一款新颖的LED贴片点阵“名片”式显示屏,可以显示姓名、性别、工作部门等信息,还可以显示不同速度、不同动作方式的文本及预置图型。它可以吊挂在脖子上或者夹在衣服上,作为公共场所个人及企业身份的证明及宣传之用。LED胸牌既可以显示中文,也可以显示英文。对于那些需要显示的信息量不是很大,分辨率不是很高,制造成本较低的场合,使用小屏幕LED点阵显示器比较适用。电源大多采用锂电池,锂电池具有使用寿命长、能量较高、重量轻和高低温适应能力强的优点。     目前,大多数LED胸牌的点阵显示系统自带字库,显示效果主要靠硬件扫描来驱动,该方法比较简单,但是显示
[单片机]
利用<font color='red'>ARM</font> LPC1112设计LED电子胸牌
美国国家半导体推出高度集成的100V半桥式PWM控制器
美国国家半导体公司日前推出一款据称是业界最高度集成的100V半桥式脉冲宽度调制(PWM)控制器。厂商只要采用这款控制器,便可将半桥式直流/直流转换器的体积缩至最小。 这款LM5035芯片是美国国家半导体LM5000高电压控制器系列的最新型号,其特点是除了内置电压模式的PWM控制器之外,还另外设有高端及低端的2A峰值电流门驱动器以及两个可设定的次级线圈同步整流器场效应晶体管(FET)驱动器。由于整流开关功能可以通过设定加以控制,因此工程师可以改善定时的准确性,以提高系统的功率转换效率。采用LM5035芯片的半桥式电源供应器适用于多种不同的电子系统,其中包括电信系统、数据通信设备以及功率密度及效率要求同样严格的工业控制系统。 美国
[新品]
热门资源推荐
热门放大器推荐
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
  • 学习ARM开发(16)
    ARM有很多东西要学习,那么中断,就肯定是需要学习的东西。自从CPU引入中断以来,才真正地进入多任务系统工作,并且大大提高了工作效率。采 ...
  • 学习ARM开发(17)
    因为嵌入式系统里全部要使用中断的,那么我的S3C44B0怎么样中断流程呢?那我就需要了解整个流程了。要深入了解,最好的方法,就是去写程序 ...
  • 学习ARM开发(18)
    上一次已经了解ARM的中断处理过程,并且可以设置中断函数,那么它这样就可以工作了吗?答案是否定的。因为S3C44B0还有好几个寄存器是控制中 ...
  • 嵌入式系统调试仿真工具
    嵌入式硬件系统设计出来后就要进行调试,不管是硬件调试还是软件调试或者程序固化,都需要用到调试仿真工具。 随着处理器新品种、新 ...
  • 最近困扰在心中的一个小疑问终于解惑了~~
    最近在驱动方面一直在概念上不能很好的理解 有时候结合别人写的一点usb的例子能有点感觉,但是因为arm体系里面没有像单片机那样直接讲解引脚 ...
  • 学习ARM开发(1)
  • 学习ARM开发(2)
  • 学习ARM开发(4)
  • 学习ARM开发(6)
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved