基于C#的运动控制卡开发教程
本教程主要从建立项目和添加函数库开始,通过对插补运动功能示例的讲解来熟悉运动控制卡项目的开发。
在讲解之前,我们先了解一下广州研为的iMC4xxE/A系列和iMC6xxE/A系列的运动控制卡。
iMC4xxE/A系列是以太网通信的脉冲型运动控制卡,全系列支持4-16个轴;4xxA系列有AD采样和DA/PWM输出;支持多个轴的脉冲输出和编码器反馈;拥有电子手轮功能。
iMC6xxE系列是以太网通信的EtherCat总线型运动控制卡,全系列支持4/6/8/12/16轴;6xxE系列兼容四个脉冲轴,有52个开关量输入和32个开关量输出,拥有电子手轮功能。
iMC6xxA系列是以太网通信的EtherCat总线型运动控制卡,全系列支持4-16个轴;6xxA系列拥有RS232和485通信,支持modbus协议;有24个开关量输入,有24个开关量输出,拥有电子手轮功能;可最多串联扩展64个控制卡或IO扩展卡。
下面开始介绍C#开发流程
1:新建windows窗体应用
(1)、打开VS2017,选择“文件”→“新建”→“项目”
(2)、选择开发语言为“Visual C#”→“Windows桌面”→“Windows窗体应用(.NET Framework)”,修改工程名称,点击确定即可添加工程
2:在厂家提供的光盘资料内提取C#的头文件和提供的dll函数库(以下根据iMC4xxE/A系列的函数库和头文件为例)
(1)、根据目录找到C#头文件
(2)、根据目录找到dll库(以64位函数库为例)
3、将dll函数库与C#头文件拷贝到创建的工程文件夹中,另外将dll库放入到Debug/Relase文件夹中。
4、将C#头文件和dll库导入到解决方案资源管理器中(可直接拖拽)
5、打开Form1.cs进入设计界面,双击设计界面进入程序编辑界面,使用using将C#头文件的命名空间添加进去。
6、在private void Form1_Load(object sender, EventArgs e)中使用PKG_IMC_FindNetCard函数获取网卡名称与网卡个数,并将网卡名称存放到控件中并显示出来。
7、定义一个class类里面定义全局变量g_handle,用于保存使用PKG_IMC_Open函数时获取到的设备句柄,每一个设备对应一个专属的句柄,用于后续的控制。
注意:PKG_IMC_Open函数中的两个参数都为int类型,控制卡ID出厂默认是0.网卡ID是指第几个网卡,并非网卡名称。
8、对控制卡的参数进行配置,配置方式有两种,一种是使用配置文件进行配置,另一种是通过封装好的配置函数对控制卡进行配置(以下使用封装好的配置函数为例)
注意:脉冲宽度的值设置要与驱动器允许的范围匹配。不在范围内就无法控制驱动器运动。
st = IMC_Pkg.PKG_IMC_ClearIMC(Global.g_handle);//清空所有FIFO
for (i = 0; i < Global.g_naxis; i++)
{
st = IMC_Pkg.PKG_IMC_ClearAxis(Global.g_handle, i);//清空所有的轴状态
if(st == 0)
break;
st = IMC_Pkg.PKG_IMC_SetStopfilt(Global.g_handle, 0xFFF0, i);//设置运动轴发生错误时停止运行
if(st == 0)
break;
st = IMC_Pkg.PKG_IMC_SetExitfilt(Global.g_handle, 0, i);//设置运动轴发生错误时不退出运行
if(st == 0)
break;
st = IMC_Pkg.PKG_IMC_SetPulWidth(Global.g_handle, 3000, i);//设置脉冲宽度脉冲宽度越高支持运动的速度就越低
if(st == 0)
break;
st = IMC_Pkg.PKG_IMC_SetPulPolar(Global.g_handle, 1, 1, i);//设置脉冲和方向的有效电平。非0:高电平有效, 0:为低电平有效
if(st == 0)
break;
st = IMC_Pkg.PKG_IMC_SetEncpEna(Global.g_handle, 0, i);//使能/禁用编码器反馈。非0:使能, 0:禁用
if(st == 0)
break;
st = IMC_Pkg.PKG_IMC_SetEncpMode(Global.g_handle, 0, 1, i);//设置编码器计数模式和方向。(使能编码器反馈才需要设置)
if(st == 0)
break;
st = IMC_Pkg.PKG_IMC_SetVelAccLim(Global.g_handle, 32767, 32767, i);//设置速度和加速度极限。范围:0~32767.9999
if(st == 0)
break;
st = IMC_Pkg.PKG_IMC_Setlimit(Global.g_handle, 1, 0, 1, 0, i);//使能硬件正负限位和设置低电平有效
if(st == 0)
break;
st = IMC_Pkg.PKG_IMC_SetAlm(Global.g_handle, 0, 0, i);//使能/禁用伺服报警输入. 0:不使能/低电平有效 1:使能/高电平有效
if(st == 0)
break;
st = IMC_Pkg.PKG_IMC_SetParam16(Global.g_handle, ParamDef.settlewinLoc, 1, i, (int)FIFO_SEL.SEL_IFIFO);//设置电机停止时静止窗口的大小
if (st == 0)
break;
st = IMC_Pkg.PKG_IMC_SetParamBit(Global.g_handle, ParamDef.stepmodLoc, 0, 0, i, (int)FIFO_SEL.SEL_IFIFO);//设置脉冲输出模式。0:脉冲+方向 1:正向脉冲+负向脉冲
if (st == 0)
break;
st = IMC_Pkg.PKG_IMC_SetEna(Global.g_handle, 1, i);//使能驱动器放在最后.非0:使能 0:不使能
if(st == 0)
break;
}
打开控制卡与完成配置后就可以对控制卡进行运动操作。(以下使用插补运动为示例)
1、设计插补运动界面
根据启动圆弧插补控件、启动圆弧直线插补控件和启动直线插补控件分别建立三个线程控制运动。
(1)、定义全局变量
int m_thdoing;
int[] m_axis = new int[16];
double m_acc, m_tgvel, m_endvel, m_rate;
int m_endx, m_endy, m_cx, m_cy, m_dir, m_segnum, m_axisCnt;
int[] m_startpos = new int[2];
int[] m_pos = new int[16];
bool m_isAbs;
(2)、圆弧插补
public void ArcThread()
{
int st, fifo;
string err;
m_thdoing = 1;
Button1Ch("停止");
fifo = (int)FIFO_SEL.SEL_PFIFO1; //使用插补空间1
st = IMC_Pkg.PKG_IMC_PFIFOclear(Global.g_handle, fifo); //清空PFIFO
if(st != 0){
st = IMC_Pkg.PKG_IMC_PFIFOrun(Global.handle, fifo); //启用插补空间
}
if(st != 0){
st = IMC_Pkg.PKG_IMC_SetPFIFOaccel(Global.handle, acc, fifo); //设置加速度
st = IMC_Pkg.PKG_IMC_SetPathMode(Global.g_handle, 0, 0, 0, fifo);//设置插补模式
}
if(st != 0){
st = IMC_Pkg.PKG_IMC_AxisMap(Global.g_handle, m_axis, m_axisCnt, fifo);//映射轴
}
if(st != 0){
//先移动到起点位置(不等待完成,使函数立即返回)
st = IMC_Pkg.PKG_IMC_Line_Pos(Global.g_handle, m_startpos, m_axisCnt, m_tgvel, m_tgvel, 0, fifo);
}
if(st != 0){//函数等待运动完成后才返回
st = IMC_Pkg.PKG_IMC_Arc_Pos(Global.g_handle, m_endx, m_endy, m_cx, m_cy, m_dir, m_tgvel, m_endvel, 1, fifo);
}
if(st == 0)
{
err = Global.GetFunErrStr();
MessageBox.Show(err);
}
Button1Ch("启动圆弧插补");
m_thdoing = 0;
}
(3)、圆弧直线插补
private void ArcLineThread()
{
int st, fifo;
string err;
fifo = (int)FIFO_SEL.SEL_PFIFO1;//使用插补空间 1
m_thdoing = 1;
Button2Ch("停止");
st = IMC_Pkg.PKG_IMC_PFIFOclear(Global.g_handle, fifo); //清空PFIFO
if(st != 0){
st = IMC_Pkg.PKG_IMC_PFIFOrun(Global.handle, fifo); //启用插补空间
}
if(st != 0){
st = IMC_Pkg.PKG_IMC_SetPFIFOaccel(Global.handle, acc, fifo); //设置加速度
st = IMC_Pkg.PKG_IMC_SetPathMode(Global.g_handle, 0, 0, 0, fifo);//设置插补模式
}
if(st != 0){
st = IMC_Pkg.PKG_IMC_AxisMap(Global.g_handle, m_axis, m_axisCnt, fifo);//映射轴
}
if(st != 0){
//先移动到圆弧起点位置(不等待完成,使函数立即返回)
st = IMC_Pkg.PKG_IMC_Line_Pos(Global.g_handle, m_startpos, 2, m_tgvel, m_tgvel, 0, fifo);
}
if(st != 0)
{//函数等待运动完成后才返回
st = IMC_Pkg.PKG_IMC_ArcLine_Pos(Global.g_handle, m_endx, m_endy, m_cx, m_cy, m_dir, m_pos, m_axisCnt - 2, m_tgvel, 0, 1, fifo);
}
if(st == 0)
{
err = Global.GetFunErrStr();
MessageBox.Show(err);
}
Button2Ch("启动圆弧直线插补");
m_thdoing = 0;
}
(4)、直线插补
private void LineThread()
{
int st, i, n, fifo;
string err;
int[] segpos;
segpos = new int[m_segnum * m_axisCnt];
for (i = 0; i < m_segnum; i++)
{
for (n = 0; n < m_axisCnt; n++)
segpos[i * m_axisCnt + n] = m_pos[n];
}
fifo = (int)FIFO_SEL.SEL_PFIFO1;//使用插补空间 1
m_thdoing = 1;
Button3Ch("停止");
st = IMC_Pkg.PKG_IMC_PFIFOclear(Global.g_handle, fifo); //清空PFIFO
if(st != 0){
st = IMC_Pkg.PKG_IMC_PFIFOrun(Global.handle, fifo); //启用插补空间
}
if(st != 0){
st = IMC_Pkg.PKG_IMC_SetPFIFOaccel(Global.handle, acc, fifo); //设置加速度
st = IMC_Pkg.PKG_IMC_SetPathMode(Global.g_handle, 0, 0, 0, fifo);//设置插补模式
}
if(st != 0){
st = IMC_Pkg.PKG_IMC_AxisMap(Global.g_handle, m_axis, m_axisCnt, fifo);//映射轴
}
if(st != 0){
if(m_isAbs)//使用绝对坐标
{
if(m_segnum > 1)
st = IMC_Pkg.PKG_IMC_MulLine_Pos(Global.g_handle, segpos, m_axisCnt, m_segnum, m_tgvel, 0, 1, fifo);
else
st = IMC_Pkg.PKG_IMC_Line_Pos(Global.g_handle, m_pos, m_axisCnt, m_tgvel, 0, 1, fifo);
}else{//使用相对坐标
if(m_segnum > 1)
st = IMC_Pkg.PKG_IMC_MulLine_Dist(Global.g_handle, segpos, m_axisCnt, m_segnum, m_tgvel, 0, 1, fifo);
else
st = IMC_Pkg.PKG_IMC_Line_Dist(Global.g_handle, m_pos, m_axisCnt, m_tgvel, 0, 1, fifo);
}
}
if(st == 0)
{
err = Global.GetFunErrStr();
MessageBox.Show(err);
}
Button3Ch("启动直线插补");
m_thdoing = 0;
return;
}
结合上述三个线程,总结运动控制卡进行插补运动之前所必须的配置有以下函数
运动控制卡开发简易操作流程(运动控制卡是线程安全,可以执行多线程控制)