1. 什么是PWM? PWM(脈沖寬度調制)簡單的講是一種變頻技術之一,是靠改變脈沖寬度來控制輸出電壓,通過改變周期來控制其輸出頻率。如果還不是很清楚,好吧,來看看我們實際生活中的例子,我們的電風扇為什么扭一下按扭,風扇的轉速就會發(fā)生變化;調一下收音機的聲音按鈕,聲音的大小就會發(fā)生變化;還有待會兒我們要講的蜂鳴器也會根據不同的輸入值而發(fā)出不同頻率的叫聲等等?。∵@些都是PWM的應用,都是通過PWM輸出的頻率信號進行控制的。
本文引用地址:http://www.biyoush.com/article/201611/318018.htm2. ARM Linux中的PWM
根據S3C2440的手冊介紹,S3C2440A內部有5個16位的定時器,定時器0、1、2、3都帶有脈沖寬度調制功能(PWM),定時器4是一個沒有輸出引腳的內部定時器,定時器0有一個用于大電流設備的死區(qū)生成器??聪聢D解釋吧??!

由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控制寄存器控制著所有定時器的屬性和狀態(tài),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的死區(qū)生成器(設置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. 蜂鳴器的種類和工作原理
蜂鳴器主要分為壓電式蜂鳴器和電磁式蜂鳴器兩種類型。
壓電式蜂鳴器主要由多諧振蕩器、壓電蜂鳴片、阻抗匹配器及共鳴箱、外殼等組成。有的壓電式蜂鳴器外殼上還裝有發(fā)光二極管。多諧振蕩器由晶體管或集成電路構成。當接通電源后(1.5~15V直流工作電壓),多諧振蕩器起振,輸出1.5~2.5kHZ的音頻信號,阻抗匹配器推動壓電蜂鳴片發(fā)聲。
電磁式蜂鳴器由振蕩器、電磁線圈、磁鐵、振動膜片及外殼等組成。接通電源后,振蕩器產生的音頻信號電流通過電磁線圈,使電磁線圈產生磁場。振動膜片在電磁線圈和磁鐵的相互作用下,周期性地振動發(fā)聲。
有源蜂鳴器和無源蜂鳴器的區(qū)別:這個“源”字是不是指電源,而是指震蕩源,即有源蜂鳴器內有振蕩源而無源蜂鳴器內部沒有振蕩源。有振蕩源的通電就可以發(fā)聲,沒有振蕩源的需要脈沖信號驅動才能發(fā)聲。
額外知識:簡單蜂鳴器的制作方法
1)制備電磁鐵M:在長約6厘米的鐵螺栓上繞100圈導線,線端留下5厘米作引線,用透明膠布把線圈粘好,以免線圈松開,再用膠布把它粘在一個盒子上,電磁鐵就做好了;
2)制備彈片P:從鐵罐頭盒上剪下一條寬約2厘米的長鐵片,彎成直角,把電磁鐵的一條引線接在彈片上,再用膠布把彈片緊貼在木板上;
3)用曲別針做觸頭Q,用書把曲別針墊高,用膠布粘牢,引出一條導線,如圖連接好電路;
4)調節(jié)M與P之間的距離(通過移動盒子),使電磁鐵能吸引彈片,調節(jié)觸點與彈片之間的距離,使它們能恰好接觸,通電后就可以聽到蜂鳴聲。
2. 開發(fā)板上蜂鳴器原理圖分析

由原理圖可以得知,蜂鳴器是通過GPB0 IO口使用PWM信號驅動工作的,而GPB0口是一個復用的IO口,要使用它得先把他設置成TOUT0 PWM輸出模式。
3. 編寫合適開發(fā)板的蜂鳴器驅動程序,文件名: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//主設備號 #definePWM_NAME"my2440_pwm"http://設備名稱 staticintdevice_major=PWM_MAJOR;//系統動態(tài)生成的主設備號
//打開設備 staticintpwm_open(structinode*inode,structfile*file) { //對GPB0復用口進行復用功能設置,設置為TOUT0 PWM輸出 s3c2410_gpio_cfgpin(S3C2410_GPB0,S3C2410_GPB0_TOUT0);
return0; }
//關閉設備 staticintpwm_close(structinode*inode,structfile*file) { return0; }
//對設備進行控制 staticintpwm_ioctl(structinode*inode,structfile*file,unsignedintcmd,unsignedlongarg) { if(cmd<=0)//如果輸入的參數小于或等于0的話,就讓蜂鳴器停止工作 { //這里又恢復GPB0口為IO口輸出功能,由原理圖可知直接給低電平可讓蜂鳴器停止工作 s3c2410_gpio_cfgpin(S3C2410_GPB0,S3C2410_GPB0_OUTP); s3c2410_gpio_setpin(S3C2410_GPB0,0); } else//如果輸入的參數大于0,就讓蜂鳴器開始工作,不同的參數,蜂鳴器的頻率也不一樣 { //定義一些局部變量 unsignedlongtcon; unsignedlongtcnt; unsignedlongtcfg1; unsignedlongtcfg0;
structclk*clk_p; unsignedlongpclk;
//以下對各寄存器的操作結合上面講的開始一個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;//關閉死區(qū)、自動重載、關反相器、更新TCNTB0&TCMPB0、啟動定時器0 __raw_writel(tcon,S3C2410_TCON);//設置定時器控制寄存器的0-4位,即對定時器0進行控制
tcon&=~2; __raw_writel(tcon,S3C2410_TCON);//清除定時器0的手動更新位 }
return0; }
//設備操作結構體 staticstructfile_operations pwm_fops= { .owner=THIS_MODULE, .open=pwm_open, .release=pwm_close, .ioctl=pwm_ioctl, };
//定義一個設備類 staticstructclass*pwm_class;
staticint__init pwm_init(void) { //注冊為字符設備,主設備號為0讓系統自動分配,設備名為my2440_pwm,注冊成功返回動態(tài)生成的主設備號 device_major=register_chrdev(PWM_MAJOR,PWM_NAME,&pwm_fops);
if(device_major<0) { printk(PWM_NAME" register falid!/n"); returndevice_major; }
//注冊一個設備類,使mdev可以在/dev/目錄下自動建立設備節(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)建一個設備節(jié)點,設備名為PWM_NAME,即:my2440_pwm device_create(pwm_class,NULL,MKDEV(device_major,0),NULL,PWM_NAME);
return0; }
staticvoid__exit pwm_exit(void) { //注銷設備 unregister_chrdev(device_major,PWM_NAME);
//刪除設備節(jié)點 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" dependsonARCH_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蜂鳴器設備選項
Device Drivers ---> Character devices ---> <*> My2440 PWM Beep Device (NEW) |
6. 編譯內核并下載到開發(fā)板上。這里要注意,現在我們不需要手動的在開發(fā)板上創(chuàng)建設備的節(jié)點了,因為我們現在使用了mdev進行管理了(使用方法請看:設備文件系統剖析與使用),在驅動程序中也添加了對類設備接口的支持。之前講的一些驅動都沒有,以后我們都使用這種方法?,F在可以查看到/dev目錄下自動創(chuàng)建好的my2440_pwm設備節(jié)點,就直接可以使用它了。
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
intmain(intargc,char**argv) { inttmp; intfd; inti;
//打開蜂鳴器設備 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);
return0; } |
8. 在開發(fā)主機上交叉編譯測試應用程序,并到文件系統的/usr/sbin目錄下,然后重新編譯文件系統下載到開發(fā)板上。
#arm-linux-gcc -o pwm_test pwm_test.c |
9. 在開發(fā)板上運行測試程序。可以看到根據你輸入參數的大小,蜂鳴器也會發(fā)生不同頻率的叫聲,輸入0蜂鳴器停止鳴叫。
評論