CANUSB上位机

转自http://blog.sina.com.cn/s/blog_948ce9dd01017yy2.html

硬件设备是DSP电机板的CAN通信模块和周立功公司生产的USBCAN-I/II智能CAN接口卡组成的进行数据传输,上位机实质是将can接口卡中收到的数据读取出来在pc机上处理以及发送命令或数据给CAN接口卡,CAN接口卡采用CAN协议发送给DSP电机板的CAN通信模块,间接实现上位机与DSPCAN通信。

由于上位机只需和CAN接口卡进行数据交换,因此,上位机的协议将简化,充分利用周立功公司提供的CAN接口卡的接口函数库即可轻松开发出适用于自己的CAN上位机。接口函数库的下载地址http://www.embedcontrol.com/products/PCI/USBCAN.asp

在上位机程序中,主要需要使用的函数有DWORD__stdcallVCI_OpenDevice(DWORDDevType,DWORDDevIndex,DWORDReserved);

 

DWORD__stdcallVCI_CloseDevice(DWORD DevType,DWORD DevIndex);

 

DWORD __stdcall VCI_InitCan(DWORD DevType, DWORD DevIndex, DWORD CANIndex, PVCI_INIT_CONFIG pInitConfig);

 

DWORD__stdcallVCI_StartCAN(DWORD DevType,DWORD DevIndex,DWORD CANIndex);

 

DWORD__stdcallVCI_ResetCAN(DWORD DevType,DWORD DevIndex,DWORD CANIndex);

 

ULONG__stdcallVCI_Receive(DWORD DevType,DWORD DevIndex,DWORD CANIndex,PVCI_CAN_OBJ pReceive,ULONG Len,INT WaitTime=1);

 

ULONG__stdcallVCI_Transmit(DWORD  DevType,DWORD  DevIndex,DWORD CANIndex,PVCI_CAN_OBJ pSend,ULONG Len);

 

  vc中使用这些库函数时,

首先,把库函数文件都放在工作目录下。库函数文件总共有三个文件:ControlCAN.hControlCAN.libControlCAN.dll和一个文件夹kerneldlls

其次 1)在扩展名为.CPP的文件中包含ControlCAN.h头文件。

如:#includeControlCAN.h

(2) 在工程的连接器设置中连接到ControlCAN.lib文件。

如:在VC7环境下,在项目属性页里的配置属性→连接器→输入→附加依赖项中添加

ControlCAN.lib

或者,在源文件的包含头文件处添加#pragma comment(lib,"controlcan.lib")

 

 OpenDevice时,需要用到的参数有DevType ,DevIndex。分别表示接口设备类型和设备索引号。设备类型的定义见下表。设备索引号,比如当只有一个PCI5121时,索引号为0,有两个时可以为01

//接口卡设备类型定义

#define VCI_PCI5121               1

#define VCI_PCI9810               2

#define VCI_USBCAN1               3

#define VCI_USBCAN2               4

#define VCI_USBCAN2A              4

#define VCI_PCI9820               5

#define VCI_CAN232                6

#define VCI_PCI5110               7

#define VCI_CANLITE               8

#define VCI_ISA9620               9

#define VCI_ISA5420               10

#define VCI_PC104CAN              11

#define VCI_CANETUDP              12

#define VCI_CANETE                12

#define VCI_DNP9810               13

#define VCI_PCI9840               14

#define VCI_PC104CAN2             15

#define VCI_PCI9820I              16

#define VCI_CANETTCP              17

#define VCI_PEC9920               18

#define VCI_PCI5010U              19

#define VCI_USBCAN_E_U            20

#define VCI_USBCAN_2E_U           21

#define VCI_PCI5020U              22

#define VCI_EG2OT_CAN             23

 

 

VCI_InitCan时要用到的结构体参数VCI_INIT_CONFIG,这个结构体定义了初始化CAN的配置,设置CAN的波特率、屏蔽码。对于设备类型为PCI-5010-U/PCI-5020-U/USBCAN-E-U/USBCAN-2E-U时,对滤波和波特率的设置应该放到VCI_SetReference里设置,这里pInitConfig中的成员只有Mode需要设置,其他的6个成员可以忽略。

typedefstruct_INIT_CONFIG{

DWORD AccCode;

DWORD AccMask;

DWORD Reserved;

UCHAR Filter;

UCHAR Timing0;

UCHAR Timing1;

UCHAR Mode;

}VCI_INIT_CONFIG,*PVCI_INIT_CONFIG;

设置结构体参数的Timing0Timing1可以设置CAN的波特率,常见的波特率设置如下:

CAN波特率         定时器     定时器1

5Kbps               0xBF        0xFF

10Kbps              0x31        0x1C

20Kbps              0x18        0x1C

40Kbps              0x87        0xFF

50Kbps              0x09        0x1C

80Kbps              0x83        0Xff

100Kbps             0x04        0x1C

125Kbps             0x03        0x1C

200Kbps             0x81        0xFA

250Kbps             0x01        0x1C

400Kbps             0x80        0xFA

500Kbps             0x00        0x1C

666Kbps             0x80        0xB6

800Kbps             0x00        0x16

1000Kbps            0x00        0x14

 

VCI_ReceiveVCI_Transmit时需要用到PVCI_CAN_OBJ结构体,这个结构体用来传送CAN的信息帧。

typedefstruct_VCI_CAN_OBJ{

UINT ID;

UINT TimeStamp;

BYTE TimeFlag;

BYTE SendType;

BYTE RemoteFlag;

BYTE ExternFlag;

BYTE DataLen;

BYTE Data[8];

BYTE Reserved[3];

}VCI_CAN_OBJ,*PVCI_CAN_OBJ;

成员参数的含义:

ID

报文ID

TimeStamp

接收到信息帧时的时间标识,从CAN控制器初始化开始计时。

TimeFlag

是否使用时间标识,为1TimeStamp有效,TimeFlagTimeStamp只在此帧为接收帧时有意义。

SendType

发送帧类型,=0时为正常发送,=1时为单次发送,=2时为自发自收,=3时为单次自发自收,只在此

帧为发送帧时有意义。(当设备类型为EG20T-CAN时,发送方式在VCI_InitCan中通过设置,此处的

设置无效,设置为自发自收模式后EG20T-CAN不能从总线上接收数据,只能收到自己发出的数据)RemoteFlag

是否是远程帧。

ExternFlag

是否是扩展帧。

DataLen

8

数据长度(<=8),即Data的长度。

Data

报文的数据。

Reserved

系统保留。

接收信息创建一个线程,在线程中接收数据。AfxBeginThread(ReceiveThread,this);注意,如果线程函数是类的成员函数,需要声明为静态的。全局函数不需要声明为静态。原因是类的静态函数不属于该类的任何一个对象,而是属于类本身,所以不受对象局部变量的影响,在运行时可以直接调用类的静态函数,从而启动线程!普通成员函数再参数传递时编译器会加一个this指针,而静态不会,否则你编译的时候会出错.

 

  总结:CAN通信上位机开发利用CAN接口卡提供的函数库很方便就能操作。注意VCI_CAN_OBJ结构体和VCI_INIT_CONFG结构体。

 

函数操作流程:


 

MFC主要代码

 


void CCanTestDlg::OnBnClickedButtoninitial()
{
 // TODO: 在此添加控件通知处理程序代码
 // TODO: 在此添加控件通知处理程序代码
 // TODO: Add your control notification handler code here
 if(m_connect ==true){ //如果已经连接
  m_connect =false;
  Sleep(500);
  ShowList("USBCAN已断开连接");
  MessageBox("USBCAN已经断开连接","提示",MB_OK|MB_ICONQUESTION);
  GetDlgItem(IDC_ButtonInitial)->SetWindowText("重新连接");
  VCI_CloseDevice(VCI_USBCAN2,0);
  return;
 }

 //如果未连接
 UpdateData(true);
 VCI_INIT_CONFIG init_config;
 memset(&init_config,0,sizeof(VCI_INIT_CONFIG));
 init_config.AccCode=0;     //验收码
 init_config.AccMask=0xffffffff;     //屏蔽码
 init_config.Filter=1;    //过滤方式
 init_config.Mode=0;      //模式
 init_config.Timing0=0;    //定时器0
 init_config.Timing1=0x1c;    //定时器1

 //打开设备
 if(VCI_OpenDevice(4,0,1)!=STATUS_OK)
 {
  MessageBox("打开设备失败!","警告",MB_OK|MB_ICONQUESTION);
  return;
 }
 //初始化CAN
 if(VCI_InitCAN(4,0,1,&init_config)!=STATUS_OK)
 {
  MessageBox("初始化CAN失败!","警告",MB_OK|MB_ICONQUESTION);
  VCI_CloseDevice(VCI_USBCAN2,0);
  return;
 }
 //启动CAN
 if(VCI_StartCAN(4,0,1)!=1)
 {
  MessageBox("启动CAN失败!","警告",MB_OK|MB_ICONQUESTION);
  VCI_CloseDevice(VCI_USBCAN2,0);
  return;
 }
 m_connect=true;
 MessageBox("USBCAN成功初始化","提示",MB_OK|MB_ICONQUESTION);
 ShowList("USBCAN成功连接");
 GetDlgItem(IDC_ButtonInitial)->SetWindowText("断开连接");
 m_thread=AfxBeginThread(ReceiveThread,this);


}


void CCanTestDlg::OnBnClickedButtonsend()
{
 // TODO: 在此添加控件通知处理程序代码
 // TODO: Add your control notification handler code here
 if(m_connect==false)
 {
  MessageBox("USBCAN未连接,请先连接","提示",MB_OK|MB_ICONQUESTION);
  return;

 }
 VCI_CAN_OBJ frameinfo;
 char szFrameID[9];
 unsigned char FrameID[4]={0,0,0,0};
 memset(szFrameID,'0',9);
 unsigned char Data[8];
 char szData[25];
 BYTE datalen=0;

 UpdateData(true);
 if(m_frameid.GetLength()==0||
  (m_senddata.GetLength()==0))
 {
  ShowList("请输入数据");
  return;
 }

 if(m_frameid.GetLength()>8)
 {
  ShowList("ID值超过范围");
  return;
 }
 if(m_frameid.GetLength()>24)
 {
  ShowList("数据长度超过范围,最大为8个字节");
  return;
 }
 
 memcpy(&szFrameID[8-m_frameid.GetLength()],(LPCTSTR)m_frameid,m_frameid.GetLength());
 StrToInt((unsigned char*)szFrameID,FrameID,4,0);

 datalen=(m_senddata.GetLength()+1)/3;
 strcpy(szData,(LPCTSTR)m_senddata);
 StrToInt((unsigned char*)szData,Data,datalen,1);


 UpdateData(false);

 frameinfo.DataLen=datalen;
 memcpy(&frameinfo.Data,Data,datalen);

 frameinfo.RemoteFlag=0;
 frameinfo.ExternFlag=0;
 if(frameinfo.ExternFlag==1)
 {
  frameinfo.ID=((DWORD)FrameID[0]<<24)+((DWORD)FrameID[1]<<16)+((DWORD)FrameID[2]<<8)+
   ((DWORD)FrameID[3]);
 }
 else
 {
  frameinfo.ID=((DWORD)FrameID[2]<<8)+((DWORD)FrameID[3]);  
 }
 frameinfo.SendType=m_combosendmeans.GetCurSel();

 if(VCI_Transmit(4,0,1,&frameinfo,1)==1)
 {
  //m_sendcnt += 1;
  ShowList("写入成功");  
 }
 else
 {
  ShowList("写入失败");  
 }
}


int CCanTestDlg::CharToInt(unsigned char chardata, unsigned char *intdata)
{
 unsigned char cTmp;
 cTmp=chardata-48;
 if(cTmp>=0&&cTmp<=9)
 {
  *intdata=cTmp;
  return 0;
 }
 cTmp=chardata-65;
 if(cTmp>=0&&cTmp<=5)
 {
  *intdata=(cTmp+10);
  return 0;
 }
 cTmp=chardata-97;
 if(cTmp>=0&&cTmp<=5)
 {
  *intdata=(cTmp+10);
  return 0;
 }
 return 1;
}


int CCanTestDlg::StrToInt(unsigned char* str, unsigned char* intdata, int length, int flag)
{
 unsigned char cTmp=0;
 int i=0;
 for(int j=0;j<length;j++)
 {
  if(CharToInt(str[i++],&cTmp))
   return 1;
  intdata[j]=cTmp;
  if(CharToInt(str[i++],&cTmp))
   return 1;
  intdata[j]=(intdata[j]<<4)+cTmp;
  if(flag==1)
   i++;
}
return 0;
}

 

 

void CCanTestDlg::ShowList(CString str)
{
 m_listbox.InsertString(m_listbox.GetCount(),str);
 m_listbox.SetCurSel(m_listbox.GetCount()-1);
}


unsigned int CCanTestDlg::ReceiveThread(void *param)
{
 CCanTestDlg *dlg=(CCanTestDlg*)param;
 CListBox *box=(CListBox *)dlg->GetDlgItem(IDC_DataList);
 VCI_CAN_OBJ frameinfo[50];
 VCI_ERR_INFO errinfo;
 int len=1;
 int i=0;
 CString str,tmpstr;
 while(1)
 {
  Sleep(1);
  if(dlg->m_connect==0)
   break;
  len=VCI_Receive(4,0,1,frameinfo,50,200);
  if(len<=0)
  {
   //注意:如果没有读到数据则必须调用此函数来读取出当前的错误码,
   //千万不能省略这一步(即使你可能不想知道错误码是什么)
   VCI_ReadErrInfo(4,0,1,&errinfo);
  }
  else
  {
   for(i=0;i<len;i++)
   {
    str="接收到数据帧:  ";
    if(frameinfo[i].TimeFlag==0)
     tmpstr="时间标识:无  ";
    else
     tmpstr.Format("时间标识:x ",frameinfo[i].TimeStamp);
    str+=tmpstr;
    tmpstr.Format("帧ID:x ",frameinfo[i].ID);
    str+=tmpstr;
    str+="帧格式:";
    if(frameinfo[i].RemoteFlag==0)
     tmpstr="数据帧 ";
    else
     tmpstr="远程帧 ";
    str+=tmpstr;
    str+="帧类型:";
    if(frameinfo[i].ExternFlag==0)
     tmpstr="标准帧 ";
    else
     tmpstr="扩展帧 ";
    str+=tmpstr;
    box->InsertString(box->GetCount(),str);
    if(frameinfo[i].RemoteFlag==0)
    {
     str="数据:";
     if(frameinfo[i].DataLen>8)
      frameinfo[i].DataLen=8;
     for(int j=0;j<frameinfo[i].DataLen;j++)
     {
      tmpstr.Format("x ",frameinfo[i].Data[j]);
      str+=tmpstr;
     }
     //EnterCriticalSection(&(dlg->m_Section));
     //LeaveCriticalSection(&(dlg->m_Section));
     box->InsertString(box->GetCount(),str);
       
   }
   box->SetCurSel(box->GetCount()-1);
  }
 }
 return 0;
}


void CCanTestDlg::OnBnClickedButtonclear()
{
 // TODO: 在此添加控件通知处理程序代码
 ((CListBox *)GetDlgItem(IDC_DataList))->ResetContent();
}


void CCanTestDlg::OnBnClickedButtonpause()
{
 // TODO: 在此添加控件通知处理程序代码
 if(m_handle_flag)
 {
 GetDlgItem(IDC_ButtonPause)->SetWindowText("继续");
 m_thread->SuspendThread(); 
 m_handle_flag=false;
 }
 else
 {
 GetDlgItem(IDC_ButtonPause)->SetWindowText("暂停");
 m_thread->ResumeThread(); 
 m_handle_flag=true;
 }
}

 

注意:

1,线程声明要声明为静态函数,AfxBeginThread返回值是一个CWinThread 的指针,定义时,CWinThread *m_thread;

2,GetDlgItem(IDC_DataList)的类型转换要在引用成员函数前进行,即,要注意括号的位置。

((CListBox *)GetDlgItem(IDC_DataList))->ResetContent();若在CListBox外不加双括号,其成员函数会找不到ResetContent函数。
  小心谨慎,差之毫厘,谬以千里。

3,线程的暂停继续,可以实现接收数据的暂停和继续。m_thread->SuspendThread(); m_thread->ResumeThread(); 


  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值