CAN总线的上位机设计,本章将基于windows平台做出相关讲解。
CAN总线上位机的开发,若我们不是开发CAN卡的厂商,基本来说都是二次开发。我们需借助当前使用的CAN卡设备,利用CAN卡提供的动态库或静态库,做相应的应用开发。
前期准备:
1.UI选择方案:VS自带的MFC C# 或当下流行的QT等(这个主要看各人习惯,对哪种平台熟悉,选哪种吧)
2.对多线程开发的理解(建议采用多线程)
3.CAN卡和二次开发库
4.下位机(具有CAN外设,软件实现CAN基本功能)
5.具备15765 14229的协议基础
接下来我们看看如何实现基本的二次开发吧
一、二次开发库及其资料解读(这里笔者就用国外的PCAN和国内的ITEKON举例)
?
????????其实不管哪一家的CAN卡,都提供了常规平台的demo,如:vc qt labview等等,查阅提供的函数手册,我们就可以实现基本的设备查询读写操作了。如PCAN提供了及其完整的工程和执行文件,如下:
讲到这里了是不是很简单,别人提供了源码工程和手册,我们只需要提供的二次开发库,定制自己的上位机。
二、平台基本元素介绍,如下两家sdk基础包都差不多,设备初始化读写关闭等等:
TPCANStatus __stdcall CAN_Initialize(
TPCANHandle Channel,
TPCANBaudrate Btr0Btr1,
TPCANType HwType _DEF_ARG,
DWORD IOPort _DEF_ARG,
WORD Interrupt _DEF_ARG);
TPCANStatus __stdcall CAN_InitializeFD(
TPCANHandle Channel,
TPCANBitrateFD BitrateFD);
TPCANStatus __stdcall CAN_Uninitialize(
TPCANHandle Channel);
TPCANStatus __stdcall CAN_Reset(
TPCANHandle Channel);
TPCANStatus __stdcall CAN_GetStatus(
TPCANHandle Channel);
TPCANStatus __stdcall CAN_Read(
TPCANHandle Channel,
TPCANMsg* MessageBuffer,
TPCANTimestamp* TimestampBuffer);
TPCANStatus __stdcall CAN_ReadFD(
TPCANHandle Channel,
TPCANMsgFD* MessageBuffer,
TPCANTimestampFD *TimestampBuffer);
TPCANStatus __stdcall CAN_Write(
TPCANHandle Channel,
TPCANMsg* MessageBuffer);
TPCANStatus __stdcall CAN_WriteFD(
TPCANHandle Channel,
TPCANMsgFD* MessageBuffer);
TPCANStatus __stdcall CAN_FilterMessages(
TPCANHandle Channel,
DWORD FromID,
DWORD ToID,
TPCANMode Mode);
TPCANStatus __stdcall CAN_GetValue(
TPCANHandle Channel,
TPCANParameter Parameter,
void* Buffer,
DWORD BufferLength);
TPCANStatus __stdcall CAN_SetValue(
TPCANHandle Channel,
TPCANParameter Parameter,
void* Buffer,
DWORD BufferLength);
TPCANStatus __stdcall CAN_GetErrorText(
TPCANStatus Error,
WORD Language,
LPSTR Buffer);
DWORD __stdcall VCI_OpenDevice(DWORD DeviceType,DWORD DeviceInd,DWORD Reserved);
DWORD __stdcall VCI_CloseDevice(DWORD DeviceType,DWORD DeviceInd);
DWORD __stdcall VCI_InitCAN(DWORD DeviceType, DWORD DeviceInd, DWORD CANInd, PVCI_INIT_CONFIG pInitConfig);
DWORD __stdcall VCI_ReadBoardInfo(DWORD DeviceType,DWORD DeviceInd,PVCI_BOARD_INFO pInfo);
DWORD __stdcall VCI_ReadErrInfo(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,PVCI_ERR_INFO pErrInfo);
DWORD __stdcall VCI_ReadCANStatus(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,PVCI_CAN_STATUS pCANStatus);
DWORD __stdcall VCI_GetReference(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,DWORD RefType,PVOID pData);
DWORD __stdcall VCI_SetReference(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,DWORD RefType,PVOID pData);
ULONG __stdcall VCI_GetReceiveNum(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd);
DWORD __stdcall VCI_ClearBuffer(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd);
DWORD __stdcall VCI_StartCAN(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd);
DWORD __stdcall VCI_ResetCAN(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd);
ULONG __stdcall VCI_Transmit(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,PVCI_CAN_OBJ pSend,ULONG Len);
ULONG __stdcall VCI_Receive(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,PVCI_CAN_OBJ pReceive,ULONG Len,INT WaitTime=-1);
添加动态库到工程,如VS为例(具体怎么添加库可找教程,网上一大把):
?
?三、基本框架搭建(demo采用MFC)
void CCAN_UDS_Win32Dlg::OnBnClickedButtonOpen()
{
/*先清空MessageBox 参数*/
m_LstBoxMessage.ResetContent();
if (IsConnect == TRUE)
{
MessageBox(_T("The device is connect!"));
return;
}
/*开启设备相关初始化工作*/
VCI_INIT_CONFIG init_config;
int baud;
UpdateData(true);
init_config.AccCode = 0;
init_config.AccMask = 0xffffff;
init_config.Filter = 0;
init_config.Mode = 0;
/*Select User Baud rate*/
baud = m_ComboBaud.GetCurSel();
switch (baud)
{
case 0: //1000
init_config.Timing0 = 0;
init_config.Timing1 = 0x14;
break;
case 1: //800
init_config.Timing0 = 0;
init_config.Timing1 = 0x16;
break;
case 2: //666
init_config.Timing0 = 0x80;
init_config.Timing1 = 0xb6;
break;
case 3: //500
init_config.Timing0 = 0;
init_config.Timing1 = 0x1c;
break;
case 4://400
init_config.Timing0 = 0x80;
init_config.Timing1 = 0xfa;
break;
case 5://250
init_config.Timing0 = 0x01;
init_config.Timing1 = 0x1c;
break;
case 6://200
init_config.Timing0 = 0x81;
init_config.Timing1 = 0xfa;
break;
case 7://125
init_config.Timing0 = 0x03;
init_config.Timing1 = 0x1c;
break;
case 8://100
init_config.Timing0 = 0x04;
init_config.Timing1 = 0x1c;
break;
case 9://80
init_config.Timing0 = 0x83;
init_config.Timing1 = 0xff;
break;
case 10://50
init_config.Timing0 = 0x09;
init_config.Timing1 = 0x1c;
break;
}
if (VCI_OpenDevice(m_devtype, 0, 0) != STATUS_OK)
{
//MessageBox(_T("Open device fault!"), _T("Alarm"), MB_OK | MB_ICONQUESTION);
InsertMessageToListBox(TEXT("Open device fault!"));
return;
}
if (VCI_InitCAN(m_devtype, 0, 0, &init_config) != STATUS_OK)
{
//MessageBox(_T("Init can fault!"), _T("Alarm"), MB_OK | MB_ICONQUESTION);
InsertMessageToListBox(TEXT("Init can fault!"));
VCI_CloseDevice(m_devtype, 0);
return;
}
if (VCI_StartCAN(m_devtype, 0, 0) != 1)
{
//MessageBox(_T("The device start false!"));
InsertMessageToListBox(TEXT("The device start false!"));
}
else
{
InsertMessageToListBox(TEXT("Open device success!"));
/*创建收发线程*/
hThread[0] = (HANDLE)_beginthreadex(NULL, 0, xCAN_TxThread, pThis, 0, NULL);
hThread[1] = (HANDLE)_beginthreadex(NULL, 0, xCAN_RxThread, pThis, 0, NULL);
IsConnect = TRUE;
}
}
void CCAN_UDS_Win32Dlg::OnBnClickedButtonReset()
{
if (IsConnect == FALSE)
{
return;
}
/*断开设备相关*/
if (VCI_ResetCAN(m_devtype, 0, 0) == TRUE)
{
IsConnect = FALSE;
MessageBox(_T("The device reset ok!"));
}
else
{
MessageBox(_T("The device reset false!"));
}
}
unsigned int WINAPI xCAN_TxThread(PVOID lpParameter)
{
CCAN_UDS_Win32Dlg* dlg = (CCAN_UDS_Win32Dlg*)lpParameter;
UINT8 frameReqStatus = REQ_FALSE;
UINT8 reqStatus = xCAN_FAILURE;
while(1)
{
WaitForSingleObject(hCAN_TxEvent, INFINITE);
if (xCAN_SUCCESS == dlg->my_UDSCAN.xCAN_FrameReq(&frameReqStatus))
{
/*获取帧请求后的状态:1.准备继续发送,*/
switch (frameReqStatus)
{
case REQ_SFSUCCESS:
case REQ_FFSUCCESS:
case REQ_CFSUCCESS:
ResetEvent(hCAN_TxEvent);
break;
case REQ_CFWAITE:
break;
default:
break;
}
}
Sleep(dlg->cycleTimer);
}
return 0;
}
unsigned int WINAPI xCAN_RxThread(PVOID lpParameter)
{
CCAN_UDS_Win32Dlg* dlg = (CCAN_UDS_Win32Dlg*)lpParameter;
while(1)
{
WaitForSingleObject(hCAN_RxEvent, INFINITE);
xCAN_ReceiveFrameData(dlg);
ResetEvent(hCAN_RxEvent);
}
}
?????????除此之外,需要对文件分析做相关了解(主机厂对文件格式有要求:srec,hex等等),文件内容大致两部分,一个是bin二进制文件(要烧录到ROM的),一个是描述其地址确切位置,这里不展开介绍,网上可以搜索资料了解。另外这里补充一句,嵌入式IDE像Keil IAR等等,都有针对文件格式转换的工具,具体可了解相关文档。
四、实现UDS功能(UDS是一套完整的标准协议,其实只需要熟透ISO 15765-2?和?ISO 14229-1?用代码形式表达出来就可以了。因为有保密协议,笔者就不展示核心代码了)
|