【嵌入式底层知识修炼】软件模拟周期占空比可调的PWM(+源代码)


任何事情都不应满足只会使用,而是要求懂得原理。

————小白


  需要软件模拟PWM的理由,一般是硬件PWM的IO端口不够用,因为软件模拟PWM不稳定,准确性也比较差,所以在硬件PWM可用的情况下是不会使用软件模拟的,但无论怎样,软件模拟的作用也很大,可以作为测试用例、通用PWM、替补方案等等

01 - 软件模拟思路

  软件模拟可调占空比思路图如下:
Alt
  程序整体的过程如下:

  1. 选一个普通IO端口作为PWM输出口
  2. 选一个定时器产生时基Tick,同时有一个全局计数Counter
  3. 在每个Tick内进行计数,当计数值达到要求时,IO端口作相应输出

  假设现在需要周期20ms,占空比为30%的PWM,也就是需要输出高电平14ms,低电平6ms,并周期产生,于是详细步骤如下:

  1. 计算周期和高低电平所占时间,Period = 20ms,High = 14ms,Low = 6ms
  2. 初始化普通IO如P3.5为输出端口
  3. 初始化定时器,产生1ms的时基Tick
  4. 启动定时器,在中断服务函数ISR中进行Counter计数
  5. ISR或主进程中对比High / LowCounter,一旦Counter >= High / Low就让P3.5输出1 / 0,其余时间输出0 / 1Counter > Period后就让Counter清0

  如果程序需要的是us级别,那么就作相应的修改即可

02 - 核心源码

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

/*define----------------------------------------------------------*/
#define Fsys_Mhz				12.0
#define set_port_out(x,y)		P##x##PCR &= ~(1UL<<(y));\
								P##x##CR  |= (1UL<<(y));
#define timer0_set_ms(x)		TL0 = ((uint16)(65536-x*1000*Fsys_Mhz))%256;\
								TH0 = ((uint16)(65536-x*1000*Fsys_Mhz))/256;
#define timer0_start()			TCON |= 0x10
#define timer0_stop()			TCON &= ~0x10

#define bitSet(value,x)			((value) |= (1UL<<(x)))
#define bitReset(value,x)		((value) &= ~(1UL<<(x)))
#define pwm_start				timer0_start
#define pwm_stop				timer0_stop

/*variable---------------------------------------------------------*/
uint16 timer0_counter = 0;
uint16 timer0_pwm_period = 0;
uint16 timer0_pwm_high_rate = 0;

/*function---------------------------------------------------------*/
void timer0_init(void)
{
	TMOD |= 0x01;		
	TCON1 |= 0x04;		
	timer0_set_ms(1);
	
	ET0 = 1;
	EA = 1;
}

void pwm_init(uint16 period, uint16 high_rate)
{
	//set PWM output P4.2
	set_port_out(4,2);	
	
	//Period and High-Rate
	timer0_pwm_period = period;
	timer0_pwm_high_rate = period*high_rate/100;
	
	timer0_init();
}

void timer0_ISR(void) interrupt 1
{
	//The default time base of 1ms is generated
	timer0_stop();
	timer0_set_ms(1);
	
	//output
	if(++timer0_counter <= timer0_pwm_high_rate) {
		bitSet(P4,2);
	}
	else if(timer0_counter > timer0_pwm_period) {
		timer0_counter = 0;
	} else {
		bitReset(P4,2);
	}
	
	timer0_start();
}

  最后,在main中调用pwm_init(),并在需要启动PWM的地方调用pwm_start()即可开启PWM,随后在指定端口上将会持续输出,如果不需要PWM,则调用pwm_stop()

int main(void)
{
	……
	pwm_init(200,30);
	……
}
……
{
	……
	pwn_start();
	……
}
……
{
	……
	pwn_stop();
	……
}

03 - 软件模拟测试结果

  使用示波器抓取波形,然后查看波形信息,小白测试了几组数据:

  • Period = 10ms,High = 4ms, Duty = 40%
    在这里插入图片描述
  • Period = 20ms,High = 14ms, Duty = 70%
    在这里插入图片描述
  • Period = 100ms,High = 56ms, Duty = 56%
    在这里插入图片描述
  • Period = 500ms,High = 123ms, Duty =24.6%
    在这里插入图片描述
  • Period = 1000ms,High = 456ms, Duty = 45.6%
    在这里插入图片描述
  • Period = 2000ms,High = 1000ms, Duty = 50%
    在这里插入图片描述

  能够看到,软件模拟PWM是不稳定不准确的,原因有若干:

  • 周期与占空比不能进行整数运算,得不到准确的占空比,于是得不到准确的数据
  • 软件翻转IO不是瞬间的,而是需要一个微小的时间,这个时间将影响整体效果
  • Tick如果越短,中断频率就越高,加上时钟源的不稳定,整体时间将会偏移很大
  • ……

04 - 源码链接

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

05 - 总结

  • 软件模拟PWM具有不准确、不稳定的特性
  • 定时器时基Tick越长,定时越准,产生的PWM也就越准确
  • 周期和占空比最好能够进行整数运算,否则PWM依然不准确
  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值