天天看點

51單片機PWM控制電機

    小時候玩的四驅車,給直流馬達接通電源就嗤嗤的跑出去了。電機種類衆多,就屬控制直流電機最簡單,隻要對調正負極就能改變電機的轉向。另外,直流電機負載能力強,适合做越野車的驅動電機。為了做可調速的越野車,就有了這篇文章。

    電源電壓輸出是固定的,電機的轉速也是以是固定的,為了調節電機的轉速,就得改變電源電壓的輸出(為嘛要改變電機轉速?舉個簡單的列子,雙電機驅動小車-一邊一個電機-如何實作轉向?讓兩邊輪胎上的速度不同即可)。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。

結尾部分,貼上仿真圖和仿真結果:

51單片機PWM控制電機

1)占空比100%:

51單片機PWM控制電機

2)差不多55%占空比: