ผู้เขียน หัวข้อ: เรียนรู้ครั้งที่ 22 [การสร้างโปรแกรมควบคุมสเต็ปเปอร์มอเตอร์ด้วย Visual C#]  (อ่าน 7200 ครั้ง)

admin

  • Administrator
  • Hero Member
  • *****
  • กระทู้: 706
    • ดูรายละเอียด
    • อีเมล์
สเต็ปเปอร์มอเตอร์หรือที่บางคนเรียกว่าสเต็ปปิ้งมอเตอร์ เป็นมอเตอร์ที่ทำงานโดยอาศัยพลังงานจากไฟฟ้ากระแสตรงเช่นเดียวกับมอเตอร์กระแสตรง  แต่มีความความแตกต่างจากมอเตอร์กระแสตรงที่เมื่อป้อนไฟให้กับสเต็ปเปอร์มอเตอร์จะขยับเพียงเล็กน้อยเพื่อเข้าสเต็ป  ในขณะที่มอเตอร์ไฟฟ้ากระแสตรงทั่วไปจะหมุนตราบที่ยังมีพลังงานจ่ายให้แก่ตัวมัน ทั้งนี้เป็นเพราะโครงสร้างของสเต็ปเปอร์มอเตอร์นั่นเอง โดย สเต็ปเปอร์มอเตอร์มีด้วยกัน 2  แบบคือ
   1. Bipolar เป็นสเต็ปเปอร์มอเตอร์ที่มีขดลวด 2 ขดมีสายไฟให้ต่อใช้งาน 4 เส้น
   2. Unipolar เป็นสเต็ปเปอร์มอเตอร์ที่มีขดลวด 4 ขด (2 ขดแบบมีแท็ปกลาง)
สำหรับการเรียรรู้ในครั้งนี้เป็นการเรียนรู้ที่ใช้สเต็ปเปอร์มอเตอร์แบบ Unipolar แบบที่มี 5 สาย (จุดแท็ปกลางของทั้ง 2 ขดเชื่อมต่อกันแล้ว) เป็นมอเตอร์เบอร์ เบอร์ 28BYJ-48-5V มีคุณสมบัติในการขับสเต็ปครบ 1 รอบคือ 360° ใช้ 512 สเต็ป เมื่อขับแบบฟูลสเต็ป 1 เฟส คุณสมบัติโดยทั่วไปของมอเตอร์มีดังนี้
4 Phase 5 Wire Connection
-100% Brand New
-Phase : 4
-Current : 160 mA per winding (320 mA in 4-step mode) Measured: 250mA stopped, 200 mA running fast
-Resistance : 31 Ω per coil winding (from Red wire to any coil) (Some 24-28 ohms)
-Voltage : 5V DC
-Step Angle (8-Step sequence: Internal Motor alone): 5.625° (64 steps per revolution)
-Step Angle (4-Step sequence: Internal Motor alone): 11.25° (32 steps per revolution)
-Gear Reduction ratio: 1 / 64 (Not really exact: probably 63.68395.:1 )
-SO: it takes (64*64 = 4096 steps per output shaft revolution.. In 8-step sequence.
-SO: it takes (32*64 = 2048 steps per output shaft revolution.. In 4-step sequence.
-NOTE: Arduino "Stepper Library" runs in 4-step mode
-No-Load Pull-Out Frequency : 800pps
-No-Load Pull-In Frequency : 500pps
-Pull-In Torque : ≥ 78.4mN.m
-Wiring Instruction : A (Blue), B (Pink), C (Yellow), D (Orange), E (Red, Mid-Point)
-Weight : 30g

คอนเซปของการสร้าง
   -บอร์ด Arduino รอรับคำสั่งที่ส่งมาจากคอมพิวเตอร์ผ่านทางพอร์ตอนุกรมแล้วมาถอดรหัสคำสั่งว่าต้องการสั่งให้สเต็ปมอเตอร์หมุนไปทางทิศทางใด (ตามเข็ม:CW หรือทวนเข็มนาฬิกา:CCW) และต้องการให้หมุนไปจำนวนกี่องศาโดยข้อมูลที่รับมาตัวบอร์ดจะทำการประมวลผลและคำนวณโดยค่ามุมที่รับมาเป็นองศาทำการแปลงเป็นสเต็ปในการควบคุมในอัตราส่วน 360° /512 step แล้วจึงทำการสั่งให้มอเตอร์ทำงานตามสเต็ปที่คำนวณได้ ในขณะที่ขับมอเตอร์แต่ละสเต็ปให้บอร์ดส่งค่ามุมย้อนกลับไปแสดงผลที่จอคอมพิวเตอร์ผ่านทางพอร์ตอนุกรม
   -โปรแกรมอินเตอร์เฟสที่สร้างขึ้นด้วย Visual C# ทำการรับค่ามุมและทิศทางหมุนจากผู้ใช้งานแล้วส่งออกไปทางพอร์ตอนุกรมโดยข้อมูลที่ส่งไปเป็นคำสั่งควบคุมที่ประกอบด้วย ข้อมูลสั่งสเต็ปมอเตอร์ ทิศทางหมุนและองศาที่ต้องการหมุน สร้างส่วนรับข้อมูลองศาที่ส่งเข้ามาทางพอร์ตอนุกรม(ถูกส่งมาจากบอร์ดควบคุมมอเตอร์)เพื่อแสดงผลมุมที่กำลังดำเนินการ

มีขั้นตอนดังนี้
1. วงจรบอร์ดควบคุมสเต็ปเปอร์มอเตอร์โดยใช้ Arduino เป็นดังรูป


2. รูปการเชื่อมต่อ


3. โปรโตคอลของคำสั่งที่สร้างขึ้นประกอบด้วย
   -คำสั่งสั่งทำงานสเต็ปมอเตอร์ 4 ไบต์แรก
   -คำสั่งกำหนดทิศทางการหมุนเป็นไบต์ที่ 5 โดยค่าที่ใส่จะเป็นตัวอักษร "F" หรือ "R"
   -ข้อมูลองศาที่ต้องการหมุนในไบต์ที่ 6 เป็นต้นไป (การเรียนรู้ครั้งนี้เขียบบังคับให้ใส่ไม่เกิน 10000°)


4.สร้างโปรเจคไฟล์แล้ววางตัวคอนโทรลและแก้ไขชื่อดังรูป ตัวคอนโทรลที่ใช้งานได้แก่
    -Button
    -ComboBox
    -GroupBox
    -Label
    -SerialPort
    -TextBox


5. คลิกเพิ่มรายการในคอมโบบ็อกที่ใช้เลือกความเร็วในการสื่อสาร

ความเร็วที่ให้เลือกใช้มีดังนี้
โค๊ด: [Select]
9600
19200
38400
57600
74880
115200

6.กดปุ่ม Shift ค้างไว้แล้วคลิกที่คอมโบบ็อกทั้ง 2 ตัวตั้งค่าคุณสมบัติเริ่มต้นให้ DropDownStyle ให้เป็น DropDownList เพื่อไม่ให้ผู้ใช้งานพิมพ์ทับลงในช่องขณะรันโปรแกรม (ให้เลือกอย่างเดียว)


7. ดับเบิลคลิกที่ฟอร์มแล้วเขียนโค้ด


8. เขียนโค้ดให้โปรแกรมสแกนหาพอร์ตอนุกรมที่คอมพิวเตอร์มองเห็น

   -ใช้งานพอร์ต
   -ประกาศตัวแปรเก็บทิศทางการหมุน
   -สแกนหาพอร์ตอนุกรมที่คอมพิวเตอร์มองเห็น ณ ปัจจุบัน และให้เลือกบอดเรทเป็น 19200 เป็นค่าเริ่มต้น
หมายเหตุ เนื่องจากมีการส่งย้อนกลับไปยังคอมพิวเตอร์ผ่านพอร์ตอนุกรมที่เป็นค่ามุมที่ขยับมอเตอร์ถี่ ๆ ต่ำกว่า 20mS ในแต่ละค่าจึงใช้บอดเรทต่ำ (9600) ไม่ได้จะทำให้ข้อมูลรับได้ผิดพลาด
โค้ด
โค๊ด: [Select]
using System.IO.Ports;
โค๊ด: [Select]
        String direction;
โค๊ด: [Select]
            baudrate_cbb.SelectedIndex = 1;
            string[] ports = SerialPort.GetPortNames();
            port_cbb.Items.AddRange(ports);
            port_cbb.SelectedIndex = 0;

9. คลิกที่คอมโบบ็อกที่ใช้เลือกพอร์ต แล้วคลิกเลือกเหตุการณ์กระตุ้นเป็น MouseClick (แล้วดับเบิลคลิก) เพื่อเขียนโค้ดให้ทำงานเมื่อมีเมาส์มาคลิกที่คอมโบบ็อกตัวนี้


10. เขียนโค้ดให้โปรแกรมสแกนหาพอร์ตอนุกรมใหม่ที่คอมพิวเตอร์มองเห็นในจังหวะที่มีการคลิกเมาส์ที่คอมโบบ็อกนี้

โค้ด
โค๊ด: [Select]
            port_cbb.Items.Clear();
            string[] ports = SerialPort.GetPortNames();
            port_cbb.Items.AddRange(ports);
            port_cbb.SelectedIndex = 0;

11. ดับเบิลคลิกที่ปุ่ม Connect เพื่อเขียนโค้ดให้โปรแกรมเปิดพอร์ตเชื่อมต่อ


12. เขียนโค้ดควบคุม

โค้ด
โค๊ด: [Select]
            con_btn.Enabled = false;
            dis_btn.Enabled = true;
            send_btn.Enabled = true;
            if (serialPort1.IsOpen)
                serialPort1.Close();
            serialPort1.PortName = port_cbb.Text;
            serialPort1.BaudRate = Convert.ToInt32(baudrate_cbb.Text);
            try
            {
                serialPort1.Open();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

13. ดับเบิลคลิกที่ปุ่ม Disconnect เพื่อเขียนโค้ดปิดการใช้งานพอร์ต


14. เขียนโค้ดควบคุม

โค้ด
โค๊ด: [Select]
            con_btn.Enabled = true;
            dis_btn.Enabled = false;
            send_btn.Enabled = false;
            if (serialPort1.IsOpen)
            {
                serialPort1.Close();
            }

15. คลิกที่ TextBox เลือกเหตุการณ์กระตุ้นเป็น KeyPress เพื่อเขียนโค้ดให้รับเฉพาะตัวเลขเท่านั้น


16. เขียนโค้ดให้พิมพ์ได้เฉพาะตัวเลขลงช่องเท่านั้น

โค้ด
โค๊ด: [Select]
            if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar))
            {
                e.Handled = true;
            }

17. คลิกที่ TextBox เลือกเหตุการณ์กระตุ้นเป็น TextChange เพื่อเขียนโค้ดให้รับได้ไม่เกิน 10000


18. เขียนโค้ดให้รับตัวเลขได้ไม่เกิน 10000

โค้ด
โค๊ด: [Select]
            if (angle_tb.Text != "" && Int16.Parse(angle_tb.Text) > 10000)
            {
                MessageBox.Show("Number of angle is over size.", "invalid value angle");
                angle_tb.Text = angle_tb.Text.Substring(0, (angle_tb.TextLength - 1));
            }

19. ดับเบิลคลิกที่ปุ่มส่งคำสั่งไปยังบอร์ดเพื่อเขียนโค้ด


20. เขียนโค้ดส่งคำสั่งไปยังบอร์ด

โค้ด
โค๊ด: [Select]
            if (angle_tb.Text != "")
            {
                if (cw_rdb.Checked == true)
                    direction = "F";
                else
                    direction = "R";
                serialPort1.WriteLine("STM1" + direction + angle_tb.Text);
 
            }
            else
                MessageBox.Show("No angle value", "False Command");

21. คลิกไอคอน Serial Port เลือกการกระตุ้นเป็น DataReceived แล้วดับเบิลคลิก


22. เขียนโค้ดรับข้อมูลเข้ามาแล้วแสดงผลมุม(องศา) ที่บอร์ดกำลังหมุนไป

โค้ด
โค๊ด: [Select]
        private string DispString;   //used to store the values read
โค๊ด: [Select]
                DispString = serialPort1.ReadExisting();
                this.Invoke(new EventHandler(DisplayText));
โค๊ด: [Select]
        private void DisplayText(object sender, EventArgs e)
        {
            string[] value = DispString.Split(new string[] { "," }, StringSplitOptions.None);
            status_lable.Text =value[0];
        }

23. คลิกที่ฟอร์มเลือกเหตุการณ์กระตุ้นเป็น FormClosing


24. เขียนโค้ดให้ปิดการใช้งานพอร์ตอนุกรมก่อนปิดโปรแกรม

โค้ด
โค๊ด: [Select]
            if (serialPort1.IsOpen)
                serialPort1.Close();
            Application.Exit();
โค้ดสำหรับบอร์ด Arduino
25. โค้ดส่วนแยกรหัสคำสั่งที่รับเข้ามาจากพอร์ตอนุกรมแล้วสั่งให้สเต็ปมอเตอร์หมุนตามค่ามุมที่รับมาได้


26. ฟังก์ชั่นสั่งให้สเต็ปมอเตอร์หมุนไปยังมุมที่กำหนด


โค้ดเต็ม(บอร์ด Arduino)
โค๊ด: [Select]
#define phaseA 4
#define phaseB 5
#define phaseC 6
#define phaseD 7
char PinCon[] = {phaseA, phaseB, phaseC, phaseD};
int i, j;                         // Step counter

String inputString = "";          // a String to hold incoming data
String command = "";              // a String to hold command
String direction = "";            // a String to hold direction of motor rotaged
int angle = 0;                    // a String to hold angle to rotation
bool stringComplete = false;      // whether the string is complete

byte pattern[] = {
  0B1000,
  0B0100,
  0B0010,
  0B0001
};
void send2port(byte data);
void setup()
{
  Serial.begin(19200);            // if 9600 when send data to serial port delay over 30ms.
  inputString.reserve(200);       // reserve 200 bytes for the inputString:
  for (i = 0; i < 4; i++)
  {
    pinMode(PinCon[i], OUTPUT);
  }
  i = 0;
}
void loop()
{
  int x = inputString.length() - 1;                   //calculate length of incoming string
  if (stringComplete) {
    inputString = inputString.substring(0, x);        //delete '\n'
    command = inputString.substring(0, 4);            //detect command
    direction = inputString.substring(4, 5);          //detect direction
    angle = inputString.substring(5, x).toInt();      //detect angle
    if (command == "STM1")
    {
      stepcontrol();
    }
    inputString = "";                    // clear the string:
    stringComplete = false;
  }
  send2port(0B0000);
}

void stepcontrol(void)
{
  int stepm = map(angle, 0, 360, 0, 512);             //convert true angle to step control
  for ( j = 0; j < stepm; j++)
  {
    send2port(pattern[i]);
    Serial.print(map(j, 0, 512, 0, 360));             //Send angle return to computer
    Serial.println(",");
    delay(5);
    if (direction == "F")
      (i < 3) ? i++ : i = 0;
    else
      (i > 0) ? i-- : i = 3;
  }
  Serial.print(angle);                                //Send final angle return to computer
}
void send2port(byte data)
{
  digitalWrite(PinCon[0], (data & 1 ) ? HIGH : LOW);
  digitalWrite(PinCon[1], (data & 2 ) ? HIGH : LOW);
  digitalWrite(PinCon[2], (data & 4 ) ? HIGH : LOW);
  digitalWrite(PinCon[3], (data & 8 ) ? HIGH : LOW);
}
void serialEvent() {
  while (Serial.available()) {
    char inChar = (char)Serial.read();
    inputString += inChar;
    if (inChar == '\n') {
      stringComplete = true;
    }
  }
}

27. ผลการรัน
<a href="https://www.youtube.com/v/7IHtRPFcg2Q" target="_blank" class="new_win">https://www.youtube.com/v/7IHtRPFcg2Q</a>
« แก้ไขครั้งสุดท้าย: ตุลาคม 26, 2018, 10:11:32 PM โดย admin »

admin

  • Administrator
  • Hero Member
  • *****
  • กระทู้: 706
    • ดูรายละเอียด
    • อีเมล์

admin

  • Administrator
  • Hero Member
  • *****
  • กระทู้: 706
    • ดูรายละเอียด
    • อีเมล์