Arduino A4950 驅動直流電機 超詳細版

St.浩發表於2021-01-02

Arduino A4950 驅動直流電機

對於自己做車的大部份同學來說,我和大家一樣,用的最多的就是L298N驅動器,這次在家想試著自己研究一個驅動能力更好的更加方便的電機驅動,而網上使用Arduino L298N 驅動小車的例子非常的多 A4950 的一篇也沒有,那好吧,希望這篇文章能給L298N用膩了的同學提供一個更好更新的選項

1.實驗準備

1.Arduino 系列微控制器
2.直流電機
3.A4950驅動器

2.A4950晶片簡介

在這裡插入圖片描述
一個A4950驅動器可驅動兩個直流電機
驅動板工作電壓範圍:7.6V~30V

A4950引腳對應引腳
VCC微控制器5V
GND微控制器GND
VM驅動電源7.6~30V
AIN1控制A電機的1號PWM引腳
AIN2控制A電機的2號PWM引腳
AOUT1電機A正極
ATOU2電機A負極
BN1控制A電機的1號PWM引腳
BIN2控制A電機的2號PWM引腳
BOUT1電機B正極
BTOU2電機B負極

一個模組上有兩組 VCC GND VM 至少接一組

微控制器 A4950 驅動電源 記得共地

A4950 驅動是通過比較兩個控制引腳輸出PWM的大小關係來確定電機方向的
兩個控制引腳輸出PWM的差值決定電機的轉速

3.程式設計

我們接下來的程式碼都已驅動一個電機為例,剩下一個如法炮製就可以了

3.1簡易驅動板

unsigned int Motor_AIN1=2;       //控制A電機的PWM引腳  一定改成自己用的
unsigned int Motor_AIN2=3;       
char Motor_Order;                //定義一個字元型變數儲存串列埠輸入命令
void setup() 
{
  Serial.begin(9600);            //開啟串列埠
  Serial.println("/*****開始驅動*****/");
  pinMode(Motor_AIN1,OUTPUT);   //設定兩個驅動引腳為輸出模式
  pinMode(Motor_AIN2,OUTPUT); 
}

void loop() 
{
 while(Serial.available()>0)    //檢測串列埠是否有命令
  {
    Motor_Order=Serial.read();  //將命令儲存在變數中
    switch(Motor_Order)
    {
      //傳送字元1電機正轉
      case '1' : analogWrite(Motor_AIN1,250); analogWrite(Motor_AIN2,0);Serial.println("/*****電機正傳*****/");break;
      //傳送字元2電機反轉
      case '2' : analogWrite(Motor_AIN1,0); analogWrite(Motor_AIN2,250);Serial.println("/*****電機反轉*****/");break;
      //傳送其他字元電機停轉
      default  : analogWrite(Motor_AIN1,0); analogWrite(Motor_AIN2,0);Serial.println("/*****停轉****/");break;
    }
  }
}

燒錄之後開啟串列埠分別輸入指令就可以了
在這裡插入圖片描述

3.2串列埠輸入調速版接上位機版(高階版)

好吧這個對萌新來說看起來好像有一點複雜
簡單說功能就是在串列埠按照輸入協議:
控制A電機的1號引腳PWM值.控制A電機的2號引腳PWM值

注:控制A電機的1號引腳PWM值(一個英文的點)控制A電機的2號引腳PWM值
例: 在串列埠輸入 10.255
功能:控制A電機的1號PWM引腳輸出0,控制A電機的1號PWM引腳輸出255
實現:電機反轉,控制速度的PWM差值為 255-10=245

先把完整的程式碼貼上,我會逐個部分講解
先講一下邏輯實現的步驟
1.從串列埠接收一個包含被一個點分開的兩個數字的字串
2.從一個完整字串中擷取出兩個數字字串
3.將數字字串轉換成整形
4.通過PWM引腳將轉換好的數值輸出

第二步詳細實現:1.獲取分割符號(既:點的位置)2.根據點的位置前後擷取

unsigned int Motor_AIN1=2;
unsigned int Motor_AIN2=3;
String   Motor_Order,String_Motor_AIN1_Value,String_Motor_AIN2_Value;
unsigned int Motor_AIN1_Value,Motor_AIN2_Value,Point_desepote;
void setup() 
{
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("/*****開始測試*****/");
  pinMode(Motor_AIN1,OUTPUT);
  pinMode(Motor_AIN2,OUTPUT);
}
/*****************獲取截止位****************/
unsigned int Motor_Point_desepote(String Motor_Order)
{
  unsigned int desepote,point_desepote;
  for(desepote=0;desepote<Motor_Order.length();desepote++)
    if (Motor_Order[desepote]=='.')
    {
      point_desepote=desepote+1;
      Serial.print("點的位置為:");
      Serial.println(point_desepote);
      break;
    }
    return point_desepote;    
}
/****************擷取字串函式***************/
String String_fragment(String Complete_information,unsigned int Intitial_Position,unsigned int Final_Position)
{
  String Fragment;
  unsigned int location;
  for(location=Intitial_Position-1;location<Final_Position;location++)
  Fragment+=Complete_information[location];
  return Fragment;
}
/******************電機驅動函式**************/
void Drive_Motor(unsigned int Motor_AIN1_Value,unsigned int Motor_AIN2_Value)
{
  analogWrite(Motor_AIN1,Motor_AIN1_Value); 
  analogWrite(Motor_AIN2,Motor_AIN2_Value);
  Serial.println("/*****驅動電機*****/");
}
void loop() 
{
  // put your main code here, to run repeatedly:
  while(Serial.available()>0)
  {
    Motor_Order=Serial.readString();
    Point_desepote=Motor_Point_desepote(Motor_Order);
    String_Motor_AIN1_Value=String_fragment(Motor_Order,1,Point_desepote-1);
    String_Motor_AIN2_Value=String_fragment(Motor_Order,Point_desepote+1,Motor_Order.length());
//    Serial.print("String_Motor_AIN1_Value:");
//    Serial.println(String_Motor_AIN1_Value);
//    Serial.print("String_Motor_AIN2_Value:");
//    Serial.println(String_Motor_AIN2_Value);    
    Motor_AIN1_Value=constrain(String_Motor_AIN1_Value.toInt(),0,255);
    Motor_AIN2_Value=constrain(String_Motor_AIN2_Value.toInt(),0,255);
    Serial.print("Motor_AIN1_Value:");
    Serial.println(Motor_AIN1_Value);
    Serial.print("Motor_AIN2_Value:");
    Serial.println(Motor_AIN2_Value);
    if(Motor_AIN1_Value==Motor_AIN2_Value)
    {
     Drive_Motor(Motor_AIN1_Value,Motor_AIN2_Value);
     Serial.println("電機停轉");
    }
    else if(Motor_AIN1_Value>Motor_AIN2_Value)
    {
      Drive_Motor(Motor_AIN1_Value,Motor_AIN2_Value);
      Serial.println("電機正轉"); 
    }
    else if(Motor_AIN1_Value<Motor_AIN2_Value)
    {
      Drive_Motor(Motor_AIN1_Value,Motor_AIN2_Value);
      Serial.println("電機反轉"); 
    }
  }
}

3.2.1 定義部分

unsigned int Motor_AIN1=2;
unsigned int Motor_AIN2=3;
String   Motor_Order,String_Motor_AIN1_Value,String_Motor_AIN2_Value;
unsigned int Motor_AIN1_Value,Motor_AIN2_Value,Point_desepote;
變數名稱功能
Motor_AIN1控制A電機的1號PWM引腳號
Motor_AIN2控制A電機的2號PWM引腳
Motor_Order用來儲存串列埠處的完整指令
String_Motor_AIN1_Value儲存控制A電機的1號PWM值字串變數
String_Motor_AIN2_Value儲存控制A電機的2號PWM值字串變數
Motor_AIN1_Value儲存控制A電機的1號PWM值整形變數
Motor_AIN2_Value儲存控制A電機的2號PWM值整形變數
Point_desepote儲存初始命令點的位置

3.2.2獲取點的位置

/*****************獲取截止位****************/
unsigned int Motor_Point_desepote(String Motor_Order)
{
  unsigned int desepote,point_desepote;
  for(desepote=0;desepote<Motor_Order.length();desepote++)
    if (Motor_Order[desepote]=='.')
    {
      point_desepote=desepote+1;
      Serial.print("點的位置為:");
      Serial.println(point_desepote);
      break;
    }
    return point_desepote;    
}

功能:檢索字串中點的位置並返回

注: 字串長度表示方式 : 字串名.length()
例: Motor_Order.length()

3.2.3字串擷取函式

/****************擷取字串函式***************/
String String_fragment(String Complete_information,unsigned int Intitial_Position,unsigned int Final_Position)
{
  String Fragment;
  unsigned int location;
  for(location=Intitial_Position-1;location<Final_Position;location++)
  Fragment+=Complete_information[location];
  return Fragment;
}

功能:從指定字串中截從第(Intitial_Position)位擷取到第(Final_Position)位
並作為字串變數返回

注:使用方法 : String_fragment(要擷取字串變數名,擷取的初始位,擷取的終止位)

3.2.4電機驅動函式

void Drive_Motor(unsigned int Motor_AIN1_Value,unsigned int Motor_AIN2_Value)
{
  analogWrite(Motor_AIN1,Motor_AIN1_Value); 
  analogWrite(Motor_AIN2,Motor_AIN2_Value);
  Serial.println("/*****驅動電機*****/");
}

這個不多說了只要分別輸入數值就可以了

3.2.5初始化設定函式

void setup() 
{
  // put your setup code here, to run once:
  Serial.begin(9600);                    //開啟串列埠
  Serial.println("/*****開始測試*****/");
  pinMode(Motor_AIN1,OUTPUT);            //設定兩控制引腳為輸出模式
  pinMode(Motor_AIN2,OUTPUT);
}

3.2.6主函式實現

void loop() 
{
  // put your main code here, to run repeatedly:
  while(Serial.available()>0)              //判斷串列埠是否接收到資料
  {
    Motor_Order=Serial.readString();       //將串列埠的字串存到Motor_Order中
    Point_desepote=Motor_Point_desepote(Motor_Order);//獲取Motor_Order字串中點的位置
    String_Motor_AIN1_Value=String_fragment(Motor_Order,1,Point_desepote-1);//擷取儲存控制A電機的1號PWM值字串變數(從第一位擷取到點的前一位)
    String_Motor_AIN2_Value=String_fragment(Motor_Order,Point_desepote+1,Motor_Order.length());//擷取儲存控制A電機的2號PWM值字串變數(從點的後一位擷取到最後一位)
//    Serial.print("String_Motor_AIN1_Value:");
//    Serial.println(String_Motor_AIN1_Value);
//    Serial.print("String_Motor_AIN2_Value:");
//    Serial.println(String_Motor_AIN2_Value);    
    Motor_AIN1_Value=constrain(String_Motor_AIN1_Value.toInt(),0,255);//將擷取儲存控制A電機的1號PWM值字串變數轉換成整形,並設定值約束其在0~255範圍內
    Motor_AIN2_Value=constrain(String_Motor_AIN2_Value.toInt(),0,255);
    //將擷取儲存控制A電機的2號PWM值字串變數轉換成整形,並設定值約束其在0~255範圍內
    Serial.print("Motor_AIN1_Value:");   //將數值列印以便觀察
    Serial.println(Motor_AIN1_Value);
    Serial.print("Motor_AIN2_Value:");
    Serial.println(Motor_AIN2_Value);
    //驅動控制部分
    if(Motor_AIN1_Value==Motor_AIN2_Value)  //如果Motor_AIN1_Value=Motor_AIN2_Value   Motor_AIN1_Value,Motor_AIN2_Value  兩PWM差值為0  電機停轉
    {
     Drive_Motor(Motor_AIN1_Value,Motor_AIN2_Value);
     Serial.println("電機停轉");
    }
    else if(Motor_AIN1_Value>Motor_AIN2_Value)//如果Motor_AIN1_Value>Motor_AIN2_Value ,   Motor_AIN1_Value-Motor_AIN2_Value 兩PWM差值為為正  電機正轉 
    {
      Drive_Motor(Motor_AIN1_Value,Motor_AIN2_Value);
      Serial.println("電機正轉"); 
    }
    else if(Motor_AIN1_Value<Motor_AIN2_Value)//如果Motor_AIN1_Value>Motor_AIN2_Value ,   Motor_AIN1_Value-Motor_AIN2_Value 兩PWM差值為為負  電機反轉 
    {
      Drive_Motor(Motor_AIN1_Value,Motor_AIN2_Value);
      Serial.println("電機反轉"); 
    }
  }
}

開啟串列埠輸入命令效果如圖
在這裡插入圖片描述

到這裡就全部結束了,還有一個要說的
我為什麼一定要轉換成字串型之後再轉換成整形,而不是在一個函式裡直接解決,因為arduino官方庫沒找到類似指定位置的字串擷取函式,單獨列出來方便理解,方便類似功能移植

4. 後言

本文章是通過Arduino系列微控制器程式碼實現功能,同樣的如果看懂原理,用其他微控制器當然也可以實現,A4950的模組詳細資料好多我就不貼了,希望有幫到大家

相關文章