小时候玩的四驱车,给直流马达接通电源就嗤嗤的跑出去了。电机种类众多,就属控制直流电机最简单,只要对调正负极就能改变电机的转向。另外,直流电机负载能力强,适合做越野车的驱动电机。为了做可调速的越野车,就有了这篇文章。
电源电压输出是固定的,电机的转速也因此是固定的,为了调节电机的转速,就得改变电源电压的输出(为嘛要改变电机转速?举个简单的列子,双电机驱动小车-一边一个电机-如何实现转向?让两边轮胎上的速度不同即可)。how?用PWM调制的方法,把恒定的直流电源电压调制成频率一定宽度可变的脉冲电压序列,从而可以改变平均输出电压的大小,以调节电机的转速。电源电压在此处就是51MCU的引脚输出,4.5-5V,只要在引脚上产生频率可调的波形即可。假设在一个周期内,就10ms吧,前5ms引脚输出高电平,后5ms引脚输出低电平,周而复始,引脚上输出50%占空比的稳定方波;再改改,前2ms输出高电平,后8ms输出低电平,引脚上输出20%占空比的稳定方波。
MCU产生周期性事件很简单,用定时器定时产生中断即可。一般差不多一下形式:
void Isr01() interrupt 1 { static unsigned int cnt; cnt++; if(cnt==40) { cnt=0; } }假设每250us产生一次中断,以上中断函数统计40次中断,即10ms。
unsigned int condition=20; void Isr01() interrupt 1 { static unsigned int cnt; cnt++; //一个周期 if(cnt==40) { cnt=0; } //事件1 if(cnt<condition) { //do sth } //事件2 else { //do other thing } }condition就是新增加的统计事件,前半个周期为一个事件,后半个周期为另一个事件。这里,当cnt小于5ms,输出高电平,当cnt大于输出低电平,合在一起生成一个50%占空比的方波。
unsigned int condition=20; void Isr01() interrupt 1 { static unsigned int cnt; cnt++; //一个周期 if(cnt==40) { cnt=0; } if(cnt<condition) { //前半个周期do sth pin=0x00; } else { //后半个周期do other thing pin=0x01; } }为了改变占空比,只要在ISR外修改condition即可,贴出最终的代码:
#include <REG52.H> #include <INTRINS.H> sbit P2_0 = P2^0; sbit P2_1 = P2^1; #define MakeByte(target, Hi,Lo) \ do{ \ target |= (((Hi)<<4)|(Lo)); \ }while(0); \ #define SetTH(n,val) \ do{ \ TH##n = val; \ }while(0); \ #define SetTL(n,val) \ do{ \ TL##n = val; \ }while(0); \ #define EnableET(n) \ do{ \ ET##n = 0x01; \ IE |= 0x80; \ }while(0); \ enum KEYSTAT { KEYDOWN=0,KEYUP, }; unsigned char iterator = 0x00; void DelayMs(unsigned int ms) { int i=0,j=0; for(;i<ms;i++) { for(j=0;j<1000;j++) { _nop_(); } } } void OnKeyDown() { if(iterator == 0x28) iterator = 0x00; else iterator += 0x04; } OnKeyUp() {} int main() { unsigned char curKeyStat = KEYUP; unsigned char preKeyStat = KEYUP; SetTH(0,0x06); SetTL(0,0x06); MakeByte(TMOD,0x02,0x02); EnableET(0); TR0 = 0x01; while(1) { while(1) { curKeyStat = P2_1; switch(curKeyStat) { case KEYDOWN: if(curKeyStat == preKeyStat) { //确实按下键 OnKeyDown(); } else { //两次按键不同 状态待定 } preKeyStat = curKeyStat; break; case KEYUP: if(curKeyStat == preKeyStat) { //确实松开键 OnKeyUp(); } else { //两次按键不同 状态待定 } preKeyStat = curKeyStat; break; } DelayMs(200); } } return 0; } void IsrT0() interrupt 1 { static unsigned int enterIsr = 0; TR0 = 0x00; //每250us进入isr enterIsr++; //10ms一个周期 if(enterIsr == 0x28) { enterIsr = 0x00; P2_0 = 0x0; } if(enterIsr<=iterator) { P2_0 = 0x00; } else { P2_0 = 0x01; } TR0 = 0x01; }在主函数中,通过判断按下键来改变condition。
结尾部分,贴上仿真图和仿真结果:
1)占空比100%:
2)差不多55%占空比:
3)0%的占空比,电机休息了: