单片机——PWM调光工作原理
前言:
如果只用單片機做一個調光系統,pwm是可以實現的,但是如果有其它的功能(比如傳感器要檢測,顯示屏顯示數據等等功能)就不推薦了。其它函數一多,定時器的時間又比較短,以至于單片機大多數時間都用在定時中斷函數里去了,處理其它函數的時間太少,其它函數執行太慢,效果不佳。如果定時器的時間太大,其它功能倒是有時間執行了,但是小燈的閃爍就會太嚴重,效果不佳。
目錄
- PWM工作原理
- 1、PWM簡介
- 2、工作原理
- 3、代碼實現
- 1)定時器代碼實現
- 2)串口部分代碼實現:
PWM工作原理
1、PWM簡介
PWM(pulse width modulation)是脈沖寬度調制的縮寫,利用微處理器的數字輸出來對模擬電路進行控制的一種技術。應用領域包括:功率控制與變換,電動機控制,伺服控制,調光等等。
2、工作原理
pwm調光是通過調節占空比的方式調節燈光亮度的。它先要設定一個周期(比如10ms),然后在這個周期內調節高電平和低電平的時間。比如:
1、10ms內全是高電平,這個燈就是最亮;
2、如果5ms是高電平,5ms是低電平,這樣其實是燈亮5ms,然后熄滅5ms,燈是在不斷的閃爍的,但是由于燈頻閃太快,人肉眼是分辨不過來,所以最后人觀測燈的效果就是亮度減半了;
3、如果10ms內全是低電平,這個燈就是熄滅的狀態。
如下圖:所以我們可以設定一個臨界值c,只需要調節這個c就可以調節周期內高低電平的時間了。
3、代碼實現
先說一下最終實現的樣子吧:
通過在串口中發送調光命令(數字+#的組合方式,數字范圍0-100),比如我要調節燈的來高度為50,就通過串口發送“50#”的命令,然后燈的亮度就是最高亮度的一半,我通過串口發送“100#“,燈的亮度就是最亮。
所以接下來依次實現所有思路:
先引入一個小疑問:
有人問為什么我們要用定時器呢,這里用定時器來固定周期長度,用定時器確定時間,相對延時函數來說要準確些,當然不用定時器也能實現PWM調光,用延時函數實現的方法大致如下:
//這里周期為10us int c=1; //亮度為最亮的1/10 while(1) {led=1; delay_us(c);led=0;delay_us(10-c); }下面還是言歸正傳,回到這次話題上
1)定時器代碼實現
1、先對定時器進行初始化 void InitTimer0(void) {TMOD |= 0x01; //設置定時器0工作方式1TH0 = 0x0FF; //設置時間為1usTL0 = 0x0FF;EA = 1; //開總中斷ET0 = 1; //開定時器0中斷TR0 = 1; //啟動定時器0,開始計數 }初始化中有一個小細節: 這里選擇了定時器0,后面串口也要用到定時器,所以串口只能選擇定時器1了,兩邊都會對TMOD這個寄存器初始化,所以對其賦值為了不影響另一個定時器的配置,我們這里沒有采用TMOD = 0x01;的方式,而是采用TMOD |= 0x01;的方式。這樣就可以防止影響高四位了(因為高四位對應定時器1)。
2、再寫定時器中斷函數代碼 int time0Flag; //time0Flag用于計數,判斷當前多少us了 int num1; //num1就是上圖中的C,通過調節它,從而調節高低電平的占空比void Timer0Interrupt(void) interrupt 1 {TH0 = 0x0FF; //重新賦予計數初值,方便下次計數TL0 = 0x0FF;time0Flag++; //記錄計數時間為多少us,if(time0Flag>100) //PWM占空比周期為100us,超過周期變量賦值為0,變為下一周期time0Flag=0;/*你會不會產生疑問?為什么把下面的這個判斷功能放在定時器中斷函數里,為什么不放在主函數里。放在主函數里沒有放在這里執行的次數多,執行的次數多,pwm調光時燈的頻閃就越小,看起越舒服。還好主函數里沒有什么執行函數,你可以在主函數的while循環里執行一些其它函數,當函數多起來的時候,單品即的時間都基本用在處理定時中斷函數去了,主函數里的函數就會得不到及時處理。把下面的功能放在主函數里和其它函數一起不斷執行呢,你可以試一下,我試過了,哪個屏閃哦,沒得說,燈簡直不要太閃。那有沒有既有效可以解決pwm調光的頻閃,同時又可以在主函數里運行很多其它函數,目前我還沒有找到解決辦法,想來想去,可能唯一的辦法就是多cpu并行執行的多線程了,pwm調光單獨開一個線程,但單片機不允許呀,哈哈。所以就這樣搞著玩一下吧。*/if(time0Flag<num1)led=0; //led為單片機連接的燈的引腳elseled=1; }每次進入定時器,都判斷當前時間是否達到num1,沒有就讓燈亮,達到了就讓燈滅
PWM的周期要盡可能的小!,這樣調節占空比,人肉眼才難發覺
假如你周期設為10s,然后調節占空比,實現高電平5s,低電平5s,然后不斷這樣亮滅亮滅。這個頻率,你能接受?這樣就不是調光了,就是閃光燈了。所以周期越小,然后調節高低電平時間,這樣燈就會閃得越快,人肉眼就越難看出燈在閃爍,最終呈現的就是燈的亮度變化了
2)串口部分代碼實現:
1、使用串口我們就要先對串口進行一系列的初始化,先列出串口的初始化代碼 void UartInit() {SCON=0X50; //設置串口為工作方式1TMOD|=0X20; //設置計數器1工作方式2//PCON=0X80; //波特率加倍TH1=0xfd; //計數器初始值設置,注意波特率是9600的TL1=0xfd;IP =0x10; //將串口中斷設置為高優先級,等同于語句于:PS=1;ES=1; //打開接收中斷EA=1; //打開總中斷TR1=1; //打開計數器 }注意:IP寄存器平時我們很少用到,這里這條語句”IP =0x10;“不能少,去掉后串口中斷不能正常工作,因為串口中斷的優先級比定時器0的優先級低,因而在你想要從串口中接收、發送數據時,往往得不到處理,因為都處理定時中斷函數去了,如果沒有這條語句,加之你定時中斷的時間很短的話,那么很有可能不會進入串口中斷,每次都會優先執行定時中斷0。所以我們人為的將串口中斷的優先級設置為高優先級,讓串口中斷優先處理。那會不會有人問,這樣的話那不就換成定時中斷函數得不到處理了?不會的,因為從串口中接收、發送數據不可能每時每刻都在進行,用到它的時候很少,它沒有中斷的大部分時間,都可以去處理定時中斷函數了。
2、然后就是串口中斷函數的實現 //串口發送字符 void SendChar(char Char) {SBUF=Char;while(!TI);TI=0; } //串口發送字符串 void SendString(char *p) {while(*p!='\0'){SendChar(*p);p++;} }int uartFlag=0; //全局變量,接收串口命令中數組內的標號 char receiveData[8]={'\0'}; //全局變量,用于接收串口數據,表示命令最多有*個字符,對方的控制命令以#號結束//串口中斷函數 void Uart() interrupt 4 {if(RI) //如果是串口接收到一幀數據,就會產生中斷,RI標志變為1{char sf,i,len;RI = 0; //手動將標志置0,方便下次判斷sf=SBUF;if(sf!='#') //對方發送的命令都以'#'作為結束符,如果本次接受的字符不是'#',則保存命令中的字符到數組 {receiveData[uartFlag++]=sf; //保存緩存中的數據}else //表示接收到一條命令了{ receiveData[uartFlag]='\0'; //命令最后加上'\0',便于字符串比較(strcmp函數)uartFlag=0; //表示本次數據接收完畢,置0,便于接收下條命令SendString(receiveData);num1=0; //每次接收到調光的命令后都要將它置0len=strlen(receiveData); for(i=0;i<len;i++) //將命令中的字符數組,解析、組合成對應的數字,方便定時器中斷函數中進行比較{num1+=((receiveData[i]-'0') * pow(10,len-1-i));}}} }結束了!!,上面的代碼就是所有,看了實在還是不會,下面放上完整的項目代碼:
【[完整代碼地址]】(https://download.csdn.net/download/qq_41873236/18303870)
總結
以上是生活随笔為你收集整理的单片机——PWM调光工作原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 简历中的“自我评价“怎么写?记录一个满分
- 下一篇: LYNC2010两台虚机部署笔记2