Arduino RTOS控制多个步进电机

Arduino RTOS控制多个步进电机

材料;
1、arduino mega2560
2、3个M42S驱动器
3、3个42步进电机
4、24V电源

在这里插入图片描述

/3个电机分别是Y轴,ZX轴(左边X轴),YX轴(右边X轴)/
#include <Arduino_FreeRTOS.h>
#include <queue.h>  //队列在这里很重要,串口接收的控制指令会分配给队列,其他任务再从队列里获取,避免多任务同时访问一个数据
/************定义4个任务,一个串口收发任务,3个电机任务**************/
void TaskSerial( void *pvParameters );//任务名称可以自己定
void Task_Y( void *pvParameters );
void Task_ZX( void *pvParameters );
void Task_YX( void *pvParameters );

QueueHandle_t  Yc,  Yn,  //定义Y轴电机的指令的队列,Yc是控制电机方向,Yn是控制电机步数,下面是另外两个电机的指令
               ZXc,  ZXn,
               YXc,  YXn;

void setup()
{
  Serial.begin(115200, SERIAL_8N1);
  
/************定义各电机的控制指令队列**************/
  /*..Y..*/
  Yc = xQueueCreate( 1, sizeof( char ) );//队列数据类型定义,1是队列长度,char是Yc的数据类型
  Yn = xQueueCreate( 1, sizeof( long ) );

  /*..ZX..*/
  ZXc = xQueueCreate( 1, sizeof( char ) );
  ZXn = xQueueCreate( 1, sizeof( long ) );

  /*..YX..*/
  YXc = xQueueCreate( 1, sizeof( char ) );
  YXn = xQueueCreate( 1, sizeof( long ) );
  
/************创建4个任务(也可以在任务中创建任务),一个串口收发任务,3个电机任务**************/
  xTaskCreate(TaskSerial, "Serial" , 256 , NULL, 2 , NULL );//“Serial”是任务名称,256是该任务分配的存储空间,优先级为2(数字越大优先级越高)
  xTaskCreate(Task_Y, "Y" , 128 , NULL, 1 , NULL );
  xTaskCreate(Task_ZX, "ZX" , 128 , NULL, 1 , NULL );
  xTaskCreate(Task_YX, "YX" , 128 , NULL, 1 , NULL );

}

void loop()
{}

/*---------------------- Tasks Serial---------------------*/
void TaskSerial(void *pvParameters)
{
  (void) pvParameters;
  //下面写串口任务需要的初始化参数
  char buffer1[22] = " ";//这个用来储存串口数据
  char data1 = 'F';//这个用来储存电机判断指令
  char data2 = ' ';//这个用来储存电机方向判断指令
  long number = 0;//这个用来储存电机运行步数
 /*---------------------初始化复位电机(需要限位开关)---------------------*/
    if (data1 == 'F')
    {
      xQueueSend( Yc, &data1, 1);
      xQueueSend( ZXc, &data1, 1);
      xQueueSend( YXc, &data1, 1);
      data1 = ' ';
    }
    
  for (;;)
  {
    /*---------------------接收数据---------------------*/
    if (Serial.available() > 0)
    {
       data1 = Serial.read();//串口接收指令,将第一个字符给data1,(用来判断需要控制哪电机)
      data2 = Serial.read();//串口接收指令,将第2个字符给data2,(用来判断电机方向)
      Serial.readBytes(buffer1, 22);//串口接收指令,将剩余字符数组buffer1,(用来储存电机运行步数)
//例如电脑发送“YQ5000”给Arduino,Y会给data1,Q会给到data2,5000会给到buffer1,这样电机Y会顺时针方向走5000步
      /****复位****/
      if (data1 == 'F')//电脑发送“F”,进入复位操作
      {
        xQueueSend( Yc, &data1, 1);//将data1的数据"F"分别发给Yc,ZXc,YXc,电机都往限位开关一边转
        xQueueSend( ZXc, &data1, 1);
        xQueueSend( YXc, &data1, 1);
        data1 = ' ';
      }
     
      /****Y轴****/
      if (data1 == 'Y')
      {
        number = atol(buffer1);//将数组buffer1的值强制转换为long型,把buffer1储存的步数给number
        xQueueSend( Yn, &number, 1);//把步数发给Yn
        xQueueSend( Yc, &data2, 1);//把方向发给Yc
        number = 0;//清空步数信息
      }

      /****ZX轴****/
      if (data1 == 'X')
      {
        number = atol(buffer1);
        xQueueSend( ZXn, &number, 1);
        xQueueSend( ZXc, &data2, 1);
        number = 0;
      }

      /****YX轴****/
      if (data1 == 'x')
      {
        number = atol(buffer1);
        xQueueSend( YXn, &number, 1);
        xQueueSend( YXc, &data2, 1);
        number = 0;
      }

      /****清空缓冲****/
      for (int k = 0; k < 22; k++)
      {
        buffer1[k] = ' ';
      }
      data1 = ' ';
      data2 = ' ';
    }
    
    vTaskDelay(1); //等待15sm,避免任务拥挤
  }
}

/*------------------------------------------Y轴----------------------------------------- */
void Task_Y(void *pvParameters)  // Y轴电机任务
{
  (void) pvParameters;
    /*----------- Y轴电机任务初始化配置 -----------*/
  char Ydata = ' ';//这是电机方向数据,它会去队列中获取方向数据
  long Ynumber = " ";//这是电机步数数据,它会去队列中获取步数数据
  long Ycp = 0;//这个是电机步数对比值,它与Ynumber对比  


  pinMode(22, OUTPUT);    //DIR,电机驱动器DIR接Arduino 2560的D22,后面接线照着接
  pinMode(23, OUTPUT);    //CP
  pinMode(24, OUTPUT);    //EN
  digitalWrite(24, LOW);  //EN     为了使电机不发热,先脱机
  pinMode(25, INPUT_PULLUP);  // Y轴电机限位开关(限位开关一抽头接D25,另一抽头接GND,可以在D25接一个上拉电阻,或者在D25接一个电容到GND来 防止抖动)

  for (;;)
  {

    if (xQueueReceive( Yc, &Ydata, 3) )//判断队列Yc是否有数据进来,并获取数据(方向数据)给Ydata
    {
      xQueueReceive( Yn, &Ynumber, 3);//获取电机步数Yn的数据给Ynumber,因为串口任务里先发送Yn数据,再发送Yc数据,所以用Yc判断数据是否进来,Yc数据进来,则Yn数据已进来

      if (Ydata == 'Q')//电机顺时针
      {
        digitalWrite(24, HIGH);     //电机使能
        digitalWrite(22, LOW);      //电机顺时针
        while (Ycp < Ynumber)//电机走Ynumber个步数
        {
          digitalWrite(23, HIGH);
          vTaskDelay( 0.03 / portTICK_PERIOD_MS );
          digitalWrite(23, LOW);
          vTaskDelay( 0.03 / portTICK_PERIOD_MS );
          Ycp++;
        }
        Ycp = 0;//对比数据清零
     
      }
      if (Ydata == 'H')//电机逆时针
      {
        digitalWrite(24, HIGH);     
        digitalWrite(22, HIGH);      //电机逆时针
        while (Ycp < Ynumber)
        {
          digitalWrite(23, HIGH);
          vTaskDelay( 0.03 / portTICK_PERIOD_MS );
          digitalWrite(23, LOW);
          vTaskDelay( 0.03 / portTICK_PERIOD_MS );
          Ycp++;
        }
        Ycp = 0;
    
      }

      /*------------复位------------*/
      if (Ydata == 'F')
      {
        digitalWrite(24, HIGH);  //EN
        digitalWrite(22, HIGH);  //靠近电机端
        while (digitalRead(25) == 1)      //电机复位,直到碰到限位开关
        {
          digitalWrite(23, LOW);
          vTaskDelay( 0.03 / portTICK_PERIOD_MS );
          digitalWrite(23, HIGH);
          vTaskDelay( 0.03 / portTICK_PERIOD_MS );
        }
      }

      digitalWrite(24, LOW);
    }
    digitalWrite(24, LOW);
    vTaskDelay( 1  );
  }

}

/*------------------------------------------ZX轴(左边X轴)-----------------------------------------*/
void Task_ZX(void *pvParameters)  // This is a task.
{
  (void) pvParameters;
  char ZXdata = ' ';
  long ZXnumber = " ";
  long ZXcp = 0;

  /*----------- ZX -----------*/
  pinMode(26, OUTPUT);    //DIR
  pinMode(27, OUTPUT);    //CP
  pinMode(28, OUTPUT);    //EN
  digitalWrite(28, LOW);  //EN OFF

  pinMode(29, INPUT_PULLUP);  // 限位开关
  for (;;)
  {
    if (xQueueReceive( ZXc, &ZXdata, 3) )
    {
      xQueueReceive( ZXn, &ZXnumber, 3);

      if (ZXdata == 'A')
      {
        digitalWrite(28, HIGH);     //EN
        digitalWrite(26, LOW);      //远离电机端
        while (ZXcp < ZXnumber)
        {
          digitalWrite(27, HIGH);
          vTaskDelay( 0.05 / portTICK_PERIOD_MS );
          digitalWrite(27, LOW);
          vTaskDelay( 0.05 / portTICK_PERIOD_MS );
          ZXcp++;
        }
        ZXcp = 0;

      }
      if (ZXdata == 'a')
      {
        digitalWrite(28, HIGH);     //EN
        digitalWrite(26, HIGH);      //靠近电机端
        while (ZXcp < ZXnumber)
        {
          digitalWrite(27, HIGH);
          vTaskDelay( 0.05 / portTICK_PERIOD_MS );
          digitalWrite(27, LOW);
          vTaskDelay( 0.05 / portTICK_PERIOD_MS );
          ZXcp++;
        }
        ZXcp = 0;

      }

      /*------------复位------------*/
      if (ZXdata == 'F')
      {
        digitalWrite(28, HIGH);  //EN
        digitalWrite(26, HIGH);  //靠近电机端
        while (digitalRead(29) == 1)      //左右-复位
        {
          digitalWrite(27, LOW);
          vTaskDelay( 0.05 / portTICK_PERIOD_MS );
          digitalWrite(27, HIGH);
          vTaskDelay( 0.05 / portTICK_PERIOD_MS );
        }
     
      }

      digitalWrite(28, LOW);
    }
    digitalWrite(28, LOW);
    vTaskDelay( 1  );
  }

}

/*------------------------------------------YX轴(右边X轴)-----------------------------------------*/
void Task_YX(void *pvParameters)  // This is a task.
{
  (void) pvParameters;
  char YXdata = ' ';
  long YXnumber = " ";
  long YXcp = 0;
   
  /*----------- YX -----------*/
  pinMode(30, OUTPUT);    //DIR
  pinMode(31, OUTPUT);    //CP
  pinMode(32, OUTPUT);    //EN
  digitalWrite(32, LOW);  //EN OFF

  pinMode(33, INPUT_PULLUP);  // 限位开关
  for (;;)
  {
    if (xQueueReceive( YXc, &YXdata, 3) )
    {
      xQueueReceive( YXn, &YXnumber, 3);

      if (YXdata == 'B')
      {
        digitalWrite(32, HIGH);     //EN
        digitalWrite(30, LOW);      //远离电机端
        while (YXcp < YXnumber)
        {
          digitalWrite(31, HIGH);
          vTaskDelay( 0.04 / portTICK_PERIOD_MS );
          digitalWrite(31, LOW);
          vTaskDelay( 0.04 / portTICK_PERIOD_MS );
          YXcp++;
        }
        YXcp = 0;
      }
      if (YXdata == 'b')
      {
        digitalWrite(32, HIGH);     //EN
        digitalWrite(30, HIGH);      //靠近电机端
        while (YXcp < YXnumber)
        {
          digitalWrite(31, HIGH);
          vTaskDelay( 0.04 / portTICK_PERIOD_MS );
          digitalWrite(31, LOW);
          vTaskDelay( 0.04 / portTICK_PERIOD_MS );
          YXcp++;
        }
        YXcp = 0;
      }

      /*------------复位------------*/
      if (YXdata == 'F')
      {
        digitalWrite(32, HIGH);  //EN
        digitalWrite(30, HIGH);  //靠近电机端
        while (digitalRead(33) == 1)      //左右-复位
        {
          digitalWrite(31, LOW);
          vTaskDelay( 0.04 / portTICK_PERIOD_MS );
          digitalWrite(31, HIGH);
          vTaskDelay( 0.04 / portTICK_PERIOD_MS );
        }
      }

      digitalWrite(32, LOW);
    }
    digitalWrite(32, LOW);
    vTaskDelay( 1  );
  }

}

通讯方法:上位机发送"YQ1234"-Y轴电机转1234步,发送“YH1234”-Y轴电转1234步,
上位机发送"XA1234"-ZX轴电机转1234步,发送“Xa1234”-ZX轴电转1234步,
上位机发送"xB1234"-YX轴电机转1234步,发送“xb1234”-YX轴电转1234步,
上位机发送"F"所有电机复位,直到触发限位开关停止
先了解FreeRTOS
有不懂可以直接发问题到邮箱ruirong_chen@qq.com

  • 9
    点赞
  • 76
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

C_RR

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值