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