该方案引导大家如何设计或使用一款控制板来实现直流有刷电机的XY轴控制,水泵流量的控制,料盒的控制和自动下料控制。

1,设计的理念
在自动化控制中,我们用步进电机、伺服电机、无刷电机来实现各种运动控制,这些电机通常可以达到很精确的位移控制。但是其缺点也较为明显:电机和控制器的成本比较昂贵。
那么在一些对精度要求不高的地方是不是可以用简单的直流有刷电机来实现呢,当然也是可以的,这样可以节省大量的成本。
同时控制板可以支持Modbus RTU通信协议,这样通过串口就可以任意控制电机了,哈哈,是不是很好玩?

2,控制方案功能简介
该控制卡用于控制和驱动两路执行机构,其执行机构可以为带Hall编码器的直流电机、水泵流量控制系统、料盒自动下料机构等。用户可以通过Modbus RTU通信协议配置控制卡的两路中任意一路的运行模式、工作使能、转速和刹车。
控制卡可以独立配置两路中任意一路执行机构的工作模式:

a,线性往返模式,该模式需要带霍尔转速检测的直流减速电机作为执行机构。通过复位、校准、工作使能等操作,只要用户通过Modbus
RTU写入目的寄存器数据,则执行机构自动运行到指定的目的位置。使系统达到类似步进电机的控制效果。该模式通常用于线性XYZ轴的控制,机械自动化的机械臂,自动控制,机器人,智能小车等应用。
b,脉冲检测模式,该模式驱动直流执行部件运行指定的脉冲数量后,然后停机。a) 如水泵流量控制系统,水泵作为执行电机,流量计的脉冲作为脉冲检测,当需要水泵流出300ml液体,则工作使能后,向目的寄存器写入300ml对应的脉冲数量后,则水泵在输出300ml液体后自动停机。b) 如料盒下料控制系统,带霍尔转速检测的直流减速电机作为执行机构和脉冲检测单元,当需要出料50g量,则工作使能后,向目的寄存器写入50g料对应的脉冲数量后,则系统输出50g物料后自动停机。
c,持续正转模式,该模式下工作使能后电机开始持续正转,直到工作模式禁止后才刹车停机。
d,持续反转模式,该模式下工作使能后电机开始持续反转,直到工作模式禁止后才刹车停机。

3,XY结构部件搭建
下图为XY轴的十字形图片,其主要由齿条,齿轮(1模20齿),拖链线槽,直流蜗杆电机(带霍尔编码器),接近开关,光轴滑块和上下底板组成,大家可以根据图示自行组装完成。其位置检测主要通过霍尔编码器的脉冲来实现。其运行精度达到1%以内。

4,料盒自动下料的结构搭建
下图为料盒自动控制的组装图片,其主要由料盒,转接齿,直流减速电机(带霍尔编码器),和下底板组成,大家可以根据图示自行组装完成。其下料的精度可以达到5%以内。

5,水泵流量自动控制的结构搭建
下图为水泵流量控制器的组装图片,其主要由小水泵,涡轮流量计,和X轴(将XY轴拆掉一个Y轴,简单吧)部件组成,大家可以根据图示自行组装完成。其流量与流量计的精度有关。
温馨提示,流量计应该安装到出水口,且远离出水口,避免水锤效应和气泡,因为这些都会影响流量计的精度。

6,原理图
好了,上面讲了一大堆没用的东西,现在上传关键的原理图,大家看好了。
电源部分:
电机驱动部分:

Modbus RTU,485原理图

MCU外设,光耦隔离,接近传感器检测电路
7,软件代码-Modbus RTU部分读写代码

#include "main.h"
#include "M031Series.h"void AnswerProcess(uint8_t cmd);
void WriteProcess(void);void CmdDataProcess(void)
{uint8_t offset=0;uint8_t FrameLength=0;union { uint16_t udat;uint8_t buf[2];}umid;uint16_t GetCrc16=0;if((command.RxTimeOutEnable==1)&(command.RxTimeOutCounter>FrameModbusTimerInterval))  //100us*5=500us  //Cmd timeout ,data invalid.{for(offset=0;offset<64;offset++)command.RxBuf[offset]=0;command.RxCount=0;command.RxTimeOutEnable=0;           }else if((command.RxBuf[0]==SysRegister.Address)&&(command.RxCount>7)){if(command.RxBuf[1]==CmdRead){GetCrc16=GetCrcData(command.RxBuf,6);umid.buf[0]=command.RxBuf[6];umid.buf[1]=command.RxBuf[7];if(umid.udat==GetCrc16){umid.buf[0]=command.RxBuf[3];umid.buf[1]=command.RxBuf[2];command.StartAddress=umid.udat;command.OpeRegisterNum=command.RxBuf[5];AnswerProcess(CmdRead);}for(offset=0;offset<8;offset++)command.RxBuf[offset]=0;command.RxCount=0;command.RxTimeOutEnable=0;           }else if(command.RxBuf[1]==CmdWrite){command.OpeRegisterNum=command.RxBuf[5];FrameLength=9+command.OpeRegisterNum*2;if(command.RxCount>=FrameLength){GetCrc16=GetCrcData(command.RxBuf,(FrameLength-2));umid.buf[0]=command.RxBuf[FrameLength-2];umid.buf[1]=command.RxBuf[FrameLength-1];if(umid.udat==GetCrc16){umid.buf[0]=command.RxBuf[3];umid.buf[1]=command.RxBuf[2];command.StartAddress=umid.udat;WriteProcess();AnswerProcess(CmdWrite);}for(offset=0;offset<64;offset++)command.RxBuf[offset]=0;command.RxCount=0;command.RxTimeOutEnable=0;           }}}
}void AnswerProcess(uint8_t cmd)
{uint8_t offset=0;uint16_t i=0;
//      uint32_t mid=0;uint16_t CRC16=0;RS485Mode(1);if(cmd==CmdWrite){for(offset=0;offset<6;offset++) command.TxBuf[offset]=command.RxBuf[offset];CRC16=GetCrcData(command.TxBuf,offset);command.TxBuf[offset++]=CRC16;command.TxBuf[offset++]=CRC16>>8;for(i=0;i<offset;i++){UART0->DAT =command.TxBuf[i];UART_WAIT_TX_EMPTY(UART0);}for(i=0;i<offset;i++)command.TxBuf[i]=0;command.OpeRegisterNum=0;command.StartAddress=0;}if(cmd==CmdRead){command.TxBuf[offset++]=SysRegister.Address;command.TxBuf[offset++]=CmdRead;command.TxBuf[offset++]=command.OpeRegisterNum*2;for(i=command.StartAddress;i<(command.StartAddress+command.OpeRegisterNum);i++){switch(i){case BaseAddress:command.TxBuf[offset++]=0;command.TxBuf[offset++]=SysRegister.Address;break;case BaseBandRate:command.TxBuf[offset++]=0;command.TxBuf[offset++]=SysRegister.BandRateConfig;break;case BaseCh1Offset:command.TxBuf[offset++]=0;command.TxBuf[offset++]=MotorControl.M1LinearErrorOffset;break;case BaseCh2Offset:command.TxBuf[offset++]=0;command.TxBuf[offset++]=MotorControl.M2LinearErrorOffset;break;case BaseCh1CurrentLocationHByte:command.TxBuf[offset++]=MotorControl.M1CurrentBuffCount>>24;///256;command.TxBuf[offset++]=MotorControl.M1CurrentBuffCount>>16;//%256;break;case BaseCh1CurrentLocationLByte:command.TxBuf[offset++]=MotorControl.M1CurrentBuffCount>>8;///256;command.TxBuf[offset++]=MotorControl.M1CurrentBuffCount;//%256;break;case BaseCh1DestinationLocationHByte:command.TxBuf[offset++]=MotorControl.M1DestinationBuffCount>>24;///256;command.TxBuf[offset++]=MotorControl.M1DestinationBuffCount>>16;//%256;break;case BaseCh1DestinationLocationLByte:command.TxBuf[offset++]=MotorControl.M1DestinationBuffCount>>8;///256;command.TxBuf[offset++]=MotorControl.M1DestinationBuffCount;//%256;break;case BaseCh1Speed:command.TxBuf[offset++]=0;command.TxBuf[offset++]=SysRegister.M1Speed;break;case BaseCh1RunMode:command.TxBuf[offset++]=0;command.TxBuf[offset++]=SysRegister.M1RunMode;break;case BaseCh1Filter:command.TxBuf[offset++]=0;command.TxBuf[offset++]=SysRegister.M1Filter;break;case BaseCh1ResetCmd:command.TxBuf[offset++]=0;command.TxBuf[offset++]=SysRegister.M1ResetCmd;break;case BaseCh1CalibrationDataHByte:command.TxBuf[offset++]=MotorControl.M1CalibrationBuffCount>>24;///256;  high 2 bytescommand.TxBuf[offset++]=MotorControl.M1CalibrationBuffCount>>16;//%256;break;case BaseCh1CalibrationDataLByte:command.TxBuf[offset++]=MotorControl.M1CalibrationBuffCount>>8; //Low 2 butescommand.TxBuf[offset++]=MotorControl.M1CalibrationBuffCount;break;case BaseCh2CurrentLocationHByte:command.TxBuf[offset++]=MotorControl.M2CurrentBuffCount>>24;///256;command.TxBuf[offset++]=MotorControl.M2CurrentBuffCount>>16;//%256;break;case BaseCh2CurrentLocationLByte:command.TxBuf[offset++]=MotorControl.M2CurrentBuffCount>>8;///256;command.TxBuf[offset++]=MotorControl.M2CurrentBuffCount;//%256;break;case BaseCh2DestinationLocationHByte:command.TxBuf[offset++]=MotorControl.M2DestinationBuffCount>>24;///256;command.TxBuf[offset++]=MotorControl.M2DestinationBuffCount>>16;//%256;break;case BaseCh2DestinationLocationLByte:command.TxBuf[offset++]=MotorControl.M2DestinationBuffCount>>8;///256;command.TxBuf[offset++]=MotorControl.M2DestinationBuffCount;//%256;break;case BaseCh2Speed:command.TxBuf[offset++]=0;command.TxBuf[offset++]=SysRegister.M2Speed;break;case BaseCh2RunMode:command.TxBuf[offset++]=0;command.TxBuf[offset++]=SysRegister.M2RunMode;break;            case BaseCh2ResetCmd:command.TxBuf[offset++]=0;command.TxBuf[offset++]=SysRegister.M2ResetCmd;break;case BaseCh2CalibrationDataHByte:command.TxBuf[offset++]=MotorControl.M2CalibrationBuffCount>>24;command.TxBuf[offset++]=MotorControl.M2CalibrationBuffCount>>16;break;case BaseCh2CalibrationDataLByte:command.TxBuf[offset++]=MotorControl.M2CalibrationBuffCount>>8;command.TxBuf[offset++]=MotorControl.M2CalibrationBuffCount;break;case BaseCh1MotorBusy:command.TxBuf[offset++]=0;command.TxBuf[offset++]=MotorControl.M1MotorRunStop;break;case BaseCh2MotorBusy:command.TxBuf[offset++]=0;command.TxBuf[offset++]=MotorControl.M2MotorRunStop;break;default:command.TxBuf[offset++]=0;command.TxBuf[offset++]=0;break;}}CRC16=GetCrcData(command.TxBuf,offset);command.TxBuf[offset++]=CRC16;command.TxBuf[offset++]=CRC16>>8;for(i=0;i<offset;i++){UART0->DAT =command.TxBuf[i];UART_WAIT_TX_EMPTY(UART0);}for(i=0;i<offset;i++)command.TxBuf[i]=0;command.OpeRegisterNum=0;command.StartAddress=0;}RS485Mode(0);
}void WriteProcess(void)
{uint8_t offset=0;uint16_t i=0;offset=7;for(i=command.StartAddress;i<(command.StartAddress+command.OpeRegisterNum);i++){switch(i){case BaseAddress:offset++;SysRegister.Address=command.RxBuf[offset++];FlashNeedToUpdate=1;break;case BaseBandRate:offset++;SysRegister.BandRateConfig=command.RxBuf[offset++];FlashNeedToUpdate=1;break;case BaseCh1Filter:offset++;if(command.RxBuf[offset]<10) SysRegister.M1Filter=command.RxBuf[offset];offset++;FlashNeedToUpdate=1;break;case BaseCh1Offset:
//                                          if(command.RxBuf[offset]<50)
//                                          {MotorControl.M1LinearErrorOffset =command.RxBuf[offset++]<<8;MotorControl.M1LinearErrorOffset|=command.RxBuf[offset++];
//                                          }if(MotorControl.M1LinearErrorOffset>30)    MotorControl.M1LinearErrorOffset=30;else if(MotorControl.M1LinearErrorOffset<5)MotorControl.M1LinearErrorOffset=5;
//                                          else offset+=2;FlashNeedToUpdate=1;break;case BaseCh2Offset:
//                                          if(command.RxBuf[offset]<50)
//                                          {       MotorControl.M2LinearErrorOffset =command.RxBuf[offset++]<<8;MotorControl.M2LinearErrorOffset|=command.RxBuf[offset++];
//                                          }if(MotorControl.M2LinearErrorOffset>30)    MotorControl.M2LinearErrorOffset=30;else if(MotorControl.M2LinearErrorOffset<5)MotorControl.M2LinearErrorOffset=5;
//                                          else offset++;FlashNeedToUpdate=1;break;case BaseCh1WriteDestinationLocation:SysRegister.M1DestinationLocation=0;SysRegister.M1DestinationLocation =command.RxBuf[offset++]<<8;SysRegister.M1DestinationLocation|=command.RxBuf[offset++];if(SysRegister.M1RunMode==LinearRunMode){if(MotorControl.M1MotorRunStop==MotorStop){MotorControl.M1DestinationBuffCount=(SysRegister.M1DestinationLocation*MotorControl.M1CalibrationBuffCount)/1000;}else if(SysRegister.M1DestinationLocation>1000){MotorControl.M1ResetOk=0;MRunning(Motor1,100,Stop);}}else if(SysRegister.M1RunMode==WaterFlowerRunMode){MotorControl.M1CurrentBuffCount=0;MotorControl.M1DestinationBuffCount=SysRegister.M1DestinationLocation;SysRegister.M1DestinationLocation=0;}else MotorControl.M1DestinationBuffCount =SysRegister.M1DestinationLocation;break;case BaseCh2WriteDestinationLocation:SysRegister.M2DestinationLocation=0;SysRegister.M2DestinationLocation =command.RxBuf[offset++]<<8;SysRegister.M2DestinationLocation|=command.RxBuf[offset++];if(SysRegister.M2RunMode==LinearRunMode){if(MotorControl.M2MotorRunStop==MotorStop){MotorControl.M2DestinationBuffCount=(SysRegister.M2DestinationLocation*MotorControl.M2CalibrationBuffCount)/1000;}else if(SysRegister.M2DestinationLocation>1000){MotorControl.M2ResetOk=0;MRunning(Motor2,100,Stop);}}else if(SysRegister.M2RunMode==WaterFlowerRunMode){MotorControl.M2CurrentBuffCount=0;MotorControl.M2DestinationBuffCount=SysRegister.M2DestinationLocation;SysRegister.M2DestinationLocation=0;}else MotorControl.M2DestinationBuffCount=SysRegister.M2DestinationLocation;break;case BaseCh1Speed:offset++;SysRegister.M1Speed=command.RxBuf[offset++];FlashNeedToUpdate=1;break;case BaseCh2Speed:offset++;SysRegister.M2Speed=command.RxBuf[offset++];FlashNeedToUpdate=1;break;case BaseCh1RunMode:offset++;SysRegister.M1RunMode=command.RxBuf[offset++];MotorControl.M1CurrentBuffCount=0;MotorControl.M1DestinationBuffCount=0;MotorControl.M1CalibrationBuffCount=0;MRunning(Motor1,100,Stop);FlashNeedToUpdate=1;break;case BaseCh2RunMode:offset++;SysRegister.M2RunMode=command.RxBuf[offset++];MotorControl.M2CurrentBuffCount=0;MotorControl.M2DestinationBuffCount=0;MotorControl.M2CalibrationBuffCount=0;MRunning(Motor2,100,Stop);FlashNeedToUpdate=1;break;case BaseCh1ResetCmd:if(SysRegister.M1RunMode==LinearRunMode){MRunning(Motor1,100,Stop);offset++;SysRegister.M1ResetCmd=command.RxBuf[offset++];SysRegister.M1CalibrationCmd=0;}else offset+=2;break;case BaseCh2ResetCmd:if(SysRegister.M2RunMode==LinearRunMode){MRunning(Motor2,100,Stop);offset++;SysRegister.M2ResetCmd=command.RxBuf[offset++];SysRegister.M2CalibrationCmd=0;}else offset+=2;break;case BaseCh1CalibrationCmd:if((M1ResetPin==0)&&(SysRegister.M1RunMode==LinearRunMode)&&(MotorControl.M1MotorRunStop==MotorStop)){offset++;SysRegister.M1CalibrationCmd=command.RxBuf[offset++];MotorControl.M1MovingIndex=0;
//                                              FlashNeedToUpdate=1;MotorControl.M1CurrentBuffCount=0;MotorControl.M1DestinationBuffCount=0;MotorControl.M1LinearErrorOffset=5;}else    offset+=2;//Should storedbreak;case BaseCh2CalibrationCmd:if((M2ResetPin==0)&&(SysRegister.M2RunMode==LinearRunMode)&&(MotorControl.M2MotorRunStop==MotorStop)){offset++;SysRegister.M2CalibrationCmd=command.RxBuf[offset++];MotorControl.M2MovingIndex=0;
//                                              FlashNeedToUpdate=1;MotorControl.M2CurrentBuffCount=0;MotorControl.M2DestinationBuffCount=0;MotorControl.M2LinearErrorOffset=5;}else    offset+=2;//Should storedbreak;default:offset+=2;;break;}}}

8,软件代码-电机控制代码

#include <stdio.h>
#include "M031Series.h"
#include "main.h"
void delay_ms(uint16_t count)
{uint32_t i=0;uint16_t j=0;for(j=0;j<count;j++){for(i=0;i<200000;i++);//          WWDT_RELOAD_COUNTER();}
}
float offsetCalcult(uint8_t ch)
{float moffset=0;uint32_t full=0;float error=0;switch(ch){case 1:error = MotorControl.M1LinearErrorOffset;full=MotorControl.M1CalibrationBuffCount;break;case 2:error = MotorControl.M2LinearErrorOffset;full=MotorControl.M2CalibrationBuffCount;break;default:break;}moffset=error/1000.0;moffset=moffset*full;  return moffset;
}
void motorRuning(void)
{static uint32_t m1speed=0;static uint32_t m2speed=0;float m1offset=0;float m2offset=0;switch(SysRegister.M1RunMode){case LinearRunMode:if(SysRegister.M1ResetCmd!=0){if((M1ResetPin!=0)&&(MotorControl.M1Dir!=Reverse)){MRunning(Motor1,SysRegister.M1Speed,Reverse);}else if(M1ResetPin==0){MRunning(Motor1,SysRegister.M1Speed,Stop);SysRegister.M1ResetCmd=0;MotorControl.M1CurrentBuffCount=0;MotorControl.M1DestinationBuffCount=0;MotorControl.M1ResetOk=1;}}else if(SysRegister.M1CalibrationCmd!=0){switch(MotorControl.M1MovingIndex){case 0:if(M1ResetPin==0){MRunning(Motor1,SysRegister.M1Speed,Forward);MotorControl.M1MovingIndex++;MotorControl.M1CalibrationBuffCount=0;}else SysRegister.M1CalibrationCmd=0;break;case 1:if(M1LimitPin==0){MRunning(Motor1,SysRegister.M1Speed,Stop);delay_ms(ChangedDirDelayCount);FlashNeedToUpdate=1;SysRegister.M1CalibrationCmd=0;                                           SysRegister.M1ResetCmd=1;}break;   default:break;                                          }}else if(MotorControl.M1ResetOk!=0){m1offset=offsetCalcult(1);if((MotorControl.M1CurrentBuffCount<(MotorControl.M1DestinationBuffCount-m1offset))&&(MotorControl.M1DestinationBuffCount>m1offset)){if((MotorControl.M1Dir!=Forward)||(m1speed!=SysRegister.M1Speed)){m1speed=SysRegister.M1Speed;MRunning(Motor1,SysRegister.M1Speed,Forward);MotorControl.M1ZeroRunningToLimit=1;}}else if(MotorControl.M1CurrentBuffCount>(MotorControl.M1DestinationBuffCount+MotorControl.M1LinearErrorOffset)){if((MotorControl.M1Dir!=Reverse)||(m1speed!=SysRegister.M1Speed)){m1speed=SysRegister.M1Speed;MRunning(Motor1,SysRegister.M1Speed,Reverse);MotorControl.M1ZeroRunningToLimit=0;}}          else if(MotorControl.M1Dir!=Stop){MRunning(Motor1,100,Stop);m1speed=0;}if(M1LimitPin==0)MotorControl.M1CurrentBuffCount=MotorControl.M1CalibrationBuffCount;if(M1ResetPin==0)MotorControl.M1CurrentBuffCount=0;}break;case ContinueForwardRunMode:if((MotorControl.M1DestinationBuffCount!=0)&&((MotorControl.M1Dir!=Forward)||(m1speed!=SysRegister.M1Speed))){m1speed=SysRegister.M1Speed;MRunning(Motor1,SysRegister.M1Speed,Forward);}else if((MotorControl.M1DestinationBuffCount==0)&&(MotorControl.M1Dir!=Stop)){m1speed=0;MRunning(Motor1,SysRegister.M1Speed,Stop);}break;case ContinueReverseRunMode:if((MotorControl.M1DestinationBuffCount!=0)&&((MotorControl.M1Dir!=Reverse)||(m1speed!=SysRegister.M1Speed)))  {m1speed=SysRegister.M1Speed;MRunning(Motor1,SysRegister.M1Speed,Reverse);}else if((MotorControl.M1DestinationBuffCount==0)&&(MotorControl.M1Dir!=Stop)){m1speed=0;MRunning(Motor1,SysRegister.M1Speed,Stop);}break;case WaterFlowerRunMode:if((MotorControl.M1CurrentBuffCount<MotorControl.M1DestinationBuffCount)&&((MotorControl.M1Dir!=Forward)||(m1speed!=SysRegister.M1Speed))){m1speed=SysRegister.M1Speed;MRunning(Motor1,SysRegister.M1Speed,Forward);}else if((MotorControl.M1CurrentBuffCount>=MotorControl.M1DestinationBuffCount)&&(MotorControl.M1Dir!=Stop)){MRunning(Motor1,SysRegister.M1Speed,Stop);MotorControl.M1CurrentBuffCount=0;MotorControl.M1DestinationBuffCount=0;}break;default:break;}switch(SysRegister.M2RunMode){case LinearRunMode:if(SysRegister.M2ResetCmd!=0){if((M2ResetPin!=0)&&(MotorControl.M2Dir!=Reverse)){MRunning(Motor2,SysRegister.M2Speed,Reverse);}else if(M2ResetPin==0){MRunning(Motor2,SysRegister.M2Speed,Stop);SysRegister.M2ResetCmd=0;MotorControl.M2CurrentBuffCount=0;MotorControl.M2DestinationBuffCount=0;MotorControl.M2ResetOk=1;}}else if(SysRegister.M2CalibrationCmd!=0){switch(MotorControl.M2MovingIndex){case 0:if(M2ResetPin==0){MRunning(Motor2,SysRegister.M2Speed,Forward);MotorControl.M2MovingIndex=1;MotorControl.M2CalibrationBuffCount=0;}else SysRegister.M2CalibrationCmd=0;break;case 1:if(M2LimitPin==0){MRunning(Motor2,SysRegister.M2Speed,Stop);delay_ms(ChangedDirDelayCount);FlashNeedToUpdate=1;SysRegister.M2CalibrationCmd=0;                                           SysRegister.M2ResetCmd=1;}break;               default:break;                                          }}else if(MotorControl.M2ResetOk!=0){m2offset=offsetCalcult(2);if((MotorControl.M2CurrentBuffCount<(MotorControl.M2DestinationBuffCount-m2offset))&&(MotorControl.M2DestinationBuffCount>m2offset)){if((MotorControl.M2Dir!=Forward)||(m2speed!=SysRegister.M2Speed)){m2speed=SysRegister.M2Speed;MRunning(Motor2,SysRegister.M2Speed,Forward);MotorControl.M2ZeroRunningToLimit=1;}}else if(MotorControl.M2CurrentBuffCount>(MotorControl.M2DestinationBuffCount+MotorControl.M2LinearErrorOffset)){if((MotorControl.M2Dir!=Reverse)||(m2speed!=SysRegister.M2Speed)){m2speed=SysRegister.M2Speed;MRunning(Motor2,SysRegister.M2Speed,Reverse);MotorControl.M2ZeroRunningToLimit=0;}}          else if(MotorControl.M2Dir!=Stop){MRunning(Motor2,100,Stop);m2speed=0;}if(M2LimitPin==0)MotorControl.M2CurrentBuffCount=MotorControl.M2CalibrationBuffCount;if(M2ResetPin==0)MotorControl.M2CurrentBuffCount=0;}break;case ContinueForwardRunMode:if((MotorControl.M2DestinationBuffCount!=0)&&((MotorControl.M2Dir!=Forward)||(m2speed!=SysRegister.M2Speed))){m2speed=SysRegister.M2Speed;MRunning(Motor2,SysRegister.M2Speed,Forward);}else if((MotorControl.M2DestinationBuffCount==0)&&(MotorControl.M2Dir!=Stop)){m2speed=0;MRunning(Motor2,SysRegister.M2Speed,Stop);}break;case ContinueReverseRunMode:if((MotorControl.M2DestinationBuffCount!=0)&&((MotorControl.M2Dir!=Reverse)||(m2speed!=SysRegister.M2Speed)))  {m2speed=SysRegister.M2Speed;MRunning(Motor2,SysRegister.M2Speed,Reverse);}else if((MotorControl.M2DestinationBuffCount==0)&&(MotorControl.M2Dir!=Stop)){m2speed=0;MRunning(Motor2,SysRegister.M2Speed,Stop);}break;case WaterFlowerRunMode:if((MotorControl.M2CurrentBuffCount<MotorControl.M2DestinationBuffCount)&&((MotorControl.M2Dir!=Forward)||(m2speed!=SysRegister.M2Speed))) {m2speed=SysRegister.M2Speed;MRunning(Motor2,SysRegister.M2Speed,Forward);}else if((MotorControl.M2CurrentBuffCount>=MotorControl.M2DestinationBuffCount)&&(MotorControl.M2Dir!=Stop)){MRunning(Motor2,SysRegister.M2Speed,Stop);MotorControl.M2CurrentBuffCount=0;MotorControl.M2DestinationBuffCount=0;}break;default:break;}
}

9,技术指标

10,运行模式介绍
该控制卡支持四种运行模式,用户可通过串口进行配置,四种模式分别为:线性往返运行模式,脉冲检测运行模式,持续正转,持续反转。
任何写运行模式寄存器后,以及在线性运行模式下Reset、Limit传感器位置变更或者更换电机后,均需要重新校准电机的行程,校准操作完成后进行系统复位,然后才可对电机进行运行使能操作。校准数据掉电存储重新上电后不需要重新校准。
由于方便量化,我们所有的校准操作都是设置为Reset传感器位置到Limit传感器位置对应于0-1000;即校准完成后0代表Reset传感器位置,1000代表Limit传感器位置,中间位置线性增减,在最佳运行区间为5%-95%之间。
持续正转、持续反转和脉冲模式不需要位置检测和Reset、Limit传感器,所以切换回这三个模式后不需要重新校准行程数据。
电机的运行速度可以通过转速寄存器进行设置,该速度值实时更新,不需要电机停机重启。
a,线性运行模式
线性运行常用在负载在Reset传感器和Limit传感器之间往返运行的应用中。最常见的如XY轴运行,机械臂,自动化生产线等,用户通过串口命令可以控制电机带动负载运行到用户期望的位置中。
该模式下需要用到Reset、Limit传感器和电机的Hall转速信号线,电机的线圈供电接口。其电路图请看附录B。
第一次配置时需要校准操作,配置步骤如下:

a, 搭建系统,其中包括霍尔传感器线缆连接,电机线缆连接,Reset和 Limit传感器线缆连接以及位置安装,设备必须保证电机复位时带动负载朝着Reset传感器的位置方向移动,否则可能出现检测不到Reset传感器造成机械冲突。
b, 上电,设置工作模式为线性往返模式,并查询是否设置成功
c, 设置好期望的复位电机速度值
d, 发送复位命令,等待复位完成
e, 发送校准命令,等待校准完成
f, 根据需要写入目的寄存器800,等待执行机构自动运行到800对应的位置(0-1000对应Reset传感器到Limit传感器位置)
g, 通过读取目的寄存器数据D1、读取当前位置寄存器D2和校准数据寄存器Full,根据公差计算公式得出目前系统公差值Xd。Xd=(D1-D2)/Full。
h, 如果公差值Xd过大(超出1%),则可以适当的调整公差寄存器的数值,其可设置范围为5-30。

校准过的设备未改变工作模式和传感器位置则不需要重新校准,其配置步骤如下:

a, 系统上电
b, 发送复位命令,等待复位完成

c, 设置电机运行的速度值(如果之前设置的速度值不需要更改可跳过该项,速度值掉电保存)
d, 通过写入目的寄存器数值,则电机带动负载运行到指定的位置

当需要紧急停机时,可写入大于1000的数值到通道的目的寄存器值,该通道执行机构紧急停机。如再执行需要先复位。

b,脉冲检测模式 脉冲检测模式的工作原理是向目的寄存器写数值N,则自动开始电机运行,当检测到的脉冲数量等于N时,系统刹车停机等待新的目的寄存器数据更新。

该模式主要用于知道脉冲数量量化的应用。如流量控制系统,M接口接水泵,涡轮流量计的脉冲接脉冲检测接口,当向目的寄存器写入脉冲数量(流量计通常是脉冲模式来量化液体的流量)后,水泵开始运行,当检测到的脉冲数量和目的寄存器写入的数量相同后系统停机,起到输出流量控制的作用。
如料盒下料自动控制系统,M接口接料盒电机,电机的霍尔信号接脉冲检测接口,当向目的寄存器写入脉冲数量(自动下料的原理通常是多少个脉冲对应多少克物料)后,料盒电机开始运行,当检测到的脉冲数量和目的寄存器写入的数量相同后系统停机,起到输出物料控制的作用。
该模式用到电机的Hall信号线或者涡轮流量计脉冲检测信号线,电机线圈供电接口,其电路图请看附录C
操作配置步骤如下:

a, 搭建系统,水泵或电机接到M接口,蜗轮流量计或其他类型的脉冲检测信号或电机霍尔信号接到脉冲检测接口。
b, 上电
c, 写工作模式寄存器设置为脉冲检测模式并查询是否设置成功(该寄存器掉电存储,重新上电不需要重复设置)。
d, 向目的寄存器写入需要运行的脉冲数量,当检测到的脉冲数量与目的寄存器写入的脉冲数量相同后停机,等待下次目的寄存器的更新写入。
如果需要紧急停机时,向相应通道的目的寄存器写入0,则相应通道紧急制动停机。

c,持续正转运行,持续反转运行

这两个模式类似,不同的是一个正方向旋转,一个反方向旋转。该运行模式下,通过写入相应目的寄存器非零值,启动电机(方向参照工作模式设置),写入相应目的寄存器0后停机 由于两个工作模式只有工作和停机,所以只用到电机的线圈接口。其电路图请看附录D 配置和操作步骤

 a,   搭建系统,将电机的电源线连接到控制卡b,  配置工作模式为持续正转(反转)运行模式(该寄存器掉电存储,重新上电后不需要重新设置,可跳过该步骤)。c,   向相应目的寄存器写入非零值,此时电机将按照规定的方向旋转d,    向相应目的寄存器写入0,则电机停机

10,多机通信
该控制卡作为Modbus RTU从机,支持多机星形连接,使您的系统用一个板子即可实现所有自动化控制功能,其连接方式请参考附录E。

11,技术支持:淘宝店铺小二货之家

12,模块链接:
https://item.taobao.com/item.htm?spm=a2oq0.12575281.0.0.50111deb2L9JDD&ft=t&id=662358314934

XY轴控制板,料盒控制板,水路流量控制板,直流电机控制方案和程序代码相关推荐

  1. 不同屏幕分辨率 坐标_干货 | 小议光固化3D打印XY轴分辨率精度

    当提到分辨率,我们可能立马想到电视.手机.平板电脑等电子产品.分辨率跟这些电子产品的清晰度息息相关.在3D打印行业,分辨率同样经常被提及,因为它对于3D成品质量也有着较大的影响.二维平面(XY方向)以 ...

  2. QT设置坐标轴XY轴显示范围方法

    // 使上下两个X轴的范围总是相等,使左右两个Y轴的范围总是相等 connect(fp1->xAxis, SIGNAL(rangeChanged(QCPRange)), fp1->xAxi ...

  3. lisp xy轴不等比缩放_不就是用Python做个动态图吗?看招

    大家好,今天我们要讲的是如何使用Pyecharts制作动态排名变化图 制作这样的一个动态图使用到的是Pyecharts中的TimeLine(时间线轮播图),代码实现起来其实稍有难度,但我希望能通过讲解 ...

  4. wps图表xy轴颠倒_用EXCEL来搞艺术之图表类型变换

    EXCEL中图表类型众多,根据需要我们可以选择不同的图表,但多数时候会按照自我设想进行图表类型确定,却未充分考虑受众的感受.从需求出发考虑,那是否可以在图表设计的时候,用一个控件让受众自己来选择? 对 ...

  5. 柱状图怎么设置xy轴_excel表格xy轴数据-怎么做用excel设置xy轴

    如何在excel中绘制x-y曲线图 方法:抄 1.有表如下图,做出以年份为X轴,收入支出为左边和右边的Y轴,做出关于年份和收入的折线图,步骤如下图,结果如下图:袭 2.然后,右击收入的Y轴坐标,点击& ...

  6. 柱状图中xy轴怎么出现_『怎样设置excel图标的x、y轴』excel图表xy轴名称

    怎么把EXCEL中已经生成的图表的X轴和Y轴互换啊---- 方法如下: 1.打开excel: 2.点中图表,右键,点击"选择数据" 3.点击"切换行列",完成 ...

  7. 案例分享:Qt高频fpga采集数据压力位移速度加速度分析系统(通道配置、电压转换、采样频率、通道补偿、定时采集、距离采集,导出excel、自动XY轴、隐藏XY轴、隐藏显示通道,文件回放等等)

    若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/120345072 红胖子(红模仿)的博文大全:开发技术集 ...

  8. wps两列数据分别作为xy轴_一图胜千字:科研论文插图/数据处理和图表制作学习会(2019年12月2729日 上海)...

    我是吸铁石,无论你多强大,只要你离我近,就会被磁化 放射技师考试资料独家整理历年真题.考点分析.名师讲解.影像资讯. 执业技师资格考试在路上,放射技师之路,你我同行. (全文共计1485字,预计阅读时 ...

  9. 【Python气象绘图临摹】图像绘制(下):地理子图GeoAxes、xy轴设置、应用ncl色阶colormap、各标题、海岸线、添加文本、添加矩形框

    文章目录 前言 plt.fig.ax.三者绘制区别: GeoAxes地图投影:绘图投影和数据投影 x轴.y轴设置: 多个子图之间的间距调节: 图上添加文本.矩形框: python中ncl色阶color ...

最新文章

  1. 现在很火的答题赢钱游戏,让我来简单教你怎么做自动答题器
  2. 论机器学习的正确学习姿势
  3. BZOJ 3576 江南乐
  4. 计算机房电磁辐射防护,计算机房电磁屏蔽
  5. mysql:mysql error:Access denied for user 'root'@'localhost' (using password: YES)
  6. Android中实现SQLite数据库CRUD操作的两种方式
  7. poj1330Nearest Common Ancestors(LCA小结)
  8. Redis多机功能介绍
  9. sed 删除某一行_Linux常用命令三剑客之sed,您真的会用吗?
  10. html里content标签作用,HTML content 标签
  11. ORACLE sid,pid,spid总结
  12. datatables data bind json
  13. 在Java中应用函数式编程请小心!
  14. 为什么选择Netty作为基础通信框架?
  15. MFC 创建快捷方式(学习笔记)
  16. STM32在线仿真出现是循环,使用mcuisp下载程序却可以运行,JTAG下载程序可以下载成功,却没有任何动作!
  17. 关于TUN/TAP网卡二三事以及物理网卡Ring buffer
  18. 华为 GaussDB 首席架构师 武新离职,出任易鲸捷CEO
  19. python调用函数出现未定义_在python中调用函数时出错“函数未定义”
  20. 计网 - 内容分发网络 : CDN 回源的工作机制初探

热门文章

  1. 10个明亮的圣诞照片滤镜LR预设模板 10 Bright Christmas Desktop Mobile presets
  2. VS Code 修改字体为JetBrains Mono
  3. 历年真题 九宫重排(BFS)
  4. 居于ERP的普遍失败,再论企业管理与信息化
  5. HDU 3003 PuPu(N非常大 语死早)
  6. Games101:作业6解析(含提高部分SAH)
  7. 2021年中级Java面试题
  8. VBA基础知识整理(数据类型)
  9. 国家电网利润超五大发电集团净利总和
  10. 阿里云Centos部署lamp https服务器