参考

https://blog.csdn.net/weixin_43272272/article/details/107836477

硬件接线




ZD-42S驱动器接线图


代码

#include <Arduino_FreeRTOS.h>
#include <queue.h>  //队列在这里很重要,串口接收的控制指令会分配给队列,其他任务再从队列里获取,避免多任务同时访问一个数据
/************定义4个任务,一个串口收发任务,3个电机任务**************/
void TaskSerial( void *pvParameters );//任务名称可以自己定
void Task_servo1( void *pvParameters );
void Task_servo2( void *pvParameters );
void Task_servo3( void *pvParameters );QueueHandle_t  a_dir, a_num,  //定义servo1的指令的队列,a_dir是控制电机方向,a_num是控制电机步数b_dir, b_num, c_dir, c_num;void setup()
{Serial.begin(115200, SERIAL_8N1);/************定义各电机的控制指令队列**************//*..Y..*/a_dir = xQueueCreate( 1, sizeof( char ) );//队列数据类型定义,1是队列长度,char是Yc的数据类型a_num = xQueueCreate( 1, sizeof( long ) );/*..ZX..*/b_dir = xQueueCreate( 1, sizeof( char ) );b_num = xQueueCreate( 1, sizeof( long ) );/*..YX..*/c_dir = xQueueCreate( 1, sizeof( char ) );c_num = xQueueCreate( 1, sizeof( long ) );/************创建4个任务(也可以在任务中创建任务),一个串口收发任务,3个电机任务**************/xTaskCreate(TaskSerial, "Serial" , 256 , NULL, 2 , NULL );//“Serial”是任务名称,256是该任务分配的存储空间,优先级为2(数字越大优先级越高)xTaskCreate(Task_servo1, "a_servo" , 128 , NULL, 1 , NULL );xTaskCreate(Task_servo2, "b_servo" , 128 , NULL, 1 , NULL );xTaskCreate(Task_servo3, "c_servo" , 128 , NULL, 1 , NULL );vTaskStartScheduler(); //启动任务调度}void loop()
{}/*---------------------- Tasks Serial---------------------*/
void TaskSerial(void *pvParameters)
{Serial.println("二级任务"); (void) pvParameters;//下面写串口任务需要的初始化参数char buffer1[22] = " ";//这个用来储存串口数据char data1 = ' ';//这个用来储存电机判断指令char data2 = ' ';//这个用来储存电机方向判断指令long number = 0;//这个用来储存电机运行步数for (;;){/*---------------------接收数据---------------------*/if (Serial.available() > 0) //说明串口接收到数据{Serial.println("接收到数据");data1 = Serial.read();//串口接收指令,将第一个字符给data1,(用来判断需要控制哪电机)data2 = Serial.read();//串口接收指令,将第2个字符给data2,(用来判断电机方向)Serial.readBytes(buffer1, 22);//串口接收指令,将剩余字符数组buffer1,(用来储存电机运行步数),22是读取的字节数是22/****servo1****/if (data1 == 'a'){data1= ' ';number = atol(buffer1);//将数组buffer1的值强制转换为long型,把buffer1储存的步数给numberxQueueSend( a_num, &number, 1);//把步数发给a_numnumber = 0;xQueueSend( a_dir, &data2, 1);//把方向发给a_dirdata2= ' ';}/****servo2****/if (data1 == 'b'){Serial.println("进入b选择");data1= ' ';number = atol(buffer1);xQueueSend( b_num, &number, 1);//Serial.println("number");//Serial.println(number);number = 0;xQueueSend( b_dir, &data2, 1);//Serial.println("data2");//Serial.println(data2);data2= ' ';}/****servo3****/if (data1 == 'c'){data1= ' ';number = atol(buffer1);xQueueSend( c_num, &number, 1);number = 0;xQueueSend( c_dir, &data2, 1);data2 = ' ';}/****清空缓冲****/for (int k = 0; k < 22; k++){buffer1[k] = ' ';}}vTaskDelay(3); // 等待1ms,避免任务拥挤}
}/*------------------------------------------servo1----------------------------------------- */
void Task_servo1(void *pvParameters)
{(void) pvParameters;/*----------- servo1电机任务初始化配置 -----------*/char a_data = ' ';//这是电机方向数据,它会去队列中获取方向数据long a_number = 0;//这是电机步数数据,它会去队列中获取步数数据long a_cp = 0;//这个是电机步数对比值,它与a_number对比  /*-----------servo1引脚接线-----------*/pinMode(22, OUTPUT);    //DIR,电机驱动器DIR接Arduino 2560的D22,后面接线照着接pinMode(23, OUTPUT);    //CPpinMode(24, OUTPUT);    //ENdigitalWrite(24, LOW);  //EN     为了使电机不发热,先脱机for (;;){if (xQueueReceive(a_dir,&a_data,3) )//判断队列a-dir是否有数据进来,并获取数据(方向数据)给a_data{xQueueReceive( a_num, &a_number, 3);//获取电机步数a_num的数据给a_number,因为串口任务里先发送a_num数据,再发送a_dir数据,所以用a_dir判断数据是否进来,a-dir数据进来,则a_num数据已进来if (a_data == 'f')//电机顺时针{digitalWrite(24, HIGH);     //电机使能digitalWrite(22, LOW);      //电机顺时针while (a_cp < a_number)//电机走Ynumber个步数{digitalWrite(23, HIGH);vTaskDelay( 0.001 );digitalWrite(23, LOW);vTaskDelay( 0.001  );a_cp++;}a_cp = 0;//对比数据清零}if (a_data == 'i')//电机逆时针{digitalWrite(24, HIGH);     digitalWrite(22, HIGH);      //电机逆时针while (a_cp < a_number){digitalWrite(23, HIGH);vTaskDelay(0.001);digitalWrite(23, LOW);vTaskDelay(0.001);a_cp++;}a_cp = 0;}/*------------复位------------*/if (a_data == 'F'){digitalWrite(24, LOW);  //EN     }}digitalWrite(24, LOW);vTaskDelay( 0.03); // 等待1ms}
}/*------------------------------------------servo2-----------------------------------------*/
void Task_servo2(void *pvParameters)  // This is a task.
{(void) pvParameters;char b_data = ' ';long b_number = 0;long b_cp = 0;/*----------- servo2引脚接线 -----------*/pinMode(26, OUTPUT);    //DIRpinMode(27, OUTPUT);    //CPpinMode(28, OUTPUT);    //ENdigitalWrite(28, LOW);  //EN OFFfor (;;){if (xQueueReceive( b_dir, &b_data, 3) ){xQueueReceive( b_num, &b_number, 3);Serial.println(b_data);if (b_data == 'f'){digitalWrite(28, HIGH);     //ENdigitalWrite(26, LOW);      //电机顺时针while (b_cp < b_number){digitalWrite(27, HIGH);vTaskDelay( 0.001  );digitalWrite(27, LOW);vTaskDelay( 0.001 );b_cp++;//Serial.println(b_cp);}b_cp = 0;}if (b_data == 'i'){digitalWrite(28, HIGH);     //ENdigitalWrite(26, HIGH);      //电机逆时针while (b_cp < b_number){digitalWrite(27, HIGH);vTaskDelay( 0.001 );digitalWrite(27, LOW);vTaskDelay( 0.001 );b_cp++;}b_cp = 0;}/*------------复位------------*/if (b_data == 'F'){digitalWrite(28, LOW);  //EN}}digitalWrite(28, LOW);vTaskDelay(0.03); // 等待2ms}
}/*------------------------------------------servo3-----------------------------------------*/
void Task_servo3(void *pvParameters)  // This is a task.
{(void) pvParameters;char c_data = ' ';long c_number = 0;long c_cp = 0;/*----------- servo3引脚接线 -----------*/pinMode(30, OUTPUT);    //DIRpinMode(31, OUTPUT);    //CPpinMode(32, OUTPUT);    //ENdigitalWrite(32, LOW);  //EN OFFfor (;;){if (xQueueReceive( c_dir, &c_data, 3) ){xQueueReceive( c_num, &c_number, 3);if (c_data == 'f'){digitalWrite(32, HIGH);     //ENdigitalWrite(30, LOW);      //远离电机端while (c_cp < c_number){digitalWrite(31, HIGH);vTaskDelay( 3 / portTICK_PERIOD_MS );digitalWrite(31, LOW);vTaskDelay( 3 / portTICK_PERIOD_MS );c_cp++;}c_cp = 0;}if (c_data == 'i'){digitalWrite(32, HIGH);     //ENdigitalWrite(30, HIGH);      //靠近电机端while (c_cp < c_number){digitalWrite(31, HIGH);vTaskDelay( 3 / portTICK_PERIOD_MS );digitalWrite(31, LOW);vTaskDelay( 3 / portTICK_PERIOD_MS );c_cp++;}c_cp = 0;}/*------------复位------------*/if (c_data == 'F'){digitalWrite(32, LOW);  //EN}}digitalWrite(32, LOW);vTaskDelay(3); // 等待2ms}
}

注意

1

驱动器的细分数要选择合适,不能太小,太小的话,电机转起来就会一顿一顿的。另外,细分数和程序里驱动电机发脉冲的时候的延时时间有关,细分数越大,延时时间越短。延时过短,电机不转并且发出尖锐的声音,说明脉冲频率过高,此时要增大延时时间;延时过长,电机转动很慢。

2

调试的时候,通过串口给板子发信号

即点右上角的图案。输入的脉冲数要大一点,不然有的时候电机一下就转完了还发现不了,以为是程序问题。

代码更新

最终版,用在跟ros通讯联合调试三并联平台之中

#include <Arduino_FreeRTOS.h>
#include <ros.h>
#include <std_msgs/Float32MultiArray.h>
#include <queue.h>  //队列在这里很重要,串口接收的控制指令会分配给队列,其他任务再从队列里获取,避免多任务同时访问一个数据
/************定义4个任务,一个串口收发任务,3个电机任务**************/
void TaskSerial( void *pvParameters );//任务名称可以自己定
void Task_servo1( void *pvParameters );
void Task_servo2( void *pvParameters );
void Task_servo3( void *pvParameters );char a_data = 'q';//这是电机方向数据,
long a_number = 0;//这是电机步数数据,
char b_data = 'q';
long b_number = 0;
char c_data = 'q';
long c_number = 0;ros::NodeHandle  nh;void servoCb( const std_msgs::Float32MultiArray &angle_msg)  //angle_msg其实是脉冲数
{Serial.println("进入回调函数");if (angle_msg.data[0] == 0){a_data = 'q'; a_number = 0;}if (angle_msg.data[0] > 0){a_data = 'i'; a_number = long(angle_msg.data[0]);}if (angle_msg.data[0] < 0){a_data = 'f'; a_number = long(-angle_msg.data[0]);}
// ------------------------------------------------if (angle_msg.data[1] == 0){b_data = 'q'; b_number = 0;}if (angle_msg.data[1] > 0){b_data = 'i'; b_number = long(angle_msg.data[1]);}if (angle_msg.data[1] < 0){b_data = 'f'; b_number = long(-angle_msg.data[1]);}
// ---------------------------------------------------------if (angle_msg.data[2] == 0){c_data = 'q'; c_number = 0;}if (angle_msg.data[2] > 0){c_data = 'i'; c_number = long(angle_msg.data[2]);}if (angle_msg.data[2] < 0){c_data = 'f'; c_number = long(-angle_msg.data[2]);}}ros::Subscriber<std_msgs::Float32MultiArray> sub_angle("/servo_angle", servoCb);void setup()
{Serial.begin(115200, SERIAL_8N1);nh.initNode();nh.subscribe(sub_angle);/************创建4个任务(也可以在任务中创建任务),一个串口收发任务,3个电机任务**************/xTaskCreate(TaskSerial, "Serial" , 256 , NULL, 2 , NULL );xTaskCreate(Task_servo1, "a_servo" , 128 , NULL, 1 , NULL );xTaskCreate(Task_servo2, "b_servo" , 128 , NULL, 1 , NULL );xTaskCreate(Task_servo3, "c_servo" , 128 , NULL, 1 , NULL );vTaskStartScheduler(); //启动任务调度}void loop()
{}void TaskSerial(void *pvParameters)
{(void) pvParameters;for(;;){nh.spinOnce();vTaskDelay( 25 / portTICK_PERIOD_MS ); // 等待3ms}}/*------------------------------------------servo1----------------------------------------- */
void Task_servo1(void *pvParameters)
{(void) pvParameters;/*----------- servo1电机任务初始化配置 -----------*/long a_cp = 0;//这个是电机步数对比值,它与a_number对比  /*-----------servo1引脚接线-----------*/pinMode(22, OUTPUT);    //DIR,电机驱动器DIR接Arduino 2560的D22,后面接线照着接pinMode(23, OUTPUT);    //CPpinMode(24, OUTPUT);    //ENdigitalWrite(24, HIGH);  //EN  for (;;){if (a_data == 'f')//电机顺时针{digitalWrite(22, LOW);      //电机顺时针while (a_cp < a_number)//电机走Ynumber个步数{digitalWrite(23, HIGH);vTaskDelay( 2 / portTICK_PERIOD_MS );digitalWrite(23, LOW);vTaskDelay( 2 / portTICK_PERIOD_MS  );a_cp++;}a_cp = 0;//对比数据清零a_data= 'q';}if (a_data == 'i')//电机逆时针{digitalWrite(24, HIGH);     digitalWrite(22, HIGH);      //电机逆时针while (a_cp < a_number){digitalWrite(23, HIGH);vTaskDelay(2 / portTICK_PERIOD_MS);digitalWrite(23, LOW);vTaskDelay(2 / portTICK_PERIOD_MS);a_cp++;}a_cp = 0;a_data='q';}if (a_data == 'q'){digitalWrite(24, HIGH);  //EN}vTaskDelay( 3 / portTICK_PERIOD_MS ); // 等待3ms}}/*------------------------------------------servo2-----------------------------------------*/
void Task_servo2(void *pvParameters)  // This is a task.
{(void) pvParameters;long b_cp = 0;/*----------- servo2引脚接线 -----------*/pinMode(26, OUTPUT);    //DIRpinMode(27, OUTPUT);    //CPpinMode(28, OUTPUT);    //ENdigitalWrite(28, HIGH);  //EN onfor (;;){if (b_data == 'f'){digitalWrite(26, LOW);      //电机顺时针while (b_cp < b_number){digitalWrite(27, HIGH);vTaskDelay( 2 / portTICK_PERIOD_MS  );digitalWrite(27, LOW);vTaskDelay( 2 / portTICK_PERIOD_MS );b_cp++;b_data='q';}b_cp = 0;}if (b_data == 'i'){digitalWrite(26, HIGH);      //电机逆时针while (b_cp < b_number){digitalWrite(27, HIGH);vTaskDelay( 2 / portTICK_PERIOD_MS );digitalWrite(27, LOW);vTaskDelay( 2 / portTICK_PERIOD_MS );b_cp++;}b_cp = 0;b_data='q';}if (b_data == 'q'){digitalWrite(28, HIGH);  //EN on}vTaskDelay( 3 / portTICK_PERIOD_MS ); // 等待3ms}
}/*------------------------------------------servo3-----------------------------------------*/
void Task_servo3(void *pvParameters)  // This is a task.
{(void) pvParameters;long c_cp = 0;/*----------- servo3引脚接线 -----------*/pinMode(30, OUTPUT);    //DIRpinMode(31, OUTPUT);    //CPpinMode(32, OUTPUT);    //ENdigitalWrite(32, HIGH);  //EN onfor (;;){if (c_data == 'f'){digitalWrite(30, LOW);      //远离电机端while (c_cp < c_number){digitalWrite(31, HIGH);vTaskDelay( 2 / portTICK_PERIOD_MS );digitalWrite(31, LOW);vTaskDelay( 2 / portTICK_PERIOD_MS );c_cp++;}c_cp = 0;c_data='q';}if (c_data == 'i'){digitalWrite(30, HIGH);      //靠近电机端while (c_cp < c_number){digitalWrite(31, HIGH);vTaskDelay( 2 / portTICK_PERIOD_MS );digitalWrite(31, LOW);vTaskDelay( 2 / portTICK_PERIOD_MS );c_cp++;}c_cp = 0;c_data='q';}if (c_data == 'q'){digitalWrite(32, HIGH);  //EN on}vTaskDelay(3 / portTICK_PERIOD_MS); // 等待2ms}
}

说明:
每次指令是24位,每个电机都是8位;
1号电机逆时针转1000个脉冲,2 3号电机不转,指令:100010000000000000000000

Mega2560同时控制三个步进电机相关推荐

  1. 51单片机控制二相四线步进电机

    步进电机按定子上绕组来分,有二相.三相和五相等系列.最受欢迎的是两相混合式步进电机,约占97%以上的市场份额,其原因是性价比高,配上细分驱动器后效果良好.该种电机的基本步距角为1.8°/步,配上半步驱 ...

  2. 单片机控制两个步进电机画圆_基于单片机的步进电机转速控制设计详解(附程序)...

    步进电机是将电脉冲信号转变为角位移或线位移的开环控制元件.在非超载的情况下,电机的转速.停止的位置只取决于脉冲信号的频率和脉冲数,而不受负载变化的影响,即给电机加一个脉冲信号,电机则转过一个步距角.这 ...

  3. Arduino从零开始(2)——控制舵机与步进电机

    0.前言 本文主要介绍通过Arduino控制舵机,步进电机以及循环的使用 目录 0.前言 1.介绍 2.Arduino控制舵机: 2.1方法一 2.2方法二 3.Arduino控制步进电机 1.介绍 ...

  4. 【2018】使用CD4013,CD4017,CD4053模拟三项步进电机

    想必大家小时候都玩过磁铁,两个磁铁的同极靠近彼此产生一个斥力,两个磁铁的异极靠近彼此,产生一个引力,步进电机用的便是这个原理,只是将其中的一个磁铁换成电磁铁,然后将磁铁们做成弧形,最终形成一个圆筒,三 ...

  5. 单片机两个IO口控制三个LED灯

    在项目中经常会遇到单片机IO口资源不够用的情况,那么如何让单片机的IO口利用最大化呢,这里分享一下用单片机两个IO口控制三个LED灯的几种方法. 方法一:      S1和S2分别接单片机两个IO口, ...

  6. ARDUINO:控制两台步进电机同步运转

    要控制两台步进电机同步进行运转,即同时起步,同时加速,同时到达最大速度,同时减速,最后同时停止. 这是要有一定的算法的: 假如B1电机所走的路程是B2的n倍,在同时起步的前提下,v1=n*v2,a1= ...

  7. 89c52单片机控制两个步进电机正反转加减速(程序+仿真)

    89c52单片机控制两个步进电机正反转加减速(程序+仿真) 先上图: 源代码: /*********************************************************** ...

  8. 树莓派产生PWM控制伺服电机和步进电机踩坑

    树莓派产生PWM控制伺服电机和步进电机踩坑 本人最近在做项目当中,涉及到利用PWM对伺服电机(servo motor)和步进电机(step motor)的控制.分享下所写的代码,以及遇到的问题,希望可 ...

  9. 汽车动力学与控制-三个坐标系与运动学方程

    汽车动力学与控制-三个坐标系与运动学方程 观看B站up"忠厚老实的老王"学习视频所作笔记 在汽车中常用的几个控制算法:PID/MPC/LQR 其中MPC控制效果最优,但计算量庞大, ...

最新文章

  1. 1066 Root of AVL Tree 需再做
  2. int **a[3][4] 和 sizeof(a) 和 int(**)a[3][4]
  3. linux的tmp文件夹定期会删除么,关于linux tmp下文件自动删除的问题
  4. Selenium2 Python 自己主动化測试实战学习笔记(五)
  5. JS-以鼠标位置为中心的滑轮放大功能demo1
  6. [HAOI2010]计数 数位DP+组合数
  7. 安全强化你的 Linux 服务器的七个步骤
  8. date在java的那个包_Java—包装类、Date和SimpleDateFormat、Calendar类
  9. uniapp动态修改元素节点样式
  10. Allatori java代码混淆
  11. 天正网络版修改服务器地址,修改天正网络版服务器地址
  12. html5 |移动 游戏 毕业设计,移动平台下基于HTML5的教育游戏研究与设计
  13. 天线基础知识(三)天线增益
  14. 分享一个边看视频就能边练口语的学习网站,超实用,不仅仅能学英语
  15. 8款精致的纯CSS3按钮特效
  16. 芜湖市市一中2021年高考成绩查询,2021年芜湖各高中录取分数线预测
  17. asp.net WEB 调用 CuteFTP8.0 ftpte.exe 实现大文件上传
  18. meta标签中的http-equiv属性
  19. 计算机中央控制单元是由什么组成,电子控制单元由什么组成
  20. 家庭装修是找设计师和施工队好,还是找装修公司好呀

热门文章

  1. 搭建自己的智能家居系统
  2. 对未来计算机的期盼,期待明天内涵励志说说 对未来充满希望的励志说说
  3. 【第四篇:解决校招面试中的测试设计题目】
  4. 计算机应用基础问卷调查,计算机应用基础调查问卷.docx
  5. SourceMap基本使用
  6. Ubuntu服务器访问PHP文件显示该网页无法正常运作,目前无法处理此请求HTTP ERROR 500?
  7. 关于麦肯锡、埃森哲、IBM、普华永道的笑话
  8. java 老年代 占用空间_JVM之堆内存(年经代,老年代)
  9. 前端性能监控技术方案
  10. 计算机ping使用的端口,如何用ping来测试端口(port)?