C语言零基础编写上位机软件(基于VISA库)——吉时利2306模拟电源自动测试电流项目

一、前言

        毕业一年,辗转在各电子厂之间,现在在一家主做手柄的智能控制公司,刚入职经理让尝试下个人编写吉时利2306模拟电源的控制与判断电流值的软件,只给了一个设备让我从零开发,本人很感兴趣,于是接下这一任务并从百度识图到NI-VISA官网查看C语言示例终于被我摸索出来了,以下是本人在摸索中的重点,希望能帮到各位与我一样的本科电子信息工程毕业月薪3k、梦想是全栈工程师的核动力牛马们

二、项目需求

        写项目是离不开需求的,以下是暂时完成了的需求,仅供参考

        1.Windows电脑与K2306(吉时利2306,以下略为K2306)通信并控制;

        2.K2306返回目前电流数值给Windows电脑;

        3.Windows电脑判断电流值是否在标准内;

        4.需要一个Windows程序界面,并且需要输入16位SN码与点击按钮

        5.各测试项的电流数值与PASS/FAIL的显示

        6.需要从“./”的绝对路径中的Set.txt文件内读到测试设定的标准值

        7.需要在"./Log"的绝对路径文件夹内生产命名为“now年-now月-now日 XX机型电流测试.xls”的XLS表格文件

        8.Log文件内需要自动生成测试序号,测试数据,测试时间等等

三、前期开发准备

        1.找到设备的可编程接口(一般在设备的手册内有写),一般为GPIB通信协议

        2.购买对应的接口连接线,K2306使用GPIB-USB采集卡

        3.找到使用的编程语言对应接口的库,K2306推荐VISA库:下载NI-488.2 - NI

        4.查看设备控制语言(目前使用SCPI命令为通用命令):通用SCPI命令与详细

        5.查看常用的VISA库函数:常用VISA库函数 

四、正式开发

        1.使用Visual Studio 2022编写

        2.使用EasyX图形库

  ①.确认设备正确连接

(1)设备与电脑连接        

        右键window图标打开设备管理器,其中GPIB-USB-HS图标显示为f1a5f99640e64668a7576846df1134a3.png即为正常连接,打开NI-MAX软件并找到你的设备与其主地址e491a570dd5d4fe29de7e150528fc5ec.png

 (2)设备与被测电路连接

ps:Source与Sense的正负极是必接的,DVM的正负极为充电电路,可以并联到Source与Sense一起当作恒压电池使用 

  ②.代码编写

        1.连接visa库并连接其静态库

        (1)在VS2022中点击上方的“项目”—“属性”—“VC++目录”—“包含目录”中添加:“C:\Program Files (x86)\IVI Foundation\VISA\WinNT\Include”0f23f2e96295443c8184b9e8eb21ff8d.png

     (2)在代码上方添加预编译指令:

#pragma comment(lib, "C://Program Files (x86)//IVI Foundation//VISA//WinNT//Lib_x64//msc//visa64.lib")

(ps:我使用DEBUG X64编译所以使用visa64的静态库,32位可连接visa32)

        2. 使用VISA库函数连接设备

        (1)定义连接设备函数
ViSession DefaultRM;//定义缺省设备资源
ViSession power_supply;//定义缺省设备资源
long ErrorStatus;//接受VISA函数的返回值,语句输出成功返回VI_SUCCESS
         (2)打开VISA默认资源管理器
ErrorStatus=viOpenDefaultRM(&DefaultRM);
        (3) 输入设备主地址连接
ErrorStatus=viOpen(DefaultRM, "GPIB0::1::INSTR", 0, 0, &power_supply);//中间的1为设备的子地址,可在设备上更改
        (4) 输出SCPI命令并接受返回语句
ErrorStatus = viQueryf(power_supply, "*IDN?\n", "%100c", IDN);

viQuery(vi,writeFmt,readFmt,arg1,arg2…)按设定格式对器件进行数据读写。

这里设定%100c读取格式是为方便读取其中的\s \t等格式

*IDN? 为通用SCPI命令,意思是询问设备详细信息

        (5)连接设备详细代码(自编写可能不太严谨)
void OpenPort()
{
	char VISA_address[40];
	char IDN[1024] = "";

	char SetVoltage[20];
	char Voltage_address[40];
	if (bGPIB)//定义了int bGPIB=1防止越界
	{
		strcpy(VISA_address, "GPIB0::");
		strcat(VISA_address, GPIB_address);//这里在后面编写了Fopen读取txt内参数储存
		strcat(VISA_address, "::INSTR");
		ErrorStatus = viOpenDefaultRM(&DefaultRM);
		CheckError("viOpenDefaultRM Failed!");
		ErrorStatus = viOpen(DefaultRM, VISA_address, 0, 0, &power_supply);
		CheckError("Unable to OpenPort!");
		if (ErrorStatus != VI_SUCCESS)
		{
			return;
		}
		ErrorStatus = viQueryf(power_supply, "*IDN?\n", "%100c", IDN);
		CheckError("Unable to Link!");
		if (ErrorStatus == VI_SUCCESS)
		{
			set_Device();//自写设置设备具体数值的函数
		}
		if (!bGPIB)
			viPrintf(power_supply, "System:Remote");
	}

	//printf("VISA_address:%s\n",VISA_address);
}

// 2024-8-1 12:00编写,后续会更新,先写到这里

        (6) 检查ERROR的自定义函数CheckError()的详细代码
void CheckError(const char* pMassage)
{
	if (ErrorStatus != VI_SUCCESS)
	{
		MessageBox(GetHWnd(), pMassage, "错误!", MB_OK);//弹一个确认按钮的提示窗口
	}
}//用的EasyX图形库

3.SCPI命令设置设备

 (1) 输出命令前的注意事项

 !!!重要的事情说三遍: 输出命令后面一定要带‘\n’

 !!!重要的事情说三遍: 输出命令后面一定要带‘\n’

 !!!重要的事情说三遍: 输出命令后面一定要带‘\n’ 

(2)设置设备通道输出电压值

首先,SCPI设置设备电压的命令为SOUR1:VOLT 3.5\n (其中1为通道口,切换2通道即为SOUR2:VOLT 3.5\n特别注意VOLT与数值中间有空格!)

而后,VISA库的命令输出函数为viPrintf(vi,arg1)

因此设置电压命令的代码:

ErrorStatus = viPrintf(power_supply, "SOUR1:VOLT 1.25\n");

power_supply 在 2.(1)已经定义过了

(3)设置设备通道输出电流最大限值

与上方电压值设置大同小异,SCPI设置设备电流的命令为SOUR1:CURR 1.5\n (其中1为通道口,切换2通道即为SOUR2:CURR 1.5\n特别注意CURR与数值中间有空格!)

因此设置电流命令的代码: 

ErrorStatus = viPrintf(power_supply, "SOUR1:CURR 1.5\n");

power_supply 在 2.(1)已经定义过了 

 (4)设置设备的模块化详细代码(自写不严谨)
void set_Device()
{
	char SetVoltageA[20]="";
	char SetVoltageB[20] = "";
	char VoltageA_address[40]="";
	char VoltageB_address[40] = "";
	strcpy(SetVoltageA, txtVolt.Text4());//此处由后面fopen获取的电流范围值设置
	strcpy(SetVoltageB, txtVolt.Text2());//此处由后面fopen获取的电流范围值设置
	strcpy(VoltageA_address, "SOUR1:VOLT ");
	strcat(VoltageA_address, SetVoltageA);
	strcat(VoltageA_address, "\n");
	strcpy(VoltageB_address, "SOUR2:VOLT ");
	strcat(VoltageB_address, SetVoltageB);
	strcat(VoltageB_address, "\n");
	ErrorStatus = viPrintf(power_supply, VoltageA_address);
	ErrorStatus = viPrintf(power_supply, "SOUR1:CURR 1.25\n");
	ErrorStatus = viPrintf(power_supply, VoltageB_address);
	ErrorStatus = viPrintf(power_supply, "SOUR2:CURR 1.25\n");
	CheckError("仪器设置失败");
}

PS:我在这里设置电流最大值为1.25是因为通常开机需要一个强电流,如果后续连接设备电源发现板子不开机可能会是这里的电流限制了

 4.使用SCPI命令询问设备返回当前电流值

询问电流的SCPI命令为:MEAS2:CURR?\n(其中2为通道,修改为1即切换到通道1询问)

询问设备当前通道电流的代码:

double current=0;
ErrorStatus = viQueryf(power_supply, "MEAS2:CURR?\n", "%lf", &current);

// 2024-8-3 10:00编写,其实到这里关于K2306的连接、输出及询问已经写完了,可以根据自己需要编写其中的逻辑了,后续还会更新通过类传参避免出现全局变量、如何通过fopen和fclose读取txt文件中的范围值并传参入代码、如何创建以当天日期生成LOG文件的xls格式并自动输出每一个的测试序号,测试数据,测试时间等等

//后续会更新 

5.显示界面与交互编程

(1)确定所使用的图形库并下载

        我所使用图形库为EasyX:下载EasyX

        下载后直接运行点击安装就行了,需要先安装好Visual Studio

        (现在在学习WINAPI的图形界面编程,但是我推荐如果不是和我一样只喜欢C语言的话还是用JAVA或者使用QT的C++图形库会更加便捷)

(2)EasyX界面编写
         *1* 添加EasyX图形库头文件
#include<easy.h>
          *2* 创建窗口界面 

EasyX的创建窗口界面分为三部曲:

        1设置窗口大小: HWND initgraph(int width, int height, int flag = 0);//width为长,height为宽,flag=0则不显示cmd命令行窗口,为=1则显示

        2清屏显示:void cleardevice()

        3关闭窗口:void closegraph();

详细代码为:

#include<stdio.h>
#include<easyx.h>

int main()
{
	
	initgraph(800, 600);//设置窗口大小
	setbkcolor(WHITE);//设置窗口背景颜色
	cleardevice();//清屏
	system("pause");//需要暂停函数以便显示观察,不然则窗口一闪而过关闭
	closegraph();//关闭窗口
}
        *3* 封装图形库的按钮及文本框 

   这一部分是核心内容,要讲的比较多,后面我会专门写一个文章讲这里,目前暂时先放上整个的详细代码略过

输入文本框详细代码:
class EasyTextBox
{
private:
    int left = 0, top = 0, right = 0, bottom = 0;   // 控件坐标
    TCHAR* text = NULL;                         // 控件内容
    size_t maxlen = 0; // 文本框最大内容长度
 
public:
    void Create(int x1, int y1, int x2, int y2, int max)
    {
        maxlen = max;
        text = new TCHAR[maxlen];
        text[0] = 0;
        left = x1, top = y1, right = x2, bottom = y2;

        // 绘制用户界面
        Show();
    }

    ~EasyTextBox()
    {
        if (text != NULL)
            delete[] text;
    }

    TCHAR* Text()
    {
        return text;
    }

    /*char* Text2()
    {
        return text2;
    }
    char* Text3()
    {
        return text3;
    }
    char* Text4()
    {
        return text4;
    }*/

    bool Check(int x, int y)
    {
        return (left <= x && x <= right && top <= y && y <= bottom);
    }

    // 绘制界面
    void Show()
    {
        // 备份环境值
        int oldlinecolor = getlinecolor();
        int oldbkcolor = getbkcolor();
        int oldfillcolor = getfillcolor();

        setlinecolor(LIGHTGRAY);        // 设置画线颜色
        setbkcolor(0xeeeeee);           // 设置背景颜色
        setfillcolor(0xeeeeee);         // 设置填充颜色
        fillrectangle(left, top, right, bottom);
        outtextxy(left + 10, top + 5, text);
        /*outtextxy(left + (right - left - textwidth(text) + 1) / 2, top + (bottom - top - textheight(text) + 1) / 2, text);*/

        // 恢复环境值
        setlinecolor(oldlinecolor);
        setbkcolor(oldbkcolor);
        setfillcolor(oldfillcolor);
    }

    void GetMessage1(int max,char *str)
    {
        maxlen = max;
        text = new TCHAR[maxlen];
        strcpy(text, str);
    }
    void ClearMessage(int max)
    {
        maxlen = max;
        text[maxlen] = NULL;
        size_t len = _tcslen(text);
        int width = textwidth(text);
        for(;len<=maxlen&&len>0;len--)
        {
            text[len - 1] = 0;
            width = textwidth(text);
            clearrectangle(left + 10 + width, top + 1, right - 1, bottom - 1);
        }
        Show();
    }

    int TXT_MAXreturn()
    {
        size_t len = _tcslen(text);
        if (len == maxlen - 1)
            return 1;
        else
            return 0;
    }

    void Result(int input)
    {
        success +=input;
    }

    int Result_get()
    {
        return success;
    }

    void Clear_Result()
    {
        success =0;
    }
    void OnMessage()
    {
        // 备份环境值
        int oldlinecolor = getlinecolor();
        int oldbkcolor = getbkcolor();
        int oldfillcolor = getfillcolor();

        setlinecolor(BLACK);            // 设置画线颜色
        setbkcolor(WHITE);              // 设置背景颜色
        setfillcolor(WHITE);            // 设置填充颜色
        fillrectangle(left, top, right, bottom);
        outtextxy(left + 10, top + 5, text);

        int width = textwidth(text);    // 字符串总宽度
        int counter = 0;                // 光标闪烁计数器
        bool binput = true;             // 是否输入中

        ExMessage msg;
        while (binput)
        {
            while (binput && peekmessage(&msg, EX_MOUSE | EX_CHAR, false))  // 获取消息,但不从消息队列拿出
            {
                if (msg.message == WM_LBUTTONDOWN)
                {
                    // 如果鼠标点击文本框外面,结束文本输入
                    if (msg.x < left || msg.x > right || msg.y < top || msg.y > bottom)
                    {
                        binput = false;
                        break;
                    }
                }
                else if (msg.message == WM_CHAR)
                {
                    size_t len = _tcslen(text);
                    switch (msg.ch)
                    {
                    case '\b':              // 用户按退格键,删掉一个字符
                        if (len > 0)
                        {
                            text[len - 1] = 0;
                            width = textwidth(text);
                            counter = 0;
                            clearrectangle(left + 10 + width, top + 1, right - 1, bottom - 1);
                        }
                        break;
                    case '\r':              // 用户按回车键,结束文本输入
                    case '\n':
                        binput = false;
                        break;
                    default:                // 用户按其它键,接受文本输入
                        if (len < maxlen - 1)
                        {
                            text[len++] = msg.ch;
                            text[len] = 0;

                            clearrectangle(left + 10 + width + 1, top + 3, left + 10 + width + 1, bottom - 3);  // 清除画的光标
                            width = textwidth(text);                // 重新计算文本框宽度
                            counter = 0;
                            outtextxy(left + 10, top + 5, text); // 输出新的字符串
                        }
                    }
                }
                peekmessage(NULL, EX_MOUSE | EX_CHAR);              // 从消息队列抛弃刚刚处理过的一个消息
            }

            // 绘制光标(光标闪烁周期为 20ms * 32)
            counter = (counter + 1) % 32;
            if (counter < 16)
                line(left + 10 + width + 1, top + 3, left + 10 + width + 1, bottom - 3);                // 画光标
            else
                clearrectangle(left + 10 + width + 1, top + 3, left + 10 + width + 1, bottom - 3);      // 擦光标

            // 延时 20ms
            Sleep(20);
        }

        clearrectangle(left + 10 + width + 1, top + 3, left + 10 + width + 1, bottom - 3);  // 擦光标

        // 恢复环境值
        setlinecolor(oldlinecolor);
        setbkcolor(oldbkcolor);
        setfillcolor(oldfillcolor);

        Show();
    }
};//文本框控件
按钮封装详细代码:
class EasyButton
{
private:
	int left = 0, top = 0, right = 0, bottom = 0;	// 控件坐标
	TCHAR* text = NULL;							// 控件内容
	void (*userfunc)() = NULL;						// 控件消息
    //float scale=1.0f;                                    //缩放比例
    bool checkMouse;                                 //是否在按钮上方
public:
	void Create(int x1, int y1, int x2, int y2, const TCHAR* title, void (*func)())
	{
		text = new TCHAR[_tcslen(title) + 1];
		_tcscpy(text, title);
		left = x1, top = y1, right = x2, bottom = y2;
		userfunc = func;

		// 绘制用户界面
		Show();
	}

	~EasyButton()
	{
		if (text != NULL)
			delete[] text;
	}

	bool Check(int x, int y)
	{
        /*checkMouse= (left <= x && x <= right && top <= y && y <= bottom);
            Show();*/
        /*checkMouse = (left <= x && x <= right && top <= y && y <= bottom);*/
        return (left <= x && x <= right && top <= y && y <= bottom);
	}

	// 绘制界面
	void Show()
	{
		int oldlinecolor = getlinecolor();
		int oldbkcolor = getbkcolor();
		int oldfillcolor = getfillcolor();

  //      setlinecolor(BLACK);			// 设置画线颜色
		//setbkcolor(WHITE);				// 设置背景颜色
		//setfillcolor(BLACK);			// 设置填充颜色
  //      setlinestyle(PS_SOLID, 2);
  //      setfillcolor(WHITE);
        if (checkMouse ==true)
        {
            setlinecolor(RGB(0, 120, 215));
            setfillcolor(RGB(229, 241, 251));
            //scale = 0.8f;
        }
        else
        {
            setlinecolor(RGB(173, 173, 173));
            setfillcolor(RGB(225, 225, 225));
            //scale = 1.0f;
        }
        
        fillrectangle(left, top, right, bottom);
        settextcolor(BLACK);
        setbkmode(TRANSPARENT);
		outtextxy(left + (right - left - textwidth(text) + 1) / 2, top + (bottom - top - textheight(text) + 1) / 2, text);
		setlinecolor(oldlinecolor);
		setbkcolor(oldbkcolor);
		setfillcolor(oldfillcolor);
	}

	void OnMessage()//双运行实现点击效果
	{    
        checkMouse = true;
        Show();
        Sleep(20);
        if (userfunc != NULL)
			userfunc();
        checkMouse = false;
        Show();
	}

    int ExitMessage()
    {
        checkMouse = true;
        Show();
        Sleep(20);
        if (userfunc != NULL)
            userfunc();
        checkMouse = false;
        clearrectangle(left, top, right, bottom);
        return 0;
    }
    //void OnMessage()
    //{
    //    bool checkMouse=true;
    //    ExMessage msg;
    //    while (checkMouse)
    //    {
    //        while (checkMouse && peekmessage(&msg, EX_MOUSE))
    //        {
    //            if (msg.message == WM_LBUTTONDOWN)
    //            {
    //            
    //                if (msg.x < left || msg.x > right || msg.y < top || msg.y > bottom)//移动到外面停止
    //                {
    //                    checkMouse = false;
    //                    break;
    //                }
    //                else
    //                {
    //                    if (userfunc != NULL)
    //                        userfunc();
    //                }
    //            }
    //            Show();
    //        }
    //        
    //    }


    //}
};//按钮控件
        *4* 界面文本显示及怎么使用封装的文本框和按钮

首先,文本显示函数为 void outtextxy(int x, int y, TCHAR c);//x轴坐标,y轴坐标,TCHAR类型字符串

而在EasyX界面中,坐标轴为Y轴是正数的第四象限:

其次,在输出文字显示之前,可以设置文字颜色、背景模式等等

设置文字颜色:void settextcolor(COLORREF color)

        其中COLORREF 已在.h宏定义:

设置文字背景模式:void setbkmode(int mode);

OPAQUE为不透明,默认使用当前背景颜色

TRANSPARENT为透明色

//2024-8-5 17:15 编写,后续会更新

                

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值