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