用STC15F104W单片机读取PWM信号控制IO口

本文主要介绍怎么通过遥控接收机的信号控制单片机的IO口。试验器材:

天地飞WFT09II 9通道2.4G遥控器+接收机

STC15F104W单片机


这里接收机输出的是频率为50hz,周期为20mspwm波形,通过单片机外部中断口接收pwm信号,启用定时器/计数器对pwm高电平的持续时间进行计数。

Arduino中的pulseIn函数可以直截了当地读取pwm的值,可以参考其底层的实现方法,这里有一个链接介绍pulseIn函数读取pwm的思想。

http://blog.csdn.net/soso90soso/article/details/78599621?locationNum=5&fps=1

文中关键部分:

unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout) {
  pinMode(pin, INPUT);
  uint32_t start = micros();
  while(digitalRead(pin) == state && (micros() - start) < timeout);
  while(digitalRead(pin) != state && (micros() - start) < timeout);
  start = micros();
  while(digitalRead(pin) == state && (micros() - start) < timeout);
  return micros() - start;
}

微妙级,死循环读取引脚电平。因为不确定IO口接收到输入的pwm的瞬间是高电平还是低电平,所以While循环里先判断高电平,再判断低电平,再判断一次高电平,从而获得准确的占空比。

至于怎么在单片机上读取pwm呢?在网上查找一些资料后,我了解到可以通过外部中断+定时器来实现。最初我参考这个源码:

//转发请注明:http://bbs.5imx.com
//芯片型号为STC15F100W-STC15F104W,烧写程序时请选择IRC频率12M.
//STC15F100W芯片的P3.2脚为模型接收机PWM信号输入脚.
//本程序可根据个人需求更改判断变量a的值来达到相应的要求.
//变量a的值单位为微秒:1000us=1ms,1500=1.5ms,2000=2.0ms.
#include
sfr AUXR = 0x8E;
unsigned int a=0;
sbit led1=P3^3;
sbit led2=P3^4;
sbit led3=P3^5;
bit HL;
                       
void Timer0Init(void)		
{
	AUXR &= 0x7F;		
	TMOD &= 0xF0;	
	TL0 = 0x00;		
	TH0 = 0x00;		
	TF0 = 0;	
	TR0 = 0;		
}

void main()
{   
    EA   = 1;
	INT0 = 1;
	EX0  = 1; 
	IT0  = 0;                    
    Timer0Init();
while(1){  
		 if(a>980&&a<1020)led2=0;  else led2=1;
		 if(a>1480&&a<1520)led3=0; else led3=1;
        }
}

void INT_0 (void) interrupt 0  using 2
{
   HL = INT0;
   if(HL==1)TR0 = 1;
   if(HL==0){
   TR0 = 0;
   a   = TH0;
   a   = a*256+TL0;
   TL0 = 0x00;
   TH0 = 0x00; 
              }
}
这个代码里面有一个问题就是,当单片机满足中断条件,就会不断跳到外部中断处理函数中,感觉这样太占用cpu资源,可以编写一个函数,在监听pwm值时开启外部中断,在获取到pwm值时关闭中断。具体方法如下:

#include
sfr AUXR = 0x8E;
unsigned int a0=0;
unsigned int a1=0;
//sbit led1=P3^3;
sbit led2=P3^4;
sbit led1=P3^5;
bit HL;
                   
void pulseIn0();
void pulseIn1();


void Timer0Init(void)
{
  AUXR &= 0x7F;
  TMOD &= 0xF0;
  TL0 = 0x00;
  TH0 = 0x00;
  TF0 = 0;
  TR0 = 0;
}


void main()
{   
  EA   = 1;
  //INT0 = 1;
  EX0  = 0; 
  IT0  = 0;  
  EX1  = 0; 
  IT1  = 0;
  Timer0Init();
  while(1){  
    pulseIn1();
    if(a0>980&&a0<1020)led2=0;  else led2=1;
    pulseIn0();
    if(a1>1480&&a1<1520)led1=0; else led1=1;
  }
}


void INT_0 (void) interrupt 0  using 2
{
   HL = INT0;
   if(HL==1)TR0 = 1;
   if(HL==0){
     TR0 = 0;
     a0   = TH0;
     a0   = a0*256+TL0;
     TL0 = 0x00;
     TH0 = 0x00; 
     EX0  = 0; 
   }
}
void INT_1 (void) interrupt 2  using 2
{
   HL = INT1;
   if(HL==1)TR0 = 1;
   if(HL==0){
     TR0 = 0;
     a1   = TH0;
     a1  = a1*256+TL0;
     TL0 = 0x00;
     TH0 = 0x00; 
     EX1  = 0; 
   }
}
void pulseIn0(){
  while(INT0==1);
  EX0  = 1; 
}
void pulseIn1(){
  while(INT1==1);
  EX1  = 1; 
}
STC15F104W单片机外部中断0(INT0)和外部中断1(INT1)触发有两种 发方式,上升沿或下降沿均可 发方式和仅下降沿 发方式。
TCON寄存器中的IT0/TCON.0和IT1/TCON.2决定了外部中断0和1是上升沿和下降沿均可 发还是仅下降沿 发。如果ITx = 0(x = 0,1),那么系统在INTx(x = 0,1)脚探测到上升沿或下降沿后均可产生外部中断。如果ITx = 1(x = 0,1),那么系统在INTx( x= 0,1)脚探测下降沿后才可产生外部中断。

程序中

IT0  = 0;  
IT1  = 0;

所以上升沿或下降沿后均可产生外部中断,我设想是通过上升沿触发中断,

void pulseIn0(){
  while(INT0==1);
  EX0  = 1; 
}
该段代码的意思是,如果接收到的pwm信号正处于高电平,先进入死循环,等pwm信号处于低电平再开启中断,那么下一个上升沿即可触发中断,触发中断后立即启用定时器0正好可以对高电平进行计数,到下一个下降沿时关闭中断。


试验效果如下:

INT1口接收到的pwm信号满足a1>1480&&a1<1520,led1熄灭。

而且用Arduino读取到的pwm值也在这个区间内,由此可见,用这种方法读取pwm的值精度是很高的,以后可以用STC15F104W单片机代替Arduino,通过pwm控制单片机的IO口。

用STC15F104W单片机读取PWM信号控制IO口_第1张图片

用STC15F104W单片机读取PWM信号控制IO口_第2张图片


你可能感兴趣的:(用STC15F104W单片机读取PWM信号控制IO口)