【嵌入式底层知识修炼】软件捕获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极其不稳定,慎用
  • 若要改进,可以获取多次,然后求平均,或者使用算法改进
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AM5728平台的定时器可以设置为输入捕获模式,用来捕获PWM信号的频率和占空比。 具体步骤如下: 1. 配置GPIO引脚为定时器输入捕获模式。 2. 配置定时器为输入捕获模式,并设置计数器的溢出时间。 3. 在定时器触发中断中读取捕获寄存器的值,计算出PWM信号的频率和占空比。 以下是一个简单的代码示例: ```c #include "am5728.h" #define TIMER_BASEADDR 0x48040000 // 定时器基地址 #define PWM_INPUT_PIN 10 // PWM输入引脚 #define TIMER_PERIOD 100000000 // 定时器周期,单位为ns volatile uint32_t pwm_freq, pwm_duty; // PWM频率和占空比 void timer_isr() { static uint32_t last_cnt = 0; uint32_t curr_cnt = readl(TIMER_BASEADDR + 0x24); // 读取捕获寄存器的值 uint32_t diff_cnt = curr_cnt - last_cnt; pwm_freq = TIMER_PERIOD / diff_cnt; // 计算PWM频率 pwm_duty = (diff_cnt * 100) / TIMER_PERIOD; // 计算PWM占空比 last_cnt = curr_cnt; } int main() { // 配置GPIO引脚为定时器输入捕获模式 writel(0x6, AM5728_GPIO1_BASEADDR + 0x34); // 配置引脚为输入模式 writel(0x1, AM5728_GPIO1_BASEADDR + 0x7c); // 配置引脚为定时器输入模式 // 配置定时器为输入捕获模式 writel(0x0, TIMER_BASEADDR + 0x10); // 关闭定时器 writel(0x3, TIMER_BASEADDR + 0x38); // 配置为输入捕获模式 writel(0xffffffff, TIMER_BASEADDR + 0x28); // 设置计数器初始值为最大值 writel(TIMER_PERIOD, TIMER_BASEADDR + 0x2c); // 设置定时器周期 writel(0x1, TIMER_BASEADDR + 0x10); // 启动定时器 // 注册定时器中断处理函数 register_isr(61, timer_isr); // 启用定时器中断 enable_interrupt(61); while (1); return 0; } ``` 在定时器触发中断中,我们读取了捕获寄存器的值,并计算出了PWM信号的频率和占空比。注意,由于计数器的溢出时间是固定的,所以我们可以用定时器的周期来计算PWM信号的频率和占空比。同时,由于计数器的值可能会溢出,所以我们需要在计算PWM频率和占空比时要考虑到这一点。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值