1. 什么是PWM? PWM(脈沖寬度調(diào)制)簡單的講是一種變頻技術(shù)之一,是靠改變脈沖寬度來控制輸出電壓,通過改變周期來控制其輸出頻率。如果還不是很清楚,好吧,來看看我們實(shí)際生活中的例子,我們的電風(fēng)扇為什么扭一下按扭,風(fēng)扇的轉(zhuǎn)速就會(huì)發(fā)生變化;調(diào)一下收音機(jī)的聲音按鈕,聲音的大小就會(huì)發(fā)生變化;還有待會(huì)兒我們要講的蜂鳴器也會(huì)根據(jù)不同的輸入值而發(fā)出不同頻率的叫聲等等??!這些都是PWM的應(yīng)用,都是通過PWM輸出的頻率信號(hào)進(jìn)行控制的。
本文引用地址:http://m.butianyuan.cn/article/201611/318018.htm2. ARM Linux中的PWM
根據(jù)S3C2440的手冊(cè)介紹,S3C2440A內(nèi)部有5個(gè)16位的定時(shí)器,定時(shí)器0、1、2、3都帶有脈沖寬度調(diào)制功能(PWM),定時(shí)器4是一個(gè)沒有輸出引腳的內(nèi)部定時(shí)器,定時(shí)器0有一個(gè)用于大電流設(shè)備的死區(qū)生成器??聪聢D解釋吧?。?br />
由S3C2440的技術(shù)手冊(cè)和上面這幅結(jié)構(gòu)圖,我們來總結(jié)一下2440內(nèi)部定時(shí)器模塊的特性吧:
1)共5個(gè)16位的定時(shí)器,定時(shí)器0、1、2、3都帶有脈沖寬度調(diào)制功能(PWM);
2)每個(gè)定時(shí)器都有一個(gè)比較緩存寄存器(TCMPB)和一個(gè)計(jì)數(shù)緩存寄存器(TCNTB);
3)定時(shí)器0、1共享一個(gè)8位的預(yù)分頻器(預(yù)定標(biāo)器),定時(shí)器2、3、4共享另一個(gè)8位的預(yù)分頻器(預(yù)定標(biāo)器),其值范圍是0~255;
4)定時(shí)器0、1共享一個(gè)時(shí)鐘分頻器,定時(shí)器2、3、4共享另一個(gè)時(shí)鐘分頻器,這兩個(gè)時(shí)鐘分頻器都能產(chǎn)生5種不同的分頻信號(hào)值(即:1/2、1/4、1/8、1/16和TCLK);
5)兩個(gè)8位的預(yù)分頻器是可編程的且根據(jù)裝載的值來對(duì)PCLK進(jìn)行分頻,預(yù)分頻器和鐘分頻器的值分別存儲(chǔ)在定時(shí)器配置寄存器TCFG0和TCFG1中;
6)有一個(gè)TCON控制寄存器控制著所有定時(shí)器的屬性和狀態(tài),TCON的第0~7位控制著定時(shí)器0、第8~11位控制著定時(shí)器1、第12~15位控制著定時(shí)器2、第16~19位控制著定時(shí)器3、第20~22位控制著定時(shí)器4。
還是根據(jù)S3C2440手冊(cè)的描述和上圖的結(jié)構(gòu),要開始一個(gè)PWM定時(shí)器功能的步驟如下(假設(shè)使用的是第一個(gè)定時(shí)器):
1)分別設(shè)置定時(shí)器0的預(yù)分頻器值和時(shí)鐘分頻值,以供定時(shí)器0的比較緩存寄存器和計(jì)數(shù)緩存寄存器用;
2)設(shè)置比較緩存寄存器TCMPB0和計(jì)數(shù)緩存寄存器TCNTB0的初始值(即定時(shí)器0的輸出時(shí)鐘頻率);
3)關(guān)閉定時(shí)器0的死區(qū)生成器(設(shè)置TCON的第4位);
4)開啟定時(shí)器0的自動(dòng)重載(設(shè)置TCON的第3位);
5)關(guān)閉定時(shí)器0的反相器(設(shè)置TCON的第2位);
6)開啟定時(shí)器0的手動(dòng)更新TCNTB0&TCMPB0功能(設(shè)置TCON的第1位);
7)啟動(dòng)定時(shí)器0(設(shè)置TCON的第0位);
8)清除定時(shí)器0的手動(dòng)更新TCNTB0&TCMPB0功能(設(shè)置TCON的第1位)。
由此可以看到,PWM的輸出頻率跟比較緩存寄存器和計(jì)數(shù)緩存寄存器的取值有關(guān),而比較緩存寄存器和計(jì)數(shù)緩存寄存器的值又跟預(yù)分頻器和時(shí)鐘分頻器的值有關(guān);要使用PWM功能其實(shí)也就是對(duì)定時(shí)器的相關(guān)寄存器進(jìn)行操作。手冊(cè)上也有一個(gè)公式:定時(shí)器輸出頻率 = PCLK / {預(yù)分頻器值 + 1} / 時(shí)鐘分頻值。下面我們來通過一個(gè)蜂鳴器的實(shí)例來說明PWM功能的使用。
三、蜂鳴器驅(qū)動(dòng)實(shí)例
1. 蜂鳴器的種類和工作原理
蜂鳴器主要分為壓電式蜂鳴器和電磁式蜂鳴器兩種類型。
壓電式蜂鳴器主要由多諧振蕩器、壓電蜂鳴片、阻抗匹配器及共鳴箱、外殼等組成。有的壓電式蜂鳴器外殼上還裝有發(fā)光二極管。多諧振蕩器由晶體管或集成電路構(gòu)成。當(dāng)接通電源后(1.5~15V直流工作電壓),多諧振蕩器起振,輸出1.5~2.5kHZ的音頻信號(hào),阻抗匹配器推動(dòng)壓電蜂鳴片發(fā)聲。
電磁式蜂鳴器由振蕩器、電磁線圈、磁鐵、振動(dòng)膜片及外殼等組成。接通電源后,振蕩器產(chǎn)生的音頻信號(hào)電流通過電磁線圈,使電磁線圈產(chǎn)生磁場(chǎng)。振動(dòng)膜片在電磁線圈和磁鐵的相互作用下,周期性地振動(dòng)發(fā)聲。
有源蜂鳴器和無源蜂鳴器的區(qū)別:這個(gè)“源”字是不是指電源,而是指震蕩源,即有源蜂鳴器內(nèi)有振蕩源而無源蜂鳴器內(nèi)部沒有振蕩源。有振蕩源的通電就可以發(fā)聲,沒有振蕩源的需要脈沖信號(hào)驅(qū)動(dòng)才能發(fā)聲。
額外知識(shí):簡單蜂鳴器的制作方法
1)制備電磁鐵M:在長約6厘米的鐵螺栓上繞100圈導(dǎo)線,線端留下5厘米作引線,用透明膠布把線圈粘好,以免線圈松開,再用膠布把它粘在一個(gè)盒子上,電磁鐵就做好了;
2)制備彈片P:從鐵罐頭盒上剪下一條寬約2厘米的長鐵片,彎成直角,把電磁鐵的一條引線接在彈片上,再用膠布把彈片緊貼在木板上;
3)用曲別針做觸頭Q,用書把曲別針墊高,用膠布粘牢,引出一條導(dǎo)線,如圖連接好電路;
4)調(diào)節(jié)M與P之間的距離(通過移動(dòng)盒子),使電磁鐵能吸引彈片,調(diào)節(jié)觸點(diǎn)與彈片之間的距離,使它們能恰好接觸,通電后就可以聽到蜂鳴聲。
2. 開發(fā)板上蜂鳴器原理圖分析
由原理圖可以得知,蜂鳴器是通過GPB0 IO口使用PWM信號(hào)驅(qū)動(dòng)工作的,而GPB0口是一個(gè)復(fù)用的IO口,要使用它得先把他設(shè)置成TOUT0 PWM輸出模式。
3. 編寫合適開發(fā)板的蜂鳴器驅(qū)動(dòng)程序,文件名: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
#definePWM_MAJOR 0//主設(shè)備號(hào) #definePWM_NAME"my2440_pwm"http://設(shè)備名稱 staticintdevice_major=PWM_MAJOR;//系統(tǒng)動(dòng)態(tài)生成的主設(shè)備號(hào)
//打開設(shè)備 staticintpwm_open(structinode*inode,structfile*file) { //對(duì)GPB0復(fù)用口進(jìn)行復(fù)用功能設(shè)置,設(shè)置為TOUT0 PWM輸出 s3c2410_gpio_cfgpin(S3C2410_GPB0,S3C2410_GPB0_TOUT0);
return0; }
//關(guān)閉設(shè)備 staticintpwm_close(structinode*inode,structfile*file) { return0; }
//對(duì)設(shè)備進(jìn)行控制 staticintpwm_ioctl(structinode*inode,structfile*file,unsignedintcmd,unsignedlongarg) { if(cmd<=0)//如果輸入的參數(shù)小于或等于0的話,就讓蜂鳴器停止工作 { //這里又恢復(fù)GPB0口為IO口輸出功能,由原理圖可知直接給低電平可讓蜂鳴器停止工作 s3c2410_gpio_cfgpin(S3C2410_GPB0,S3C2410_GPB0_OUTP); s3c2410_gpio_setpin(S3C2410_GPB0,0); } else//如果輸入的參數(shù)大于0,就讓蜂鳴器開始工作,不同的參數(shù),蜂鳴器的頻率也不一樣 { //定義一些局部變量 unsignedlongtcon; unsignedlongtcnt; unsignedlongtcfg1; unsignedlongtcfg0;
structclk*clk_p; unsignedlongpclk;
//以下對(duì)各寄存器的操作結(jié)合上面講的開始一個(gè)PWM定時(shí)器的步驟和2440手冊(cè)PWM寄存器操作部分來看就比較容易理解 tcfg1=__raw_readl(S3C2410_TCFG1);//讀取定時(shí)器配置寄存器1的值 tcfg0=__raw_readl(S3C2410_TCFG0);//讀取定時(shí)器配置寄存器0的值
tcfg0&=~S3C2410_TCFG_PRESCALER0_MASK; tcfg0|=(50-1);//設(shè)置tcfg0的值為49
tcfg1&=~S3C2410_TCFG1_MUX0_MASK; tcfg1|=S3C2410_TCFG1_MUX0_DIV16;//設(shè)置tcfg1的值為0x0011即:1/16
__raw_writel(tcfg1,S3C2410_TCFG1);//將值tcfg1寫入定時(shí)器配置寄存器1中 __raw_writel(tcfg0,S3C2410_TCFG0);//將值tcfg0寫入定時(shí)器配置寄存器0中
clk_p=clk_get(NULL,"pclk"); pclk=clk_get_rate(clk_p);//從系統(tǒng)平臺(tái)時(shí)鐘隊(duì)列中獲取pclk的時(shí)鐘頻率,在include/linux/clk.h中定義 tcnt=(pclk/50/16)/cmd;//計(jì)算定時(shí)器0的輸出時(shí)鐘頻率(pclk/{prescaler0 + 1}/divider value)
__raw_writel(tcnt,S3C2410_TCNTB(0));//設(shè)置定時(shí)器0計(jì)數(shù)緩存寄存器的值 __raw_writel(tcnt/2,S3C2410_TCMPB(0));//設(shè)置定時(shí)器0比較緩存寄存器的值
tcon=__raw_readl(S3C2410_TCON);//讀取定時(shí)器控制寄存器的值
tcon&=~0x1f; tcon|=0xb;//關(guān)閉死區(qū)、自動(dòng)重載、關(guān)反相器、更新TCNTB0&TCMPB0、啟動(dòng)定時(shí)器0 __raw_writel(tcon,S3C2410_TCON);//設(shè)置定時(shí)器控制寄存器的0-4位,即對(duì)定時(shí)器0進(jìn)行控制
tcon&=~2; __raw_writel(tcon,S3C2410_TCON);//清除定時(shí)器0的手動(dòng)更新位 }
return0; }
//設(shè)備操作結(jié)構(gòu)體 staticstructfile_operations pwm_fops= { .owner=THIS_MODULE, .open=pwm_open, .release=pwm_close, .ioctl=pwm_ioctl, };
//定義一個(gè)設(shè)備類 staticstructclass*pwm_class;
staticint__init pwm_init(void) { //注冊(cè)為字符設(shè)備,主設(shè)備號(hào)為0讓系統(tǒng)自動(dòng)分配,設(shè)備名為my2440_pwm,注冊(cè)成功返回動(dòng)態(tài)生成的主設(shè)備號(hào) device_major=register_chrdev(PWM_MAJOR,PWM_NAME,&pwm_fops);
if(device_major<0) { printk(PWM_NAME" register falid!/n"); returndevice_major; }
//注冊(cè)一個(gè)設(shè)備類,使mdev可以在/dev/目錄下自動(dòng)建立設(shè)備節(jié)點(diǎn) pwm_class=class_create(THIS_MODULE,PWM_NAME);
if(IS_ERR(pwm_class)) { printk(PWM_NAME" register class falid!/n"); return-1; }
//創(chuàng)建一個(gè)設(shè)備節(jié)點(diǎn),設(shè)備名為PWM_NAME,即:my2440_pwm device_create(pwm_class,NULL,MKDEV(device_major,0),NULL,PWM_NAME);
return0; }
staticvoid__exit pwm_exit(void) { //注銷設(shè)備 unregister_chrdev(device_major,PWM_NAME);
//刪除設(shè)備節(jié)點(diǎn) device_destroy(pwm_class,MKDEV(device_major,0));
//注銷設(shè)備類 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蜂鳴器驅(qū)動(dòng)代碼部署到內(nèi)核中。
#cp -f my2440_pwm.c /linux-2.6.30.4/drivers/char //把驅(qū)動(dòng)源碼到內(nèi)核驅(qū)動(dòng)的字符設(shè)備下 |
#gedit /linux-2.6.30.4/drivers/char/Kconfig //添加PWM蜂鳴器設(shè)備配置 |
config MY2440_PWM_BEEP tristate"My2440 PWM Beep Device" dependsonARCH_S3C2440 default y ---help--- My2440 PWM Beep |
#gedit /linux-2.6.30.4/drivers/char/Makefile //添加PWM蜂鳴器設(shè)備配置 |
obj-$(CONFIG_MY2440_PWM_BEEP) +=my2440_pwm.o |
5.配置內(nèi)核,選擇PWM蜂鳴器設(shè)備選項(xiàng)
Device Drivers ---> Character devices ---> <*> My2440 PWM Beep Device (NEW) |
6. 編譯內(nèi)核并下載到開發(fā)板上。這里要注意,現(xiàn)在我們不需要手動(dòng)的在開發(fā)板上創(chuàng)建設(shè)備的節(jié)點(diǎn)了,因?yàn)槲覀儸F(xiàn)在使用了mdev進(jìn)行管理了(使用方法請(qǐng)看:設(shè)備文件系統(tǒng)剖析與使用),在驅(qū)動(dòng)程序中也添加了對(duì)類設(shè)備接口的支持。之前講的一些驅(qū)動(dòng)都沒有,以后我們都使用這種方法?,F(xiàn)在可以查看到/dev目錄下自動(dòng)創(chuàng)建好的my2440_pwm設(shè)備節(jié)點(diǎn),就直接可以使用它了。
7. 編寫PWM蜂鳴器驅(qū)動(dòng)的測(cè)試程序。文件名: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
intmain(intargc,char**argv) { inttmp; intfd; inti;
//打開蜂鳴器設(shè)備 fd=open("/dev/my2440_pwm",O_RDWR);
if(fd<0) { printf("Open PWM Device Faild!/n"); exit(1); }
//提示用戶輸入一個(gè)參數(shù)來對(duì)蜂鳴器進(jìn)行調(diào)頻,0表示停止工作 printf("please enter the times number(0 is stop):/n");
while(1) { //輸入?yún)?shù) scanf("%d",&tmp); printf("times = %d/n",tmp);
//IO控制 ioctl(fd,tmp);
if(tmp<=0) { break; } }
//關(guān)閉設(shè)備 close(fd);
return0; } |
8. 在開發(fā)主機(jī)上交叉編譯測(cè)試應(yīng)用程序,并到文件系統(tǒng)的/usr/sbin目錄下,然后重新編譯文件系統(tǒng)下載到開發(fā)板上。
#arm-linux-gcc -o pwm_test pwm_test.c |
9. 在開發(fā)板上運(yùn)行測(cè)試程序??梢钥吹礁鶕?jù)你輸入?yún)?shù)的大小,蜂鳴器也會(huì)發(fā)生不同頻率的叫聲,輸入0蜂鳴器停止鳴叫。
評(píng)論