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