2. VC 编程示例
2.1 准备工作
(1) 新建一个项目,保存为“ VCExample.dsw ”;
(2) 根据前面讲述的方法,将静态库“ 8840.lib ”加载到项目中;
2.2 运动控制模块
(1) 在项目中添加一个新类,头文件保存为“ CtrlCard.h ”,源文件保存为“ CtrlCard.cpp ”;
(2) 在运动控制模块中首先自定义运动控制卡初始化函数,对需要封装到初始化函数中的库函数进行初始化;
(3) 继续自定义相关的运动控制函数, 如:速度设定函数,单轴运动函数,差补运动函数等;
(4) 头文件“ CtrlCard.h ”代码如下:
# ifndef __ADT8840__CARD__
# define __ADT8840__CARD__
运动控制模块
为了简单、方便、快捷地开发出通用性好、可扩展性强、维护方便的应用系统,我们在控制卡函数库的
基础上将所有库函数进行了分类封装。下面的示例使用一块运动控制卡
******************************************************
#define MAXAXIS 4 //最大轴数
class CCtrlCard
{
public:
int Setup_HardStop(int value, int logic);
int Setup_Stop1Mode(int axis, int value, int logic); (设置stop1 信号方式)
int Setup_Stop0Mode(int axis, int value, int logic); (设置stop0 信号方式)
int Setup_LimitMode(int axis, int value1, int value2, int logic); (设置限位信号方式)
int Setup_PulseMode(int axis, int value); (设置脉冲输出方式)
int Setup_Pos(int axis, long pos, int mode); (设置位置计数器)
int Write_Output(int number, int value); (输出单点函数)
int Read_Input(int number, int &value); (读入点)
int Get_CurrentInf(int axis, long &LogPos, long &ActPos, long &Speed); (获取运动信息)
int Get_Status(int axis, int &value, int mode); (获取轴的驱动状态)
int StopRun(int axis, int mode); (停止轴驱动)
int Interp_Move4(long value1, long value2, long value3, long value4); (四轴差补函数)
int Interp_Move3(int axis1, int axis2, int axis3, long value1, long value2, long value3); (三轴差补函数)
int Interp_Move2(int axis1, int axis2, long value1, long value2); (双轴差补函数)
int Axis_Pmove(int axis ,long value); (单轴驱动函数)
int Axis_Cmove(int axis ,long value); (单轴连续驱动函数)
int Setup_Speed(int axis ,long startv ,long speed ,long add ); (设置速度模块)
int Init_Board(int dec_num); (函数初始化) (设置速度模块)
CCtrlCard(); (定义了一个同名的无参数的构造函数)
int Result; // 返回值
};
#endif
(5) 源文件 “ CtrlCard.cpp ”代码如下: #include "stdafx.h" #include "DEMO.h" #include "CtrlCard.h" #include
"adt8840.h" int devnum=-1;
CCtrlCard::CCtrlCard() (构造函数,为什么是空的?) {
初始化函数
该函数中包含了控制卡初始化常用的库函数 ,这是调用
其他函数的基础 ,所以必须在示例程序中最先调用 返回值 <=0 表示初始化失败 ,返回值 >0 表示初始化成功
int CCtrlCard::Init_Board(int devnum) {
int mode =0;// 应答模式为 1 时 ,响应串口接收有效 ,0 时无效 if(devnum==0) ( devnum :设备号)
{
for (int i = 1; i<=MAXAXIS (最大轴数) ; i++)
{
Result=adt8840a_set_command_pos(devnum, mode,i,0);(设定逻辑计数器)
adt8840a_set_actual_pos(devnum, mode,i,0);(设定实位计数器)
adt8840a_set_startv(devnum, mode,i,0);(设定初始速度)
adt8840a_set_speed(devnum, mode,i,0);(设定驱动速度) adt8840a_set_acc(devnum,
mode,i,0);(设定加速度)
} if(Result==0 )
return 1;
else
return Result;
}
else
return -1;
设置速度模块
依据参数的值 ,判断是匀速还是加减速
设置轴的初始速度、驱动速度和加速度 参数: axis - 轴号 startv - 初始速度 speed -驱动速度 add
-加速度
返回值 =0 正确 ,返回值 =1 错误
int CCtrlCard::Setup_Speed(int axis (轴号) , long startv (初始速度) , long speed (驱动速度) , long add {
if (startv - speed >= 0) // 匀速运动
加速度) )
}
}
{
Result = adt8840a_set_startv(devnum,axis, startv); (设定初始速度) adt8840a_set_speed
(devnum,axis, startv);(设定驱动速度)
}
else
//加减速运动
{ Result = adt8840a_set_startv(devnum,axis, startv); (设定初始速度) adt8840a_set_speed
(devnum,axis, speed); (设定驱动速度) adt8840a_set_acc (devnum,axis, add); (设定加速
度)
}
return Result;
单轴驱动函数
该函数用于驱动单个运动轴运动 参数: axis-轴号 ,value- 输出脉冲数 返回值 =0 正确 ,返回值 =1
错误
int CCtrlCard::Axis_Pmove(int axis, long value)
{
Result = adt8840a_pmove(devnum,axis, value (脉冲数) ); return Result;
任意两轴插补函数 ********************
该函数用于驱动任意两轴进行插补运动
参数: axis1,axis2- 轴号、 value1,value2-脉冲数 返回值 =0 正确 ,返回值 =1 错误
int CCtrlCard::Interp_Move2(int axis1, int axis2, long value1, long value2) {
Result = adt8840a_inp_move2(devnum,axis1, axis2, value1, value2); return Result;
}
任意三轴插补函数
该函数用于驱动任意三轴进行插补运动
参数: axis1,axis2,axis3-轴号、 value1,value2,value3- 脉冲数 返回值 =0 正确 ,返回
值 =1 错误
int CCtrlCard::Interp_Move3(int axis1, int axis2, int axis3, long value1, long value2, long value3)
{
Result = adt8840a_inp_move3(devnum,axis1, axis2, axis3, value1, value2, value3); return
Result;
四轴插补函数
该函数用于驱动 XYZW 四轴进行插补运动 参数: value1,value2,value3,value4- 输出
脉冲数 返回值 =0 正确 ,返回值 =1 错误
int CCtrlCard::Interp_Move4(long value1, long value2, long value3, long value4)
{
}
} }
Result = adt8840a_inp_move4(devnum,value1, value2, value3, value4); return Result;
停止轴驱动
该函数用于立即或减速停止轴的驱动
参数:axis-轴号、mode- 减速方式(0-立即停止, 1-减速停止)返回值=0 正确,返回值=1 错误
int CCtrlCard::StopRun(int axis, int mode)
{ if(mode == 0) // 立即停止
{
Result = adt8840a_sudden_stop(devnum, axis); } else // 减速停止
{
Result = adt8840a_dec_stop(devnum, axis); } return Result;
}
获取轴的驱动状态
该函数用于获取单轴的驱动状态或插补驱动状态
参数:axis-轴号,value-状态指针(0- 驱动结束,非0-正在驱动) mode
(0-获取单轴驱动状态,1-获取插补驱动状态)返回值=0 正确,返回值=1
错误
int CCtrlCard::Get_Status(int axis, int &value, int mode) {
if (mode==0) //获取单轴驱动状态
Result=adt8840a_get_status(devnum,axis,&value);
else //获取插补驱动状态
Result=adt8840a_get_inp_status(devnum,&value); return Result;
}
/***************** 获取运动信息******************************
该函数用于反馈轴当前的逻辑位置,实际位置和运行速度
参数:axis-轴号,LogPos- 逻辑位置,ActPos-实际位置,Speed-运行速度返回值=0 正确,返回值=1 错误
int CCtrlCard::Get_CurrentInf(int axis, long &LogPos, long &ActPos, long &Speed )
{
Result = adt8840a_get_command_pos(devnum,axis, &LogPos); (获取逻辑位置) adt8840a_get_actual_pos (devnum, axis, &ActPos); (获取实际位置)
adt8840a_get_speed (devnum, axis, &Speed);(获取驱动速度)
return Result;
}
读取输入点
该函数用于读取单个输入点
参数:number-输入点(0 ~ 39)
}
返回值:0 -低电平,1 -高电平,-1 -错误
int CCtrlCard::Read_Input(int number,int &value)
{
Result = adt8840a_read_bit(devnum, number, &value); return Result;
}
输出单点函数
该函数用于输出单点信号
参数:number-输出点(0 ~ 15),value 0- 低电平、1-高电平返回值=0 正确,返回值=1 错误
int CCtrlCard::Write_Output(int number, int value)
{
Result = adt8840a_write_bit(devnum, number, value); return Result;
}
设置位置计数器
该函数用于设置逻辑位置和实际位置
参数:axis- 轴号,pos-设置的位置值
mode 0-设置逻辑位置,非0 -设置实际位置
返回值=0 正确,返回值=1 错误
int CCtrlCard::Setup_Pos(int axis, long pos, int mode)
{
if(mode==0)
{
Result = adt8840a_set_command_pos(devnum,axis, pos);(设置逻辑计数器) }
else
{
Result = adt8840a_set_actual_pos(devnum, axis, pos);(设置实位计数器) } return Result;
}
设置脉冲输出方式
该函数用于设置脉冲的工作方式
参数:axis-轴号, value- 脉冲方式0-脉冲+脉冲方式1-脉冲+方向方式返回值=0 正确,返回值=1 错误默认脉冲方式为脉冲+方向方式本程序采用默认的正逻辑脉冲和方向输出信号正逻辑
int CCtrlCard::Setup_PulseMode(int axis, int value)
{
Result = adt8840a_set_pulse_mode(devnum,axis, value, 0, 0);(设置脉冲模式)
return Result;
}
设置限位信号方式
该函数用于设定正/负方向限位输入nLMT 信号的模式
参
数:
axis
-轴号
value1 0 -正限位有效1-正限位无效
value2 0 -负限位有效1-负限位无效
logic 0-低电平有效1-高电平有效
默认模式为:正限位有效、负限位有效、低电平有效
返回值=0正确,返回值=1错误
int CCtrlCard::Setup_LimitMode(int axis, int value1, int value2, int logic)
{
Result = adt8840a_set_limit_mode(devnum,axis, value1, value2, logic);
return Result;
stop0 信号方式**********************
该函数用于设定stop0 信号的模式
参数:axis-轴号
value 0-无效 1 -有效
logic 0-低电平有效1-高电平有效默认模式为:无效
返回值=0正确,返回值=1错误
int CCtrlCard::Setup_Stop0Mode(int axis, int value, int logic)
{
Result = adt8840a_set_stop0_mode(devnum,axis, value ,logic); return Result;
}
设置stop1 信号方式
该函数用于设定stop1 信号的模式
参axis-轴号
value 0-无效 1 -有效
logic 0-低电平有效1-高电平有效
默认模式为:无效
返回值=0正确,返回值=1错误
int CCtrlCard::Setup_Stop1Mode(int axis, int value, int logic)
{
Result = adt8840a_set_stop1_mode(devnum,axis, value, logic); return Result;
}
单轴连续驱动函数
该函数用于驱动单个运动轴运动
参数:axis-轴号,value- 脉冲方向
返回值=0 正确,返回值=1 错误
int CCtrlCard::Axis_Cmove(int axis, long value)
Result = adt8840a_continue_move(devnum,axis, value); return Result; }
}
2.3 功能实现模块
2.3.1 界面设计
说明:
(1)速度设定部分—用于设定各轴的起始速度、驱动速度和加速度;位置设定—设定各轴的驱动脉冲;驱动信息—实时显
示各轴的逻辑位置、实际位置和运行速度。
(2) 驱动对象—通过选择驱动对象,确定参与联动或插补的轴;
(3) 联动—用于向所选驱动对象的所有轴发出单轴驱动指令;插补—用于向所选驱动对象的所有轴发出插补指令;停止
—停止所有轴的脉冲输出;
以上所有数据均以脉冲为单位。
2.3.2 运动控制卡初始化代码位于窗体初始化中,用户新增代码如下:
SetDlgItemText(IDC_EDIT_OPPIP,"192.168.0.123");// 指定控制卡IP 地址SetDlgItemText(IDC_EDIT_MAC,"00-AB-CD-00-01-23");// 指定网卡地址SetDlgItemText(IDC_EDIT_INFO," 请先执行初始化"); DeviceAddr_init();// 设备接口地址初始化
//******* 设置默认初始速度为100 *********
m_nStartvX = 100;
m_nStartvY = 100;
m_nStartvZ = 100;
m_nStartvA = 100;
//********* 设置默认驱动速度为2000********
m_nSpeedX = 2000;
m_nSpeedY = 2000;
m_nSpeedZ = 2000;
m_nSpeedA = 2000;
//********* 设置默认加速度为2500**********
m_nAddX = 2500;
m_nAddY = 2500;
m_nAddZ = 2500;
m_nAddA = 2500;
设置默认目标位置为1000000******
m_nPulseX = 1000000; m_nPulseY
= 1000000; m_nPulseZ = 1000000;
m_nPulseA = 1000000;
UpdateData(FALSE);
SetTimer(MAINTIMER,100,NULL);
//启动计时器
,其中依据选择对象的不同发出对应的驱动指令,代码如下:
2.3.3 联动代码位于联动按钮点击消息
中
*******************************
联动按钮动作
void CVCExampleDlg::OnButtonPmove()
{
UpdateData(TRUE);
long Startv[]={m_nStartvX,m_nStartvY ,m_nStartvZ,m_nStartvA}; // 初始速度
long Speed[]={m_nSpeedX,m_nSpeedY ,m_nSpeedZ,m_nSpeedA}; // 驱动速度
long Add[] ={m_nAddX,m_nAddY ,m_nAddZ,m_nAddA}; // 加速度if(m_bX)
//* *X 速度设定*//
g_CtrlCard.Setup_Speed(1, m_nStartvX, m_nSpeedX,
m_nAddX); }
if(m_bY )
//* *Y 速度设定*//
g_CtrlCard.Setup_Speed(2, m_nStartvY, m_nSpeedY, m_nAddY);
} if(m_bZ )
{
速度设定*//
g_CtrlCard.Setup_Speed(3, m_nStartvZ, m_nSpeedZ, m_nAddZ);
}
if(m_bA )
//* *A 速度设定*//
g_CtrlCard.Setup_Speed(4, m_nStartvA, m_nSpeedA, m_nAddA);
//驱动指令
//*************X 轴驱动***************//
if(m_bX)
g_CtrlCard.Axis_Pmove(1, m_nPulseX);
//*************Y 轴驱动***************//
if(m_bY)
g_CtrlCard.Axis_Pmove(2, m_nPulseY);
//*************Z 轴驱动***************//
if(m_bZ)
g_CtrlCard.Axis_Pmove(3, m_nPulseZ);
//*************A 轴驱动***************//
if(m_bA)
g_CtrlCard.Axis_Pmove(4, m_nPulseA);
if((!m_bX) && (!m_bY) && (!m_bZ) && (!m_bA))
MessageBox("请选择联动轴 !"," 提示 ");
}
2.3.4 插补代码位于插补按钮点击消息中 , 其中依据选择对象的不同发出对应的驱动指令 ,代码如下:
/*********************************
插补按钮动作
void CVCExampleDlg::OnButtonInpmove()
{
UpdateData();
long Startv[]={m_nStartvX,m_nStartvY ,m_nStartvZ,m_nStartvA}; // 初始速度
long Speed[]={m_nSpeedX,m_nSpeedY ,m_nSpeedZ,m_nSpeedA}; //驱动速度 long Add[]={m_nAddX,m_nAddY ,m_nAddZ,m_nAddA}; //加速度
long Pulse[]={m_nPulseX,m_nPulseY ,m_nPulseZ,m_nPulseA};
// 轴的驱动脉冲数 if(m_bX && m_bY && !m_bZ && !m_bA)
//XY 两轴插补
{ g_CtrlCard.Setup_Speed(1, Startv[0], Speed[0], Add[0]); g_CtrlCard.Interp_Move2(1, 2, Pulse[0], Pulse[1]);
}
else if(m_bX && !m_bY && m_bZ && !m_bA)
//XZ 两轴插补
{ g_CtrlCard.Setup_Speed(1, Startv[0], Speed[0], Add[0]); g_CtrlCard.Interp_Move2(1, 3, Pulse[0], Pulse[2]);
}
else if(m_bX && !m_bY && !m_bZ && m_bA)
//XW 两轴插补
{ g_CtrlCard.Setup_Speed(1, Startv[0], Speed[0], Add[0]); g_CtrlCard.Interp_Move2(1, 4, Pulse[0], Pulse[3]);
}
else if(!m_bX && m_bY && m_bZ && !m_bA)
{ g_CtrlCard.Setup_Speed(2, Startv[1], Speed[1], Add[1]); g_CtrlCard.Interp_Move2(2, 3, Pulse[1], Pulse[2]);
}
else if(!m_bX && m_bY && !m_bZ && m_bA)
//YW 两轴插补
{ g_CtrlCard.Setup_Speed(2, Startv[1], Speed[1], Add[1]); g_CtrlCard.Interp_Move2(2, 4, Pulse[1], Pulse[3]);
}
else if(!m_bX && !m_bY && m_bZ && m_bA)
//ZW 两轴插补
{ g_CtrlCard.Setup_Speed(3, Startv[2], Speed[2], Add[2]); g_CtrlCard.Interp_Move2(3, 4, Pulse[2], Pulse[3]);
}
else if(m_bX && m_bY && m_bZ && !m_bA) //XYZ 三轴插补
//*
两轴插补 ***********// //YZ 两轴插补
//*
三轴插补 *//
{
}g_CtrlCard.Setup_Speed(1, Startv[0], Speed[0], Add[0]); g_CtrlCard.Interp_Move3(1, 2, 3, Pulse[0], Pulse[1], Pulse[2]);
}
else if(m_bX && m_bY && m_bZ && !m_bA) //XYZ 三轴插补
{
g_CtrlCard.Setup_Speed(1, Startv[0], Speed[0], Add[0]); g_CtrlCard.Interp_Move3(1, 2, 3, Pulse[0], Pulse[1], Pulse[2]);
}
else if(m_bX && m_bY && !m_bZ && m_bA) //XYW 三轴插补{
g_CtrlCard.Setup_Speed(1, Startv[0], Speed[0], Add[0]); g_CtrlCard.Interp_Move3(1, 2, 4, Pulse[0], Pulse[1], Pulse[3]);
}
else if(m_bX && !m_bY && m_bZ && m_bA) //XZW 三轴插补
{
g_CtrlCard.Setup_Speed(1, Startv[0], Speed[0], Add[0]); g_CtrlCard.Interp_Move3(1, 3, 4, Pulse[0], Pulse[2], Pulse[3]);
}
else if(!m_bX && m_bY && m_bZ && m_bA) //YZW 三轴插补
{
g_CtrlCard.Setup_Speed(2, Startv[1], Speed[1], Add[1]); g_CtrlCard.Interp_Move3(2, 3, 4, Pulse[1], Pulse[2], Pulse[3]);
}
else if(m_bX && m_bY && m_bZ && m_bA) //XYZW 四轴插补
{
g_CtrlCard.Setup_Speed(1, Startv[0], Speed[0], Add[0]); g_CtrlCard.Interp_Move4(Pulse[0], Pulse[1], Pulse[2], Pulse[3]); } else {
MessageBox("请选择插补轴!"," 提示");
//* 四轴插补*//
2.4 监控模块
监控模块用于实时获取所有轴的驱动信息,显示运动信息,同时控制在驱动进行过程中,不允许发出新的驱动指令。
定时器消息完成,代码如下:
// 获取逻辑位置、实际位置、运行速度和驱动状态//
// 读取正负限位、stop0 // //*********************************************************// void CVCExampleDlg::OnTimer(UINT nIDEvent) {
long log=0,act=0,spd=0;
UINT nID1[]={IDC_POS_LOGX,IDC_POS_LOGY ,IDC_POS_LOGZ,IDC_POS_LOGW};
UINT nID2[]={IDC_POS_ACTX,IDC_POS_ACTY ,IDC_POS_ACTZ,IDC_POS_ACTW};
UINT nID3[]={IDC_RUNSPEED_X,IDC_RUNSPEED_Y ,IDC_RUNSPEED_Z,IDC_RUNSPEED_W};
CStatic *lbl;
CString str; int status[4];
for (int i=1; i lbl=(CStatic*)GetDlgItem(nID1[i-1]); str.Format("%ld",log); lbl->SetWindowText(str); lbl=(CStatic*)GetDlgItem(nID2[i-1]); str.Format("%ld",act); lbl->SetWindowText(str); lbl=(CStatic*)GetDlgItem(nID3[i-1]); // 无倍率设定,实际速度=获取的值str.Format("%ld",spd); lbl->SetWindowText(str); //****** 获取驱动状态******// g_CtrlCard.Get_Status(i,status[i-1],0); } //****************** 信号检测 ***************** // X轴STOP0 0 Y 轴STOP0 1 // Z 轴STOP0 2 A轴STOP0 3 // x 轴正限位-4 x 轴负限位- 5 // Y 轴正限位-4 Y 轴负限位- 5 // Z 轴正限位-8 Z 轴负限位-9 // A 轴正限位- 10 A 轴负限位-11 /******************************************* UINT nIDIN1[]={ IDC_STOP0_X,IDC_STOP0_Y , IDC_STOP0_Z,IDC_STOP0_W, IDC_LIMIT_X,IDC_LIMIT_X2, IDC_LIMIT_Y ,IDC_LIMIT_Y2, IDC_LIMIT_Z,IDC_LIMIT_Z2, 监控模块利用 //* 实时获取状态*// g_CtrlCard.Get_CurrentInf(i,log,act,spd); //读取逻辑位置、实际位置和运行速度 //* 显示逻辑位置********// //* 显示实际位置********// //* 显示运行速度********// //X,Y 原点 //Z,A 原点 //X 轴正负限位 //Y 轴正负限位 //Z 轴正负限位 btn=(CButton*)GetDlgItem(IDC_BUTTON_PMOVE); btn->EnableWindow(FALSE); btn=(CButton*)GetDlgItem(IDC_BUTTON_CMOVE); btn->EnableWindow(FALSE); btn=(CButton*)GetDlgItem(IDC_BUTTON_INPMOVE); btn->EnableWindow(FALSE); btn=(CButton*)GetDlgItem(IDC_BUTTON_CLEARPOS); btn->EnableWindow(FALSE); IDC_LIMIT_W,IDC_LIMIT_W2 }; CButton *btn; int value=0; //A 轴正负限位 for (i=0; i<12; i++) { g_CtrlCard.Read_Input(i,value);// 读取信号 ,注意状态不是返回值 btn=(CButton*)GetDlgItem(nIDIN1[i]); btn->SetCheck(value==0?1:0); } //* 信号检测 // 编码器 Z 相输入作 STOP1 // X 轴 STOP1 38 Y 轴 STOP1 39 // Z 轴 STOP1 40 A 轴 STOP1 41 //* UINT nIDIN2[]={ IDC_STOP1_X,IDC_STOP1_Y ,IDC_STOP1_Z,IDC_STOP1_W }; for (i=0; i<4; i++) { g_CtrlCard.Read_Input(38+i,value);// 读取 IN38 到 IN41 信号 btn=(CButton*)GetDlgItem(nIDIN2[i]); btn->SetCheck(value==0?1:0); } //* 按钮控制 if(devnum==0&&status[0]==0 && status[1]==0 && status[2]==0 && status[3]==0) { //* 驱动完成 btn=(CButton*)GetDlgItem(IDC_BUTTON_PMOVE); btn->EnableWindow(TRUE); btn=(CButton*)GetDlgItem(IDC_BUTTON_CMOVE); btn->EnableWindow(TRUE); btn=(CButton*)GetDlgItem(IDC_BUTTON_INPMOVE); btn->EnableWindow(TRUE); btn=(CButton*)GetDlgItem(IDC_BUTTON_CLEARPOS); btn->EnableWindow(TRUE); btn=(CButton*)GetDlgItem(IDC_BUTTON_BASEPARA); btn->EnableWindow(TRUE); } else { //* 驱动进行中 ********** btn=(CButton*)GetDlgItem(IDC_BUTTON_BASEPARA); btn->EnableWindow(FALSE); } CDialog::OnTimer(nIDEvent); } 2.5 停止模块 ,代码停止模块主要用于控制驱动过程中的突发事件,需要立即终止所有轴的驱动。停止模块的代码位于停止按钮的点击消 息中 如下: void CVCExampleDlg::OnButtonStop() { for (int i = 1; i<=MAXAXIS; i++) { g_CtrlCard.StopRun(i,1); }