第一个完整自己做的项目,记录一下
一、功能概述
通过调用DLL,可以读取配置文件的数据,定时发送CAN数据,实时接收CAN数据。实现2362、8602、8201的初始化,并可以通过参数控制某个通道的输入/输出。实现对电源的远程控制(程序控制)。
二、主要函数介绍
(1)Can_Read_excel(float temp[]);//读取can配置文件,参数是给dataname对应模型的值赋值。
(2)Io_Read_excel();//读取IO配置文件
(3)OninitCan1();//初始化Can1
(4)OninitCan2();//初始化Can2
(5)OninitAD_Io();//初始化所有AD、Io板卡
(6)SetIo2362(int IsPort_2362,int IsStatus_2362);//设置2362某管脚高低电平,参数:通道(0-47),状态0/1
(7)SetAD8201(int IsVolt_8201,int IsWritePort_8201);//模拟量输出,参数:电压(0-4095),输出通道(0-3)
(8)SetAD8602_1(int IsVolt_8602,int IsWritePort_8602,int IsReadPort_8602);//设置8602的输出通道及输出电压和采集的通道,参数:输出电压(0-4095),输出通道(0-3),采集通道(0-31)
(9)CTimer:定时器的回调函数,can数据发送的实现函数,实现定时发送can数据
(10)Rev_CAN_IO_thread():接收线程函数,实现实时接收数据
(11)Recv_Send();//用于启动接收线程和定时器,DLL供外部调用的接口
(12)Power(int Power_volt,int Power_curr,int com);电源控制函数,参数:设置电源电压值、电流值、与电源通信的串口。
三、具体实现
1.动态库的生成和使用
MFC->MFC DLL之后选择你需要的类型。
为了让dll导出函数,需要在每一个需要被导出的函数前面加上标识符:__declspec(dllexport)。
在编译链接时,C++会按照自己的规则篡改函数的名称,这一过程称为“名字改编”。这会导致不同的编译器、不同的语言下调用dll发生问题。因此我们希望动态链接库文件在编译时,导出函数的名称不要发生变化。
为了实现这一目的,可以再定义导出函数时加上限定符:extern “C”,如
extern "C" { void __declspec(dllexport)__stdcall fun(){......}; }。
2.全局变量
//CAN配置文件变量
注:使用vector容器需包含头文件vector
vector<CString>m_can_dataname;
vector<CString>m_send_id;
vector<CString>m_id;
vector<int>m_cycle_time;
vector<int>m_start_bit;
vector<double>m_length;
vector<double>m_factor;
vector<CString>m_byte_order;
vector<int>m_offset;
vector<CString>m_type;
vector<double>m_db_dataname_value;//存储can数据库的dataname列对应的模型中的值
vector<int>m_db_flag;//存储can数据库的s/r列,表示can数据时发送还是接收
//IO配置文件变量
vector<CString>m_io_dataname;
vector<int>m_io_flag;
vector<long>m_io_port;
vector<int>m_io_low_high;
vector<long>m_io_usemanuval;
CApplication ExcelApp;
CWorkbooks books;
CWorkbook book;
CWorksheets sheets;
CWorksheet sheet;
CRange range;
HANDLE m_hDevice2362;//PCI2362采集开句柄
HANDLE m_hDevice8602;//PCI8602采集开句柄
HANDLE m_hDevice8201;//PCI8201采集开句柄
HANDLE m_hDevice8602_2;//PCI8602采集开句柄
CCanCtr m_PCI9840_CAN;//PCI9840控制对象
CCanCtr m_PCI9840_CAN_2;//PCI9840控制对象
PCI2362Ctr m_PCI2362;//PCI2362控制对象
PCI8602Ctr m_PCI8602_2; //PCI8602控制对象
PCI8602Ctr m_PCI8602; //PCI8602控制对象
PCI8201Ctr m_PCI8201; //PCI8201控制对象
3.读取配置文件
void __declspec(dllexport)__stdcall Can_Read_excel(float temp[])
{
if (!ExcelApp.CreateDispatch(_T("Excel.Application"), NULL))
{
AfxMessageBox(_T("启动Excel服务器失败!"));
exit(1);
}
ExcelApp.put_Visible(TRUE);//可见
ExcelApp.put_UserControl(FALSE);//用户可控制
LPDISPATCH lpDisp=NULL;
COleVariant vResult;
COleVariant covOptional((long)DISP_E_PARAMNOTFOUND,VT_ERROR);
//清空vector数据
m_db_dataname_value.clear();
m_can_dataname.clear();
m_id.clear();
m_send_id.clear();
m_cycle_time.clear();
m_start_bit.clear();
m_length.clear();
m_factor.clear();
m_byte_order.clear();
m_offset.clear();
m_type.clear();
m_db_flag.clear();
/*得到工作簿容器*/
books.AttachDispatch(ExcelApp.get_Workbooks());
CString str=_T("E:\\canconfig1.xls");//打开配置文件的路径
lpDisp=books.Open(str,covOptional,covOptional,covOptional,covOptional,covOptional,covOptional,covOptional,covOptional,covOptional,covOptional,covOptional,covOptional,covOptional,covOptional);
//得到workbook
book.AttachDispatch(lpDisp);//打开一个Workbook或者创建一个Workbook
//得到worksheets
sheets.AttachDispatch(book.get_Worksheets());
//得到当前活跃sheet
//如果有单元格正处于编辑状态中,此操作不能返回,会一直等待
lpDisp = book.get_ActiveSheet();
sheet.AttachDispatch(lpDisp);
//获得使用的区域Range(区域)
CRange usedRange;
usedRange.AttachDispatch(sheet.get_UsedRange());
range.AttachDispatch(usedRange.get_Rows());
//获得使用的行数
long rownum=0;
rownum=range.get_Count();
//获得使用的列数
long columnum=0;
range.AttachDispatch(usedRange.get_Columns());
columnum=range.get_Count();
CString str2;
CString str1;
CString str3;
int m_test1;
double m_test2;
for(int i=2;i<rownum+1;i++)
{
//第一列数据
m_db_dataname_value.push_back(0.0);//push_back在数组的最后添加一个数据0.0
range.AttachDispatch(sheet.get_Cells());
range.AttachDispatch(range.get_Item(COleVariant((long)(i)), COleVariant((long)1)).pdispVal);
vResult = range.get_Value2();
m_can_dataname.push_back(vResult.bstrVal);//将数据添加到0后头
//str1=vResult.bstrVa