Wednesday, August 13, 2014

DC Motor Speed Controller with Arduino

หลายคนอาจมองเป็นเรื่องง่าย แต่สำหรับผมแล้วเป็นเรื่องที่ท้าทาย (เพราะไม่มีความรู้ แต่จำเป็นต้องใช้เครื่องมือพร้อมๆ กับการพัฒนาขึ้นใช้เอง) ผมต้องการควบคุมความเร็วรอบของมอเตอร์ ในที่นี้ผมใช้ DC-motor ความเร็วตามระบุข้างตัวมอเตอร์ 5000 rpm กินไฟ 12 v, 7 W ผมใช้ IC ขับมอเตอร์เป็น L293d และใช้ Arduino Mega 2560 board และแสดงผลทาง LCD 2x16 ขอสรุป อุปกรณ์ที่ต้องใช้ดังนี้ครับ

1. Computer จำนวน 1 เครื่อง
2. Arduino MEGA 2560 จำนวน 1 บอร์ด
3. L293d จำนวน 1 ตัว
4. DC motor จำนวน 1 ตัว
5. 5k Potentiometer จำนวน 3 ตัว
6. 100 uF 25 v Capacitor จำนวน 1 ตัว
7. 1 nF Capacitor จำนวน 1 ตัว
8. 1N4007 diode  จำนวน 1 ตัว
9. Red-, Blue-LEDs  จำนวนอย่างละ 1 ตัว (แล้วแต่ชอบเรื่องสี)
10. Power supply 6.5 v, 1A adapter สำหรับ Arduino
11. Power supply 12 v, 1A adapter สำหรับ Motor driver
(สำหรับ power supply แล้วแต่ความถนัดครับใช้แบบไหนก็ได้ ใช้ adapter 2 ตัว ง่ายดีไม่ต้องทำเอง แต่ดูไม่มืออาชีพครับ)
12. Push switch จำนวน 2 ตัว พร้อมด้วย 220 โอห์ม จำนวน 2 ตัว

ความต้องการของผมคือ

1. สามารถปรับความเร็วรอบได้

2. สามารถกำหนดเวลา speed up ขณะเริ่มต้น และ slow down เมื่อสั่งหยุดการทำงาน

3. ตั้งเวลาหมุนได้

หลักการทำงานจึงถูกออกแบบไว้ประมาณนี้

- การกำหนดค่าต่างๆ จากผู้ใช้งาน กำหนดโดยการหมุนปรับ Potentiometer ส่วนนี้จะให้ Arduino รับค่า analog เข้ามา

โดยใช้คำสั่ง  "analogRead(PIN);"

- แปลงค่าที่รับมาให้ตรงกับ ADC ในบอร์ด และความต้องการของเรา

-นำค่าที่รับมาไปกำหนดการทำงานของมอเตอร์ และแสดงผลทางหน้าจอ LCD ด้วย

-มีปุ่มกด Start และ Abort เมื่อกดปุ่ม Start มอเตอร์เริ่มทำงานทันทีและหยุดตามเวลาที่ตั้งไว้ หากต้องหยุดการทำงานระหว่างทาง ผู้ใช้สามารถกดปุ่ม Abort ได้เลย

รูปด้านล่างเอามาให้ดูก่อนว่าช่วงแรกๆ ที่คนมือใหม่ทั้ง อิเล็กทรอนิกส์ programming Arduino จะยุ่งขนาดไหนครับ ท่านดูแล้วอาจบอกว่าไม่มืออาชีพ (แอบด่าตัวเองแล้วกัน) แต่ผมคิดในใจว่า ขอแค่ผมเข้าใจมันและกำนหดให้มันทำงานตามที่ต้องการก็เพียงพอ สำหรับสถานะต้นๆ นี้ ความฉลาดค่อยพัฒนาในขั้นถัดไปก็แล้วกัน


ในรูปใช้ PWM ใน Arduino board จากนั้้นปรับค่า duty cycle จากตัวต้านทานปรับค่าได้ ทำให้ความเป็น DC มากหรือน้อยเพื่อไปกำหนดอัตราเร็วของ DC motor เบื้องต้นปรับความเร็วได้แต่ยังไม่ทราบว่า เท่าไร และมีปุ่มกด stop หยุดการทำงานของมอเตอร์ได้ (ในรูปนี้ผมยังใช้ Arduino UNO R3 อยู่นะครับ ตอนหลังเปลี่ยนเป็น Mega เพราะต้องการใช้พอร์ตเยอะขึ้น [ตามความเข้าใจของข้าพเจ้า])

ขั้นต่อไปที่ต้องการคือ

1. กำหนดค่า อัตราเร็ว เวลาในการหมุน เวลาสำหรับ speed up และ slow down จากผู้ใช้ (เพื่อรักษาและปรับการเคลื่อนที่ให้นุ่มนวล เพราะกฎของนิวตันทำงานเสมอ ต้องระวัง)

2. เพื่อปุ่ม Abort ในกรณีที่ผู้ใช้อยากให้มอเตอร์หยุดระหว่างการทำงาน

3. แสดงค่าที่ผู้ใช้กำหนดบน 16x2 LCD

4. หาวิธีวัดอัตราเร็วรอบ และป้อนค่ากลับเพื่อให้มอเตอร์หมุนเร็วเท่ากับค่าที่ผู้ใช้ตั้งไว้ (ข้อนี้คงอีกนาน แต่เห็นวิธีนึงละคือ PID control)


ส่วนรูปการต่อที่ดูง่ายจะเอามาให้ดูต่อไป แต่ไปหัดใช้โปรแกรมวาดวงจรก่อนนะ .......ผ่านไปหลายวันด้วยความขี้เกียจ เอาเป็นว่าผมขอยกวงจรที่จำลองในโปรแรกม Proteus มาให้ดูแล้วกันครับ ส่วนวงจรที่เป็น Schematic ค่อยว่ากัน รูปดังต่อไปนี้ผมใช้ Arduino Mega 2560 ครับ ซึ่งจะสอดคล้อง source code ที่เขียนใน sketch เลย ผมทดสอบแล้วทั้ง Hardware และ Software รวมไปถึงการจำลองใน Proteus ตามภาพครับ


ในรูปนี้แสดงการจำลองการทำงานของ DC motor สัญญาณเส้นสีเหลืองคือ PWM ที่ออกจาก Ardunio ส่วนเส้นสีฟ้าคือสัญญาณที่ออกจาก Motor driver IC, L293d ครับ 

อธิบายการทำงานสักนิดครับ

1. ผู้ใช้สามารถกำหนดค่าได้ดังต่อไปนี้ผ่าน ตัวต้านทานปรับค่าได้ "อัตราเร็ว รอบต่อนาที" "เวลาสำหรับ speed up และ slow down" "เวลาที่ต้องการให้มอเตอร์หมุน" Arduino จะรับค่าผ่าน analogRead แล้วแปลงเป็นข้อมูล 8-bit

2. Arduino จะรับค่า Digital เพื่อสั่งงานให้ มอเตอร์ทำงาน (กดปุ่ม Start) หรือหยุดทำงาน (Stop) ผ่าน สวิทซ์

3. หากผู้ใช้ต้องการหยุดมอเตอร์ ขณะที่ยังทำงานอยู่ สามารถกดปุ่ม Stop ได้ทันที Arduino จะรับค่าผ่าน Interrupt port (ส่วนนี้ทำอยู่นานเพราะไม่เข้าใจและหัดใช้เป็นครั้งแรก)

และนี่คือ source code ที่เขียนใน sketch ครับ แต่ยังไม่ได้ทำให้มันฉลาดนะครับ ยังขาดส่วนรักษาอัตราเร็ว เมื่อมีโหลดเข้ามา เทคนิคที่เล็งไว้คือ PID ซึ่งมีหลายที่พัฒนาไว้เรียบร้อยแต่สมองอันจำกัดของผมยังไม่เข้าใจ ลองไปดูกันครับ >>> PID

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%//
//------------------ UP-Spin_coater -------------------------//
//-----------------Designed by S.Unai -----------------------//
//-----Microfluidics and Image Processing Research Unit -----//
//------------- University of Phayao, Thailand --------------//
//----------------------- 2014 ------------------------------//
#include
//--------- LCD setPIN ------------//
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%//
int Ramp_time, Spin_speed, Spin_time, DynamicSpinTime,ii;
float ShowSpin_speed;
int Start, Stop;
volatile int State = HIGH;
int motor        = 3;     // PWM pin output signal send to motor
int SpinSpeedPIN = A8;    // Potentiometer Spin-speed adjustment by USER
int TimeDelayPIN = A9;    // Potentiometer Spin-time adjustment by USER
int SpinTimePIN  = A10;   // Ramp and Delay time for spin
int SwitchStart  = 31;    // Digital pin31= push Start switch
int SwitchAbort  = 20;    // Interrupt PIN
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%//
void setup()
{
  pinMode(motor,OUTPUT);
  pinMode(SpinSpeedPIN,INPUT);
  pinMode(SpinTimePIN,INPUT);
  pinMode(TimeDelayPIN,INPUT);
  pinMode(SwitchStart,INPUT);
  pinMode(SwitchAbort,INPUT);
  //Serial.begin(9600);
  //-----------------Set LCD ---------------//
  lcd.begin(16, 2);
  // Print a message on the specific pixels of LCD panel.
  lcd.setCursor(0, 0);  lcd.print("UP-SpinCoater V1.0");
  delay(3000);
  lcd.clear();
 
  lcd.setCursor(8,0);   lcd.print("Rp");
  lcd.setCursor(10,1);  lcd.print("s");
  lcd.setCursor(0,0);   lcd.print("RPMset");
  lcd.setCursor(12,0);  lcd.print("Time");
  lcd.setCursor(15,1);  lcd.print("s");
 
  // Interrupt action
  attachInterrupt(1, AbortAction, CHANGE);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%//
void loop()
{
//=======================Input Unit================================//
  Start = digitalRead(SwitchStart); 
  Stop  = digitalRead(SwitchAbort);
  if (Stop==1)// pull up ==0, pull down=1
  {
    State=LOW;
  }
  //-------------------------------------
  Ramp_time  = analogRead(TimeDelayPIN);
  Ramp_time  = map(Ramp_time,0,1023,0,255);
  Ramp_time  = ((10*Ramp_time)/255);
  //-------------------------------------
  //analogReadResolution(8);
  Spin_speed = analogRead(SpinSpeedPIN); //Spin_speed  = map(Spin_speed,0,1023,0,255);
  Spin_speed = map(Spin_speed,0,1023,0,255);//((10000*Spin_speed)/255);
  //-------------------------------------
  Spin_time  = analogRead(SpinTimePIN);
  Spin_time  = map(Spin_time,0,1023,0,255);
  Spin_time  = (2.58823529411765)*Spin_time; // 1.17647058823529==>300, 2.352941176==>600 s,3.92156862745098 ==> 1000 s

  ShowSpin_speed = Spin_speed;//17.6470588235294*Spin_speed;
 
//=======================Speed Control Unit=======================//
analogWrite(motor,0);  //Set initialize motor on stop mode
ii = Spin_time;

if (Start==1 && Stop==0) // pull up= 0 1, pull down= 1 0
   {
     for (int a=1; a <= 10; a++) // Speed Up
        {
            analogWrite(motor,(0.1*a*Spin_speed));
            delay(Ramp_time*100);         
        }      
     for (int b=1; b <= Spin_time; b++) // Constant Speed
        { 
            if (Stop==1)// pull up=0, pull down= 1; check Abort push button //get Stop value from interrupt function
              {                      
                  break; //Kill the constant velocity for-loop
              }
                  //delay(100);
                 
            ii = ii--;
            analogWrite(motor,Spin_speed);
            delay(1000);
                  if(ii<10 10="" br="">{
                           lcd.setCursor(12, 1);lcd.print(ii);
                           lcd.setCursor(13, 1);lcd.print("  ");
                       }
                  else if(ii>=10 && ii<100 100="" br="">{
                           lcd.setCursor(12, 1);lcd.print(ii);
                           lcd.setCursor(14, 1);lcd.print(" ");
                       }  
                  else {
                           lcd.setCursor(12, 1);lcd.print(ii);
                       }
                                           
        }    
      for (int c=10; c <=1; c--) // Speed Down
        {
            analogWrite(motor,(0.1*c*Spin_speed));
            delay(Ramp_time*100);                     
        }        
   
  }
else
     {
       digitalWrite(motor,0);
     }  
  
//==================== Display Unit ==============================// 
        if(Ramp_time<10 br=""><10 br=""> <10) {
<10 10="" br=""><100 100="" br=""><10 br=""><10 br="">                lcd.setCursor(8, 1);lcd.print(Ramp_time);
                lcd.setCursor(9, 1);lcd.print(" ");
              }
        else    
              {
                lcd.setCursor(8, 1);lcd.print(Ramp_time);
              }
//----------------------------------------------------------------//            
              // Show spin speed
              lcd.setCursor(0, 1);
              lcd.print(ShowSpin_speed);            
//---------------------------------------------------------------//

        if(Spin_time <10)<10 br="">               
<10 10="" br=""><100 100="" br=""><10 br=""><10 br=""><10 br="">               lcd.setCursor(12, 1);lcd.print(Spin_time);
               lcd.setCursor(13, 1);lcd.print("  ");
             }
          else if(Spin_time>=10 && Spin_time<100 100="" br="">{
               lcd.setCursor(12, 1);lcd.print(Spin_time);
               lcd.setCursor(14, 1);lcd.print(" ");
             }  
          else {
               lcd.setCursor(12, 1);lcd.print(Spin_time);

             }           
    }

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%//
void AbortAction()
{
     State=!State;
     Stop=1; //pull up=0, pull down=1; sending this value to main for-loop    
}


ขยายความเรื่อง interrupt (ตามความเข้าใจของข้าพเจ้า) Arduino Mega2560 มี interrupt ports ดังนี้ครับ รายละเอียดติดตามได้ที่ arduino.cc ผมใช้ขา 20
Mega25602321201918

ในโปรแกรมที่ผมเขียนจะใช้ ตัวแปรชื่อ state เป็นตัวกำหนดการเปลี่ยนแปลงเพื่อให้ Arduino รับรู้ผ่านฟังก์ชัน attachInterrupt ทางขา interrupt port

การใช้งานประมาณนี้เลยครับ

// Interrupt action
  attachInterrupt(3, AbortAction, CHANGE);

// AbortAction คือฟังก์ชันที่เขียนขึ้นมาครับ มีรายละเอียดตามนี้
void AbortAction()
{
     State=!State;
//กำหนดให้กลับไปสถานะเดิมเพื่อรอรับค่าการเปลี่ยนแปลงครั้งต่อไป
     Stop=1;
// sending this value to main for-loop      ส่งค่า Stop เท่ากับ 1 ไปให้ฟังก์ชันหลักทำงาน (ในที่นี้คือสั่งให้มอเตอร์หยุดหมุน)
}


ซึ่งในคำสั่งจะเขียน if condition ไว้ว่าหลังจากที่ทำงานไปแล้ว (start==1 และ stop==0) หากเมื่อไรก็ตามถ้าตรวจพบว่า stop==1 ให้หยุด for loop ทันทีโดยใช้คำสั่ง break


สุดท้ายครับ หากท่านใดเข้ามาเห็นแล้วจะเอาไปใช้ก็ไม่ว่ากระไร หากเอาไปพัฒนาต่อกรุณาเอามาแชร์ให้ผมด้วย เพราะผมไม่มีความรู้จริงๆ ตอนนี้ที่ศึกษาผมก็จะหมกมุ่นกับวิธีการของตัวเอง จริงๆ มันอาจจะมีวิธีที่ฉลาดกว่านี้ก็ได้เอามาแบ่งปัน ชี้แนะให้กระผมด้วยนะครับ

มาอัพเดทข้อมูลกันนิดหน่อย เรื่องการรักษาระดับอัตราเร็วรอบ ของมอเตอร์ พบว่าที่เวบ arduino.cc  มีคนพัฒนา PID library ให้ใช้แล้วครับ แต่ส่วนตัวยังไม่มีเวลามากพอที่จะลอง แต่ผมได้นำโปรแกรมพร้อม บอร์ด Arduino ที่พัฒนาในงานนี้ไปใช้กับงานอื่นได้ด้วยนะครับ ประเด็นก็คือ การพัฒนาด้วยตัวเองมันจะมีผลพลอยได้จากเทคโนโลยีที่เราพัฒนา อีกหลายอย่างตามมาที่นำไปประยุกต์กับอะไรก็ได้ ความรู้ที่แตกออกมาก็เช่นกัน สามารถนำไปช่วยคนอื่นได้เช่นกัน โอกาสต่อไปคงได้นำผลงานอื่นมาลงอีก (ที่เป็นผลพวงจาก เรื่องนี้)

1 comment :

  1. สำหรับ source code ที่ดูวุ่นวายนั้น เริ่มแรกไม่มีอะไรมากนัก แต่ก็เพิ่มเข้าไปเรื่อยๆ ตามความต้องการสุดท้ายเป็นวุ่นวายตาม การเขียนแบบบ้านๆ ของผมเอง

    สำหรับมือใหม่ ไม่ต้องกังวลเรื่องนี้ ช่วงแรกๆ โค๊ดสัก 20 บรรทัด ก็ดีใจแล้ว แต่พอเขียนไปสักพักมันก็จะวุ่นวายเอง

    ReplyDelete