【嵌入式底层知识修炼】软件捕获PWM获取周期和占空比(+源代码)


遇到问题的时候,自己研究一段时间后仍不能解决,就要及早提问,少钻牛角尖

————小白


  相比软件模拟PWM而言,软件捕获PWM则十分不稳定,因为需要大量的计算过程,这些过程消耗的时间可能会拖延处理捕获点的到达,导致出现捕获点丢失的情况,除非能够保证数据运算在两个捕获点间隔内完成,否则结果常常是错误的。还是那句话,能用硬件就不用软件,所以慎用软件捕获PWM

01 - 软件捕获PWM思路

  软件捕获PWM的方法有多种,但是无论哪一种,基本思想都是提取高电平和低电平的时间进行计算,小白使用最简单的一种,一个定时器和一个可以边沿触发的外部中断,整体思路图如下:
Alt

  下面是具体步骤:

  1. 初始化外部中断En为边沿触发,初始化相应端口Pn
  2. 选定一个定时器Tn作计数器
  3. 第一次外部中断,记录端口当前电平flag,开启定时器Tn,定时器内进行计数,包含溢出
  4. 第二次外部中断,记录计数值S1Tn继续跑
  5. 第三次外部中断,再次记录计数值S2,一次捕获完成,置标志位Kn,重置Tn和相关变量
  6. 主进程监听Kn,标志位完成后对flagS1S2进行处理
  7. 回到步骤3

  当然,这些理想状态,即我们假设每一个捕获点都能被CPU处理,但实际并不是,如果PWM源的周期频率未知,那么这样做的结果几乎是错误的,如果能够知道PWM源的一些信息比如是ms级别还是us级别、周期范围等等,就比较好处理

02 - 核心源码

  作为例子,选用SINO的SH79M1612B(8051)进行具体配置

  定时器1

#define Fsys_Mhz			12.0
#define timer1_start()		TCON |= 0x40
#define timer1_stop()		TCON &= ~0x40
#define set_timer1_ms(x)	TL1 = ((uint16)((MAX_16BIT-(x)*1000*Fsys_Mhz)))&0x00ff; \
							TH1 = (((uint16)((MAX_16BIT-(x)*1000*Fsys_Mhz)))&0xff00)>>8;}
#define set_timer1_value(x)	TL1 = (x); TH1 = (x);}

volatile uint32 timer1_overflow = 0;

void timer1_init(void)
{
	TMOD |= 0x10;		
	TCON1 |= 0x08;
	
	set_timer1_ms(1);
	ET1 = 1;									//定时器1中断允许
	EA = 1;
}

void timer1_ISR(void) interrupt 3
{
	set_timer1_ms(1);
	timer1_overflow++;
}

  捕获PWM

#define set_port_in(x,y)	P##x##PCR |= (1UL<<(y));\
							P##x##CR  &= ~(1UL<<(y));
#define enable_ex()			IEN1 |= (1<<2)
#define disable_ex()		IEN1 &= ~(1<<2)

uint8 ex_counter = 0;
uint8 ex_high_flag = 0;
volatile uint16 ex_flag = 0;
volatile uint32 ex_sum1 = 0;
volatile uint32 ex_sum2 = 0;

void catch_pwm_init(void)
{
	timer1_init();
	set_port_in(1,2);	//P1.2
	EXF0 = ((3<<2));	//high and low
	enable_ex();
	EA = 1;
}

void ex_ISR(void) interrupt 9
{
	//wait for handle
	if(!ex_flag)
	{
		ex_counter++;
		
		//first, start timer
		if(1 == ex_counter)
		{
			timer1_start();
			ex_high_flag = P1_2;
		}
		
		//second, record sum1
		if(2 == ex_counter)
		{
			t = TH1;
			t = (t<<8) + TL1;
			ex_sum1 = t + (timer1_overflow&0xff)*0xffff;
			
		}
		
		//third, record sum2
		if(3 == ex_counter)
		{
			timer1_stop();
			disable_ex();
			
			t = TH1;
			t = (t<<8) + TL1;
			ex_sum2 = t + (timer1_overflow&0xff)*0xffff;
			
			//set
			ex_flag = 1;
			
			//ready next
			ex_counter = 0;
			timer1_overflow = 0;
			set_timer1_value(0);
			enable_ex();
		}		
	}
}

  主进程

uint32 period,rate;
catch_pwm_init();
while(1) 
{
	if(ex_flag)
	{
		//period(ms)
		period = ex_sum2/Fsys_Mhz/1000;
		
		//high/low rate
		if(ex_high_flag)
		{
			rate = ex_sum1*100/ex_sum2;
		}
		else
		{
			rate = (ex_sum2-ex_sum1)*100/ex_sum2;
		}
		
		//output
		printf("%u-%u\r\n",period,rate);	
		
		//ready next
		set_timer1_value(0);
		ex_flag = 0;
		enable_ex();
		}
	}

03 - 软件捕获PWM测试结果

  我们取【嵌入式底层知识修炼】软件捕获PWM获取周期和占空比的测试用例:

  • 软件模拟Period = 10ms,High = 4ms
    在这里插入图片描述
  • 软件模拟Period = 20ms,High = 14ms
    在这里插入图片描述
  • 软件模拟Period = 100ms,High = 56ms
    在这里插入图片描述
  • 软件模拟Period = 500ms,High = 123ms
    在这里插入图片描述
  • 软件模拟Period = 1000ms,High = 456ms
    在这里插入图片描述
  • 软件模拟Period = 2000ms,High = 1000ms
    在这里插入图片描述

  可以看到,软件捕获PWM得到的结果与PWM源存在比较大的差别,极其不稳定

04 - 源码链接

  整体Keil工程
  百度网盘 提取码:wxyy

05 - 总结

  • 软件捕获PWM极其不稳定,慎用
  • 若要改进,可以获取多次,然后求平均,或者使用算法改进
相关推荐
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页