任何事情都不应满足只会使用,而是要求懂得原理。
————小白
需要软件模拟PWM
的理由,一般是硬件PWM
的IO端口不够用,因为软件模拟PWM
不稳定,准确性也比较差,所以在硬件PWM
可用的情况下是不会使用软件模拟的,但无论怎样,软件模拟的作用也很大,可以作为测试用例、通用PWM、替补方案等等
01 - 软件模拟思路
软件模拟可调占空比思路图如下:
程序整体的过程如下:
- 选一个普通IO端口作为
PWM
输出口 - 选一个定时器产生时基
Tick
,同时有一个全局计数Counter
- 在每个
Tick
内进行计数,当计数值达到要求时,IO端口作相应输出
假设现在需要周期20ms,占空比为30%的PWM
,也就是需要输出高电平14ms,低电平6ms,并周期产生,于是详细步骤如下:
- 计算周期和高低电平所占时间,
Period
= 20ms,High
= 14ms,Low
= 6ms - 初始化普通IO如
P3.5
为输出端口 - 初始化定时器,产生1ms的时基
Tick
- 启动定时器,在中断服务函数
ISR
中进行Counter
计数 - 在
ISR
或主进程中对比High / Low
与Counter
,一旦Counter >= High / Low
就让P3.5
输出1 / 0,其余时间输出0 / 1,Counter > 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
依然不准确