单片机

您所在的位置:网站首页 醒图怎么调光 单片机

单片机

#单片机| 来源: 网络整理| 查看: 265

前言: 如果只用单片机做一个调光系统,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工作方式1 TH0 = 0x0FF; //设置时间为1us TL0 = 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 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; //每次接收到调光的命令后都要将它置0 len=strlen(receiveData); for(i=0;i


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3