html对话框带有输入_创建包含源文件的IP带有参数

Vivado创建带参数的IP封装教程
本文详细介绍了如何在Vivado中创建一个包含源文件的IP,特别是带有参数的IP封装过程。从新建工程、添加源文件、设置库名和目录,到定制IP参数和GUI,最后封装IP,每个步骤都有清晰的操作指南。

有时候我们想参考官方的源码,但是有些IP怎么也找不到官方的源码,具体原因是什么呢?

下面从下面两种Vivado创建IP的流程看下具体的原因。

整个系列文章分为以下几个内容:

4e5d98141d435a2970fc031cf5b0823b.png

所谓“数字积木”,就是Vivado集成开发环境基于IP的 “积木块”设计思想。

VIvado中IP定制化流程如下:

76ed8c32ec79df2a6f27a0cb48161b30.png

来源UG896

IP目录将来自下面的IP统一到一个环境中,这些IP包括XilinxIP、第三方IP和用户IP。

基于IP-XACT标准,VivadoIP封装器工具提供了独一无二的“重用”特性。IP封装器为Vivado的任何用户提供了一种能力,即将设计流程任意阶段的一个设计进行封装,然后将该IP作为一个系统级的IP进行使用。

创建包含源文件的IP-带有参数

第一步:在操作系统下,执行菜单命令【开始】-【所有程序】-【Xilinx Design Tools】-【Vivado2018】点击【Vivado2018】,启动Vivado集成开发环境。

1746835f21ec672952518aebff5c843e.png

第二步:在“Vivado2018”主界面下,选择“Create New Project”选项,弹出“New Project-Createa New Vivado Project”对话框。

第三步:单击【Next】按钮,弹出“New Project”对话框。在该对话框中,按如下参数进行设置。

(1)Project name:gateip;

(2)Project location:XXX\test;

(3)选中“Create project subdirectory”前面的复选框。

fcc3059799bf92c167f086fd2e943dd7.png

第四步:单击【Next】按钮,弹出“New Project-Project Type”对话框。在该对话框中,按如下参数进行设置。

(1)选中“RTL Project”前面的复选框;

(2)其他按默认设置。

2bc12209221c154c64b942d152040e40.png

第七步:单击【Next】按钮,弹出“New Project-Default Part”对话框。在该对话框中,选择器件“xc7k325tffg900-2”。

34694f38566fb570ad55df10deb138a8.png

第八步:单击【Next】按钮,弹出“New Project-New Project Summary”对话框。

5de7aba2aa43e3d5e3a9f719247e145c.png

第九步:单击【Finish】按钮。

至此,完成新工程的创建。

106056bc340454724b726bc08190f21b.png

接下来添加文件

第一步:【Flow Navigateor】-【Add source】按钮

9702fe0d78aef5812105fc2eedd3e81c.png

弹出“Add Sources“对话框。在该对话框中,单击【Add or crete design source】按钮,弹出“Add Source Files”对话框。在该对话框中,定位到XXXX\source路径。

010a908f30eaa36ab6c3f082efbdd4d4.png

在该路径下,选择gate.v文件。可以看到在“Add Sources”对话框中添加了gate.v文件,并且注意下面的设置。

d6241c8b03ad92cd8aacfec2caeb80ea.png

gate.v文件

f54fbc4e2165432ac4a6c14b1c3b16eb.png

第六步:单击【Finish】按钮。

设置库名和目录的步骤如下所示

第一步:在Vivado当前工程主界面左侧的“FlowNavigator”窗口中找到并展开“PROJECT MANAGER”选项。在展开项中,单击“Settings”选项。

第二步:弹出如图所示的“Settings”对话框。在该对话框的左侧窗口中,找到并展开“IP”选项。在展开项中找到并选择“Packager”选项。在该对话框的右侧窗口中,按如下参数进行设置。

(1)Vendor:Xilinx.com

(2)Library:user

(3)Category: /UserIP

(4)其他按默认参数设置。

6896b22d43b6ab5abfc69484d48c99bb.png

第三步:单击【OK】按钮,退出“Settings”对话框。

封装定制IP的实现

封装IP的步骤如下所示。

第一步:在Vivado当前工程主界面的主菜单下,执行菜单命令【Tools】-【Createand IP Package...】;

3103e88a9921c534c6435414e46838e2.png

第二步:弹出“Create and Package New IP”对话框。

d81cf0dfbd80c99b00f4193bf1ef9ce6.png

第三步:单击【Next】按钮。

第四步:弹出“Create and Package New IP-Create Peripheral,Package IP or Packagea Block Design”对话框。在该对话框中,选中“Package your current project”前面的复选框。

42a30bc437997d01d4d11db799fac85f.png

第五步:单击【Next】按钮,弹出“Create and Package NewIP-Package Your Current Project”对话框。在该对话框中,按默认参数设置;

5c617eb1e57a4e23574a32b4d44d9891.png

第六步:单击【Next】按钮,弹出“Create and Package New IP-New IP Creation”对话框。

733cb029fa848c01bdf317dcce2f3778.png

第七步:单击【Finish】按钮,弹出“Package IP”提示对话框。

第八步:单击【OK】按钮,在Vivado右侧窗口中,出现配置IP参数的界面。如图所示,给出了“Identification”参数配置对话框。在该对话框中,按如下参数进行设置。

59c522aef258720508895657978dc421.png

(1)Library:userdefme(与前面声明的库名称一致)。

(2)Name:gate

(3)Verison:1.0

(3)Display name:gate_v1_0

(4)Description: 2 input multi_gate with same DELAY configuration parameter

(5)Vendor display name:GPNT

(6)Companyurl:空着即可

(7)其他按默认参数设置。

第九步:选择“Compatibility”选项。

8d6fdeb43980c0eaf759fe0458f2e0c6.png

第十步:弹出如图所示的“Compatibility”参数配置对话框,该配置对话框用于确认该IP所支持的FPGA的类型

第十一步:单击“File Groups”选项,弹出如图所示的“File Groups”参数配置对话框

528384457884a6e1707ab56c189a5576.png

在该对话框中,设计者可以添加一些额外的文件,如测试平台文件。

第十二步:单击 “Customization Parameters”选项。如图所示,弹出“Customization Parameters”参数配置对话框,可以看出从gate.v文件中提取了参数DELAY。

d35cf298e8fec6dc1d54aafc7fc3f622.png

第十三步:双击上图中“Delay”一行,弹出如图所示的“Edit IP Parameter”对话框。

92c391b6b8d4c8668e64a1a8de2e4896.png

(1)“Editable”选项用于决定用户是不是可以修改该参数的值,如果不想让用户修改该参数的值,则可以将“Yes”修改为“No”。

(2)“Format”选项确定值的数据格式,可选项有long、float、bool、bit string和string。设计者可以通过右侧下拉框修改数据格式的值。

(3)“Specify Range”选项用于确定其值是不是有限制。在该设计中,勾选“Specify Range”前面的复选框,表示“Delay”可选的值是有限的。

(4)在“Type”后的复选框中选择“List of values”,表示有有限个值

26822f03489ec3f2e5e3884ce23c4c55.png

第十四步:如图所示,单击该对话框中的+按钮,在“List of values”下出现输入文本框框

e867bf394014ae187a955993551328d6.png

在文本框中输入3

14a087d1177878a097bdca65bba5452f.png

按照这个方法,再添加5、7、9三个数,图中给出的是输入完4个值后的界面

d4f6197bbcaaccce8304f096e5026eef.png

在“Show As”右侧的下拉框中选择“Drop List”(表示用户可以通过下拉框选择不同的值);在“Default Value”右侧的下拉框中选择3,表示默认值为3

7ed5003671cc1d81cae71e49e9ad6926.png

第十五步:单击【OK】按钮,退出“Edit IP Parameter”对话框。

第十六步:单击“Customization GUI”选项,弹出如图所示的“Customization GUI”对话框。该对话框给出了输入/输出端口,以及带有默认值的参数选项。

e3ef3424647f0d8700ec7ece5ec4379f.png

第十七步:选择 “Review and Package”选项,弹出“Review and Package”对话框,在该对话框中单击【Package IP】按钮,如图所示。

e09c31339cb65cdbc649f867ebf9ffda.png

第十八步:弹出“Package IP”对话框,提不“Finished packaging ‘gate_v_l_0’successfully”消息,提示封装IP成功。

2286fa0facfea893d0234940ad23f413.png

第十九步:单击【OK】按钮。

第二十步:在Vivado当前工程主界面的主菜单下,执行菜单命令【File】-【Close Project】,关闭当前的工程。

看完本文有收获?请转发分享给更多人

5d79612723aec83d6b96d2e9d1adb6dd.gif

NOW

e18c0c3b660792b27d7f3cd35216c6ee.png

推荐阅读

【Vivado那些事】如何查找官网例程及如何使用官网例程

【Vivado使用误区与进阶】总结篇

【Vivado那些事】Vivado下头文件使用注意事项

【Vivado那些事】Vivado中常用的快捷键(一)F4键

【Vivado那些事】Vivado中常用的快捷键(二)其他常用快捷键

HDL Designer Series(HDS)介绍

SystemVerilog数字系统设计_夏宇闻 PDFVerilog 里面,always,assign和always@(*)区别

FPGA上如何求32个输入的最大值和次大值:分治

一文读懂TCP/IP!

《RISC-V on T-Core》学习笔记

新年愿望是什么?先送大家一波开发软件

谈谈FPGA(入门)学习的两种方式

ZYNQ-迷恋ZYNQ-FPGA开发板资源分享

零基础入门FPGA,如何学习?

点击上方字体即可跳转阅读哟

61040ddcc292b07de31760dd5dd5aff1.gif61040ddcc292b07de31760dd5dd5aff1.gif61040ddcc292b07de31760dd5dd5aff1.gif70d256a7-e553-eb11-8da9-e4434bdf6706.svg

点个在看你最好看

#include "stdafx.h" // 预编译头文件,通常用于加快编译速度 #include "ProcessResTest.h" // 包含自定义头文件 #include "ResTestDlg.h" #include "StatusLog.h" #include "Mysql.h" #include "SerialPortEx.h" #include <map> // 包含STL的map容器 #include "HttpClient.h" using namespace std; // 使用标准命名空间 // 全局变量定义 bool m_UpdateSql=true; // 控制是否更新数据库的标志,默认开启 CMysql mysql; // 数据库操作对象 CStatusLog m_StatusLog; // 日志记录对象 CString m_ServerIP=_T(""); // 服务器IP地址 CStdioFile m_WriteLog; // 文件对象,用于写日志文件 CSerialPortEx m_ResCom; // 串口通信对象,用于与电阻测试仪通信 int m_ResComNo; // 电阻测试仪串口号 int m_PrintComNo; // 打印机串口号(未使用) int m_SataionID=-1; // 当前站位ID CString m_SNID=_T(""); // SN的ID(数据库中的唯一标识) int m_SQLResult=0; // SQL操作结果 CString m_ChanleList=_T("test"); // 测试通道列表字符串,初始为"test" CString m_UpdateStr=_T(""); // 用于更新到MES的字符串 CString m_Model=_T(""); // 产品型号 CString m_SNStr=_T(""); // 产品序列号 CString m_LogPath=_T(""); // 日志文件路径 CString m_TestStation=_T(""); // 测试站名称 CString m_Operator; // 操作员 bool m_TestFinalResult; // 测试最终结果(true为通过) int m_TotalCount=0; // 测试总次数(多次测试取平均) CString m_StrDesc; // 测试描述(用于记录最大值和最小值的差值) CString m_StrMax; // 最大值字符串 CString m_StrMin; // 最小值字符串 int m_ProTestDelay=0; // 测试延迟(单位:毫秒) int m_Coun=0; // 临时计数变量 CString m_TestResult=_T(""); // 测试结果字符串 CString m_MachineID=_T(""); // 机器ID CHttpClient m_MesHttp; // HTTP客户端对象,用于与MES通信 // 定义结构体ProCahnel,用于存储每个测试通道的信息 struct ProCahnel { CString m_Upper; // 上限值 CString m_Lower; // 下限值 CString m_Count; // 测试次数(字符串形式) CString m_Moffset; // 最大允许偏移值(用于判断波动范围) double m_TestValue; // 测试值(多次测试的平均值) double m_MaxValue; // 多次测试中的最大值 double m_MinValue; // 多次测试中的最小值 CString m_Result; // 测试结果("1"为通过,"0"为失败) }; // 定义映射,键为通道名称(字符串),值为ProCahnel结构体 map <CString,ProCahnel> ProMap; // 定义映射迭代器 typedef map<CString,ProCahnel>::iterator ProMap_it; // 枚举类型,用于表示检查SN信息的结果 enum ResultList { Result_OK, // 正常通过 Result_NG, // 上一个站位NG Result_Repeat, // 当前SN已过该站位,SN重复 Result_NoRecord, // 数据库中没有记录 Result_DataBaseError, // 数据库错误 Result_StationError, // 站位错误,当前SN下一站位不是当前站位 }; void ConnectToMYSQL(void) //连接mysql数据库 { CStringA m_IP; // 定义一个ANSI字符串(CStringA)用于存储IP m_IP=m_ServerIP; // 将Unicode的m_ServerIP转换为ANSI字符串 string dbName = "mes"; // 数据库名 string host = m_IP.GetBuffer(); // 获取m_IP的缓冲区指针,用于构造string m_IP.ReleaseBuffer(); // 释放缓冲区 string user = "root"; // 用户名 string password = "liom9028"; // 密码 Database_Param Database_p(host,user,password,dbName,0,NULL,0); // 构造数据库连接参数 if(!mysql.ConnectDB(&Database_p)) // 调用ConnectDB方法连接数据库 { AfxMessageBox(_T("连接mysql数据库失败!\r\n")); // 弹出错误消息框 } else { CString m_MessageStr=_T("1>Connect TO MYSQL IS OK \r\n"); // 成功连接的消息 SendMessage(AfxGetMainWnd()->GetSafeHwnd(), WM_UPDATA_MESSAGE, 1,(LPARAM)(LPCTSTR)m_MessageStr); // 发送消息更新界面 } } int CheckSNInformation(CString m_SNID) //检查当前SN在数据库中的信息 { CString m_Buffer=_T(""); // 临时字符串 CString m_Result=_T(""); // 结果字符串(未使用) CStringA m_IDBuffer; // ANSI字符串,用于构造SQL条件 m_IDBuffer=_T("SNID=")+m_SNID+_T(" order by id desc limit 1"); // 构造查询条件:按id降序取第一条 string db_name = "mes"; // 数据库名 string tab_name = "historylist"; // 表名 string where_def =m_IDBuffer.GetBuffer(); // 获取条件字符串的缓冲区 m_IDBuffer.ReleaseBuffer(); // 释放缓冲区 string select_exp = "*"; // 选择所有列 Data_Param data_p(db_name,tab_name,where_def,select_exp,""); // 构造查询参数 mysql.SelectDB(&data_p); // 执行查询 if(mysql.SelectRecord(&data_p)) // 检查查询是否成功 { if (mysql.GetRecord()) // 是否获取到记录 { m_Buffer=mysql.row[2]; // 获取当前最新站位信息(假设第2列是站位信息) int m_StationBuffer=_ttoi(m_Buffer); // 转换为整数 if (m_SataionID!=m_StationBuffer) // 当前站位ID与数据库中的站位ID不同 { if (m_SataionID==(m_StationBuffer+1)) // 当前站位应是数据库中的下一个站位 { m_Buffer=mysql.row[3]; // 获取前一个站位的状态(第3列) mysql.FreeResult(); // 释放查询结果 if (m_Buffer==_T("1")) // 状态为1表示通过 { return Result_OK; // 允许测试 } else { AfxMessageBox(_T("当前SN在上一个站位是处于NG状态,请检查!")); // 上一个站位NG return Result_NG; } } else // 站位不连续 { mysql.FreeResult(); // 释放结果 AfxMessageBox(_T("站位错误,当前SN下一站位不是电阻测试站位,请检查!")); // 站位错误 return Result_StationError; } } else // 如果站位ID相同,检查该站位状态 { m_Buffer=mysql.row[3]; // 获取该站位的状态 if (m_Buffer==_T("0")) // 0表示未通过? { mysql.FreeResult(); // 释放结果 return Result_OK; // 允许测试 } else { mysql.FreeResult(); // 释放结果 AfxMessageBox(_T("SN 重复,当前SN已通过电阻测试站位,请检查!")); // 重复测试 return Result_Repeat; } } } else // 没有记录 { mysql.FreeResult(); // 释放结果 AfxMessageBox(_T("当前SN在数据库中没有记录,请检查!")); // 无记录 return Result_NoRecord; } } else // 查询失败 { ConnectToMYSQL(); // 尝试重新连接数据库 AfxMessageBox(_T("链接数据库失败,请检查网络链接!")); // 数据库错误 return Result_DataBaseError; } } int SearchSNList(CString m_SN) //查找当前SN信息 { CStringA m_IDBuffer; // ANSI字符串 m_IDBuffer=_T("SN='")+m_SN+_T("'"); // 构造查询条件:SN等于给定字符串 string db_name = "mes"; // 数据库名 string tab_name = "snlist"; // 表名 string where_def =m_IDBuffer.GetBuffer(); // 获取条件字符串 m_IDBuffer.ReleaseBuffer(); // 释放 string select_exp = "*"; // 选择所有列 Data_Param data_p(db_name,tab_name,where_def,select_exp,""); // 构造查询参数 mysql.SelectDB(&data_p); // 执行查询 if(mysql.SelectRecord(&data_p)) // 查询成功 { if (mysql.GetRecord()) // 获取到记录 { m_SNID=mysql.row[0]; // 第一列为SNID mysql.FreeResult(); // 释放结果 return 0; // 成功 } else // 无记录 { mysql.FreeResult(); // 释放结果 return 1; } } else // 查询失败 { ConnectToMYSQL(); // 重新连接 return 2; } } bool InsertTestResult(void) //上传测试结果到history { CString m_ResID; // 用于存储插入记录的ID(未使用) CStringA m_Valu; // ANSI字符串,用于构造插入值 if (m_TestFinalResult) // 根据测试结果构造不同的值 { m_Valu=_T("'")+m_SNID+_T("','")+m_TestStation+_T("','1',")+m_TestResult; // 测试通过 } else { m_Valu=_T("'")+m_SNID+_T("','")+m_TestStation+_T("','0',")+m_TestResult; // 测试失败 } string db_name = ""; // 数据库名(空表示使用默认) string tab_name = "historylist (SNID,StationID,TestResult,TestData)"; // 表名和列名 string where_def =""; // 无where条件 string select_exp = ""; // 无选择表达式 string m_InsertV=m_Valu.GetBuffer(); // 获取插入值字符串 m_Valu.ReleaseBuffer(); // 释放 Data_Param data_p(db_name,tab_name,where_def,select_exp,m_InsertV); // 构造插入参数 if (!mysql.InsertRecord(&data_p)) // 执行插入 { ConnectToMYSQL(); // 失败则重新连接 return false; } // 以下代码试图获取插入记录的ID,但逻辑不完整 if (mysql.SelectRecordID()) // 查询记录ID(函数名暗示) { if (mysql.GetRecord()) // 获取记录 { m_ResID=mysql.row[0]; // 保存ID mysql.FreeResult(); // 释放结果 return 1; // 成功 } else { mysql.FreeResult(); // 释放结果 return 0; // 失败 } } return 0; // 失败 } void ReadConfig(void) { CString m_FilePath; // 配置文件路径 GetModuleFileName(NULL,m_FilePath.GetBufferSetLength(MAX_PATH),MAX_PATH); // 获取当前模块文件名 m_FilePath.ReleaseBuffer(); // 释放缓冲区 m_FilePath = m_FilePath.Left( m_FilePath.ReverseFind( '\\' ) + 1 ); // 去掉文件名,保留目录 m_LogPath=m_FilePath; // 日志路径设置为当前目录 m_FilePath=m_FilePath+_T("config.ini"); // 构造配置文件路径 // 读取服务器IP GetPrivateProfileString(_T("ServerIPAdress"),_T("IP"),_T(""),m_ServerIP.GetBufferSetLength(20),20,m_FilePath); m_ServerIP.ReleaseBuffer(); // 读取机器ID GetPrivateProfileString(_T("MachieID"),_T("Machine"),_T("1"),m_MachineID.GetBufferSetLength(MAX_PATH),MAX_PATH,m_FilePath); m_MachineID.ReleaseBuffer(); // 读取电阻测试串口号 m_ResComNo=GetPrivateProfileInt(_T("ResCom"),_T("Com"),0,m_FilePath); // 读取是否启用数据库更新 int m_Data=0; m_Data=GetPrivateProfileInt(_T("Function"),_T("EnableUpdateSQL"),0,m_FilePath); if (m_Data) { m_UpdateSql=true; } else { m_UpdateSql=false; } // 读取站位ID m_SataionID=GetPrivateProfileInt(_T("StationID"),_T("ID"),-1,m_FilePath); } void WriteLogFile(void) { CTime m_time=CTime::GetCurrentTime(); // 当前时间 CString m_TestTime; // 时间字符串 CString m_FileName; // 日志文件名 m_TestTime=m_time.Format("%Y/%m/%d %H:%M:%S"); // 格式化时间 ProMap_it m_ProMap_it; // 映射迭代器 m_ProMap_it=ProMap.begin(); // 指向开始 CString m_UpperStr; // 上限字符串 CString m_LowerStr; // 下限字符串 CString m_WriteBuffer1=_T(""); // 写文件缓冲区1 CString m_WriteBuffer2=_T(""); // 写文件缓冲区2 CString m_WriteBuffer=_T(""); // 最终写内容 m_WriteBuffer1=_T("'"); // 开始构造 m_WriteBuffer1+=m_Model+_T(",'")+m_SNStr+_T(","); // 添加型号和SN CString m_Str; // 临时字符串 for (;m_ProMap_it!=ProMap.end();m_ProMap_it++) // 遍历所有通道 { m_UpperStr=m_ProMap_it->second.m_Upper; // 上限 m_LowerStr=m_ProMap_it->second.m_Lower; // 下限 // 格式化测试值(平均值为最终测试值) m_Str.Format(_T("%0.5lf"),m_ProMap_it->second.m_TestValue); m_WriteBuffer2+=m_Str+_T(","); // 添加到缓冲区2 } // 组合缓冲区1(型号和SN)、上限、下限和缓冲区2(测试值) m_WriteBuffer=m_WriteBuffer1+m_UpperStr+_T(",")+m_LowerStr+_T(",")+m_WriteBuffer2+_T(","); // 根据测试结果选择文件名 if (m_TestFinalResult) { m_FileName=m_Model+m_time.Format("_%Y-%m-%d_GOOD.csv"); // 通过的文件名 m_WriteBuffer=m_WriteBuffer+m_TestTime+_T(",PASS,")+m_Operator+_T(",\n"); // 添加时间、结果和操作员 } else { m_FileName=m_Model+m_time.Format("_%Y-%m-%d_NG.csv"); // 失败的文件名 m_WriteBuffer=m_WriteBuffer+m_TestTime+_T(",NG,")+m_Operator+_T(",\n"); } CString m_LogWritePath; // 日志文件完整路径 m_LogWritePath=m_LogPath+_T("\\log\\")+m_FileName; // 路径为当前目录下的log子目录 // 打开文件(追加模式) if ( m_WriteLog.Open(m_LogWritePath,CFile::modeCreate|CFile::modeWrite|CFile::modeNoTruncate)) { // 设置本地化,以支持中文 char* old_locale = _strdup( setlocale(LC_CTYPE,NULL) ); setlocale(LC_CTYPE, "chs" ); m_WriteLog.SeekToEnd(); // 定位到文件末尾 m_WriteLog.WriteString(m_WriteBuffer); // 写入缓冲区内容 setlocale( LC_CTYPE, old_locale ); // 恢复本地化 free( old_locale ); m_WriteLog.Close(); // 关闭文件 } } int SplitCString(CString &strIn, CStringArray& strAryRe, LPCTSTR str) //字符串分割 { assert(str!=_T("")); // 断言分隔符非空 strAryRe.RemoveAll(); // 清空结果数组 int nStart = 0; // 起始位置 int nEnd = 0; // 结束位置 CString strTmp; // 临时字符串 while ( (nEnd = strIn.Find(str, nStart)) != -1 ) // 查找分隔符 { strTmp = strIn.Mid(nStart, nEnd-nStart); // 截取子串 strAryRe.Add(strTmp); // 添加到数组 nStart = nEnd+1; // 移动起始位置 } strTmp = strIn.Mid(nStart, strIn.GetLength()); // 添加最后一个子串 strAryRe.Add(strTmp); return strAryRe.GetSize(); // 返回分割后的子串数量 } bool TestRes(void) { CString m_RestultMs=_T(""); // 从仪器接收的字符串 CString m_StrBuffer=_T(""); // 临时字符串 CString m_TestStr=_T(""); // 测试字符串(未使用) int m_Count=0; // 临时计数 int m_Index=1; // 通道索引(固定为1) double m_Upper=0.0; // 上限值(数值) double m_Lower=0.0; // 下限值(数值) double m_Test=0.0; // 测试值(数值) double m_TestMax=0.0; // 最大值(未使用) double m_TestMin=0.0; // 最小值(未使用) double m_TestVauleBuffer=0.0; // 临时存储测试值 m_TestFinalResult=true; // 默认测试结果为通过 m_UpdateStr=_T(""); // 清空更新字符串 // 循环测试次数(m_TotalCount) for (int i=0;i<m_TotalCount;i++) { m_Index=1; // 通道索引设为1 m_ResCom.ClearBuffer(); // 清空串口缓冲区 m_ResCom.WriteToPort(_T("G\n")); // 发送触发扫描命令 Sleep(m_ProTestDelay); // 延时 m_ResCom.WriteToPort(_T("?\n")); // 发送查询命令 Sleep(200); // 延时 m_RestultMs=m_ResCom.RecvData(); // 接收数据 m_StatusLog.StatusOut(_T("Receive from machine : ")+m_RestultMs); // 输出到日志 m_Count=m_RestultMs.Find(_T("=")); // 查找等号位置 if(m_Count>=0) // 找到 { m_RestultMs=m_RestultMs.Mid(m_Count+1,m_RestultMs.GetLength()-m_Count-1); // 截取等号后面的部分 if (m_RestultMs.Find(_T("mO"))>=0) // 单位:毫欧 { m_RestultMs.Replace(_T("mO"),_T("")); // 去掉单位 m_TestVauleBuffer=_ttof(m_RestultMs)/1000.0; // 转换为欧姆 } else if (m_RestultMs.Find(_T("kO"))>=0) // 单位:千欧(超量程) { AfxMessageBox(_T("测试值超过量程 当前测量值是: ") +m_RestultMs); return false; } else if(m_RestultMs.Find(_T("KO"))>=0) // 单位:千欧(KO) { m_RestultMs.Replace(_T("KO"),_T("")); m_TestVauleBuffer=_ttof(m_RestultMs)*1000; // 转换为欧姆 } else if (m_RestultMs.Find(_T("O"))>=0) // 单位:欧姆 { m_RestultMs.Replace(_T("O"),_T("")); m_TestVauleBuffer=_ttof(m_RestultMs); } else // 未知单位 { AfxMessageBox(_T("仪器返回数据异常!")); return false; } // 遍历所有通道(实际上只有一个通道,因为索引固定为1) ProMap_it m_ProMap_it; m_ProMap_it=ProMap.begin(); for (;m_ProMap_it!=ProMap.end();m_ProMap_it++) { m_Index=_ttoi(m_ProMap_it->first); // 通道索引(字符串转整数) if (m_Index==1) // 只处理通道1 { if (i==0) // 第一次测试 { m_ProMap_it->second.m_TestValue=0.0; // 初始化测试值 m_ProMap_it->second.m_MaxValue=m_TestVauleBuffer; // 最大值 m_ProMap_it->second.m_MinValue=m_TestVauleBuffer; // 最小值 m_ProMap_it->second.m_TestValue+=m_TestVauleBuffer; // 累加 } else // 后续测试 { m_ProMap_it->second.m_TestValue+=m_TestVauleBuffer; // 累加 // 更新最大值和最小值 if(m_ProMap_it->second.m_MaxValue<m_TestVauleBuffer) { m_ProMap_it->second.m_MaxValue=m_TestVauleBuffer; } if (m_ProMap_it->second.m_MinValue>m_TestVauleBuffer) { m_ProMap_it->second.m_MinValue=m_TestVauleBuffer; } } // 最后一次测试,计算平均值并判断结果 if (i==m_TotalCount-1) { m_Upper=_ttof(m_ProMap_it->second.m_Upper); // 字符串转浮点数 m_Lower=_ttof(m_ProMap_it->second.m_Lower); m_ProMap_it->second.m_TestValue=m_ProMap_it->second.m_TestValue/m_TotalCount; // 平均值 m_TestResult.Format(_T("%0.6lf"),m_ProMap_it->second.m_TestValue); // 格式化测试结果 m_Test=m_ProMap_it->second.m_TestValue; // 测试值 // 计算最大最小值差(波动范围) m_StrDesc.Format(_T("%0.6lf"),m_ProMap_it->second.m_MaxValue-m_ProMap_it->second.m_MinValue); double m_MaxOff=0.0; // 最大值 double m_MinOff=0.0; // 最小值 m_MaxOff=m_ProMap_it->second.m_MaxValue; m_MinOff=m_ProMap_it->second.m_MinValue; m_StrMax.Format(_T("%0.6lf"),m_MaxOff); // 最大值字符串 m_StrMin.Format(_T("%0.6lf"),m_MinOff); // 最小值字符串 // 判断平均值是否在上下限之间 if((m_Test>=m_Lower)&&(m_Test<=m_Upper)) { // 最大值和最小值是否也在上下限之间 if ((m_MaxOff>=m_Lower)&&(m_MaxOff<=m_Upper)) { if ((m_MinOff>=m_Lower)&&(m_MinOff<=m_Upper)) { m_ProMap_it->second.m_Result=_T("1"); // 通过 // 波动范围是否小于等于允许的最大偏移 if((_ttof(m_StrDesc))<=(_ttof(m_ProMap_it->second.m_Moffset))) { m_TestFinalResult=true ; // 最终通过 // 构造更新字符串:测试值、波动范围、最大值、最小值、状态(1) m_UpdateStr+=m_TestResult+_T(",")+m_StrDesc+_T(",")+m_StrMax+_T(",")+m_StrMin+_T(",1,"); } else { m_TestFinalResult=false ; // 波动过大 m_UpdateStr+=m_TestResult+_T(",")+m_StrDesc+_T(",")+m_StrMax+_T(",")+m_StrMin+_T(",0,"); } } else // 最小值超标 { m_TestFinalResult=false ; m_ProMap_it->second.m_Result=_T("0"); m_UpdateStr+=m_TestResult+_T(",")+m_StrDesc+_T(",")+m_StrMax+_T(",")+m_StrMin+_T(",0,"); } } else // 最大值超标 { m_TestFinalResult=false ; m_ProMap_it->second.m_Result=_T("0"); m_UpdateStr+=m_TestResult+_T(",")+m_StrDesc+_T(",")+m_StrMax+_T(",")+m_StrMin+_T(",0,"); } } else // 平均值超标 { m_TestFinalResult=false; m_ProMap_it->second.m_Result=_T("0"); m_UpdateStr+=m_TestResult+_T(",")+m_StrDesc+_T(",")+m_StrMax+_T(",")+m_StrMin+_T(",0,"); } } } else // 通道索引不是1,报错 { AfxMessageBox(_T("测试通道设定应该是1,当前是: ") +m_Index); return false; } } } else // 返回数据中没有等号 { AfxMessageBox(_T("仪器返回数据异常!")); return false; } } return true; // 测试完成 } bool RunInitOperation(void) { CString m_ResBuffer; // 串口接收缓冲区 // 打开串口 if(m_ResCom.OpenComm(m_ResComNo,9600,'N',8,0)) { // 发送查询命令 m_ResCom.WriteToPort(_T("?\n")); Sleep(100); m_ResBuffer=m_ResCom.RecvData(); // 接收数据 if (m_ResBuffer.IsEmpty()) // 无数据返回 { return false; } else { // 发送一系列设置命令 m_ResCom.WriteToPort(_T("AF\n")); //关闭数据自动返回 Sleep(200); m_ResCom.WriteToPort(_T("S7\n")); //单次触发 Sleep(200); m_ResCom.WriteToPort(_T("S4\n")); //显示R值 Sleep(200); m_ResCom.WriteToPort(_T("S3\n")); //关闭分选 Sleep(200); m_ResCom.WriteToPort(_T("S0\n")); //慢速 Sleep(200); m_ResCom.WriteToPort(_T("R0\n")); //自动量程 return true; } } else // 打开串口失败 { AfxMessageBox(_T("打开通讯串口失败!")); return false; } } UINT RunProcessResTest(LPVOID pParam) { bool m_RunMark=true; // 运行标志 bool m_PrintMark=false; // 打印标志(未使用) CString m_ResPStr=_T(""); // HTTP返回字符串 int m_Step=0; // 步骤 CString m_ChanleListOld=_T(""); // 保存上一次的通道列表 m_StatusLog.Init(_T("")); // 初始化日志文件记录类 CResTestDlg* m_PResTestDlg =(CResTestDlg*)pParam; // 对话框指针 ReadConfig(); // 读取配置 ConnectToMYSQL(); // 链接数据库 while(1) // 无限循环 { WaitForSingleObject(m_StartHandle,INFINITE); // 等待事件(开始测试) // 如果对话框要求初始化机器 if (m_PResTestDlg->m_InitMachine) { if (RunInitOperation()) // 初始化电阻测试仪 { CString m_MessageStr=_T("1> 电阻测试仪初始化成功\r\n"); SendMessage(AfxGetMainWnd()->GetSafeHwnd(), WM_UPDATA_MESSAGE, 2,(LPARAM)(LPCTSTR)m_MessageStr); } else { CString m_MessageStr=_T("1> 电阻测试仪初始化失败\r\n"); SendMessage(AfxGetMainWnd()->GetSafeHwnd(), WM_UPDATA_MESSAGE, 3,(LPARAM)(LPCTSTR)m_MessageStr); } m_PResTestDlg->m_InitMachine=false; // 重置标志 ResetEvent(m_StartHandle); // 重置事件 continue; // 继续循环 } // 从对话框获取参数 m_PrintMark=m_PResTestDlg->m_PrintEnable; // 打印使能(未使用) m_ChanleList=m_PResTestDlg->m_ProList; // 通道列表 m_TestStation=m_PResTestDlg->m_StationID; // 站位ID m_Model=m_PResTestDlg->m_ProName; // 产品型号 m_Operator=m_PResTestDlg->m_OperatorStr; // 操作员 m_ProTestDelay=m_PResTestDlg->m_TestDelay; // 测试延时 m_SNStr=m_PResTestDlg->m_SNString; // 产品序列号 m_Step=0; // 重置步骤 m_RunMark=TRUE; // 运行标志置位 while(m_RunMark) // 测试流程循环 { switch (m_Step) { case 0: //分析测试通道 { m_UpdateSql=m_PResTestDlg->m_SNEnable; // 是否更新数据库(从对话框) if (m_ChanleListOld!=m_ChanleList) // 通道列表有更新 { m_ChanleListOld=m_ChanleList; // 保存当前通道列表 ProCahnel m_ProCahnel; // 通道结构体 CStringArray m_ProArray; // 通道数组 if (!ProMap.empty()) // 清空映射 { ProMap.clear(); } // 分割通道列表字符串(逗号分隔),每6个一组 m_Coun= SplitCString(m_ChanleList,m_ProArray,_T(","))/6; if (m_Coun==0) // 分割失败 { AfxMessageBox(_T("解析测试程序文件异常!")); m_RunMark=false; // 退出 ResetEvent(m_StartHandle); // 重置事件 break; } // 本程序只支持单通道(i<1),所以只取第一组 for (int i=0;i<1;i++) { // 每组6个字段:通道索引、上限、下限、测试次数、最大偏移 m_ProCahnel.m_Upper=m_ProArray.GetAt(i*6+1); m_ProCahnel.m_Lower=m_ProArray.GetAt(i*6+2); m_ProCahnel.m_Count=m_ProArray.GetAt(i*6+3); m_TotalCount=_ttoi(m_ProArray.GetAt(i*6+3)); // 字符串转整数 m_ProCahnel.m_Moffset=m_ProArray.GetAt(i*6+4); m_ProCahnel.m_TestValue=0; // 初始化 m_ProCahnel.m_MaxValue=0; m_ProCahnel.m_MinValue=0; m_ProCahnel.m_Result=_T(""); // 插入映射,键为通道索引(第一个字段) ProMap.insert(make_pair(m_ProArray.GetAt(i*6+0),m_ProCahnel)); } } m_Step++; // 下一步 break; } case 1: //发送触发命令(进行测试) { if (!TestRes()) // 调用TestRes函数进行测试 { if(AfxMessageBox(_T("测试失败,是否重试!"),MB_YESNO)==IDYES) // 重试 { break; } else // 放弃 { m_RunMark=false; // 退出循环 ResetEvent(m_StartHandle); // 重置事件 break; } } m_Step++; // 测试成功,下一步 break; } case 2: //获取当前SN的ID { if (m_UpdateSql) // 如果需要更新数据库 { int m_SerachResult=-1; // 在snlist表中查找SN m_SerachResult=SearchSNList(m_SNStr); if (m_SerachResult==0) // 成功找到 { m_Step++; // 下一步 } else // 未找到或数据库错误 { m_SQLResult=0; // 数据库操作失败 m_Step=4; // 跳过数据库操作,直接返回结果 break; } } else // 不更新数据库 { m_SQLResult=1; // 不更新数据库时,认为SQL操作成功(跳过) m_Step=4; // 直接返回测试结果 break; } break; } case 3: //检查SN信息并插入测试结果 { // 检查SN在数据库中的状态 if (CheckSNInformation(m_SNID)!=Result_OK) { m_SQLResult=0; // 检查失败 m_Step=4; // 返回测试结果 break; } // 插入测试结果到数据库 if (!InsertTestResult()) { if(AfxMessageBox(_T("上传数据库失败,是否重试!"),MB_YESNO)==IDYES) { break; // 重试 } else { m_RunMark=false; // 退出 ResetEvent(m_StartHandle); break; } } m_SQLResult=1; // 数据库操作成功 m_Step++; // 下一步 break; } case 4: //返回测试结果数据 { // 如果需要更新MES if (m_UpdateSql) { // 调用HTTP客户端上传测试结果到MES if (!m_MesHttp.MesStartTEST(m_SNStr,m_MachineID,_T(""),m_Model,m_UpdateStr,m_ResPStr)) { m_StatusLog.StatusOut(m_ResPStr+_T("\r\n")); // 输出错误日志 AfxMessageBox(_T("上传MES失败")); // 弹出错误 // 发送消息更新界面(测试失败) SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_UpdateResult,false,(LPARAM)(LPCTSTR)m_UpdateStr); m_Step++; // 下一步 break; } m_StatusLog.StatusOut(m_ResPStr+_T("\r\n")); // 输出成功日志 } // 根据测试结果和数据库操作结果更新界面 if (m_TestFinalResult && m_SQLResult) // 测试通过且数据库操作成功 { SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_UpdateResult,true,(LPARAM)(LPCTSTR)m_UpdateStr); } else { SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_UpdateResult,false,(LPARAM)(LPCTSTR)m_UpdateStr); } m_Step++; // 下一步 break; } case 5: //写入log文件 { WriteLogFile(); // 写日志 m_Step=0; // 重置步骤 m_RunMark=false ; // 退出循环 ResetEvent(m_StartHandle); // 重置事件 break; } default:break;11:59 2025/9/15 } } Sleep(10); } return 1; // 线程返回值 } 解释代码中用到的标准库方法与函数
最新发布
09-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值