golang,go语言实现仪表控制可以采用2种方式,一种是使用CGO编程,使用驱动自带的VISA.H文件进行开发,但是感觉怪怪的,感觉这样还不如直接使用C进行开发,还有现成的版本可用。因此本文使用的是调用dll的方式来进行。

  • 常用函数

viOpenDefaultRM:打开设备管理器,初始化。

func OpenRM() uintptr {    VISA32 := syscall.NewLazyDLL("visa32.dll")    viOpenDefaultRM := VISA32.NewProc("viOpenDefaultRM")    ret, _, _ := viOpenDefaultRM.Call(uintptr(unsafe.Pointer(&resourceManager)))    fmt.Println("硬件信息为:", resourceManager)    return ret}

viFindRsrc:查询仪表清单。

func FindRsrc() bool {    VISA32 := syscall.NewLazyDLL("visa32.dll")    viFindRsrc := VISA32.NewProc("viFindRsrc")    var list int = 0    instrDor := getMyString("?*")    var data [180]byte    retcnt := 0    ret, _, _ := viFindRsrc.Call(uintptr(resourceManager), uintptr(unsafe.Pointer(instrDor.Str)), uintptr(unsafe.Pointer(&list)), uintptr(unsafe.Pointer(&retcnt)), uintptr(unsafe.Pointer(&data)))    if ret != 0 {        fmt.Println("查询代码:", ret)        return false    }    fmt.Println("查询成功->")    viFindNext := VISA32.NewProc("viFindNext")    viFindNext.Call(uintptr(list), uintptr(unsafe.Pointer(&data)))    s := string(Bytes2string(data))    viClose := VISA32.NewProc("viClose")    viClose.Call(uintptr(list))    fmt.Println(s)    return true}

viOpen:使用地址打开仪表。

    instrDor := getMyString(addr)    VISA32 := syscall.NewLazyDLL("visa32.dll")    viOpen := VISA32.NewProc("viOpen")    var instr int = 0    ret, _, _ := viOpen.Call(uintptr(resourceManager), uintptr(unsafe.Pointer(instrDor.Str)), uintptr(0), uintptr(0), uintptr(unsafe.Pointer(&instr)))    if instr == 0 {        return "打开仪器失败,错误代码为:" + fmt.Sprint(ret)    }

viClose:关闭设备管理器,关闭仪器清单,关闭打开的仪表(可以理解为释放内存)上面的3个函数都要。

viPrintf/viScanf:发送和接收仪表的数据

// 发送信息给仪表// addr为仪器地址,m为需要发送的信息,如GPIB0::15::INSTR,send:DISP TXfunc SendMsg(addr, m string) string {    instrDor := getMyString(addr)    VISA32 := syscall.NewLazyDLL("visa32.dll")    viOpen := VISA32.NewProc("viOpen")    var instr int = 0    ret, _, _ := viOpen.Call(uintptr(resourceManager), uintptr(unsafe.Pointer(instrDor.Str)), uintptr(0), uintptr(0), uintptr(unsafe.Pointer(&instr)))    if instr == 0 {        return "打开仪器失败,错误代码为:" + fmt.Sprint(ret)    }    m = m + "/n"    msg := getMyString(m)    viPrintf := VISA32.NewProc("viPrintf")    ret2, _, _ := viPrintf.Call(uintptr(instr), uintptr(unsafe.Pointer(msg.Str)))    viClose := VISA32.NewProc("viClose")    viClose.Call(uintptr(instr))    if ret2 == 0 {        return "信息发送成功!---" + m    }    return "发送失败,代码为:" + fmt.Sprint(ret2)}// 发送信息给仪表,并等待仪表返回数据。func ReadData(addr, m string) string {    instrDor := getMyString(addr)    VISA32 := syscall.NewLazyDLL("visa32.dll")    viOpen := VISA32.NewProc("viOpen")    var instr int = 0    ret, _, _ := viOpen.Call(uintptr(resourceManager), uintptr(unsafe.Pointer(instrDor.Str)), uintptr(0), uintptr(0), uintptr(unsafe.Pointer(&instr)))    if instr == 0 {        return "打开仪器失败,错误代码为:" + fmt.Sprint(ret)    }    m = m + "/n"    msg := getMyString(m)    viPrintf := VISA32.NewProc("viPrintf")    viPrintf.Call(uintptr(instr), uintptr(unsafe.Pointer(msg.Str)))    readFmt := getMyString("%t")    viScanf := VISA32.NewProc("viScanf")    var data [180]byte    viScanf.Call(uintptr(instr), uintptr(unsafe.Pointer(readFmt.Str)), uintptr(unsafe.Pointer(&data)))    s := string(Bytes2string(data))    viClose := VISA32.NewProc("viClose")    viClose.Call(uintptr(instr))    return s}// 返回的数据后面有很多个0,截断处理会更好些。func Bytes2string(data [180]byte) string {	for i := 0; i < len(data); i++ {		if data[i] == 10 || data[i] == 0 {			return string(data[:i])		}	}	return string(data[:])}
  • 常见的坑

go语言的string与C的string是有所区别的,gostring的结构是首字符的指针+长度,而Cstring的结构只有首字符指针,然后读取时顺序读到字符0为止。因此在传递中要进行转换,比如:

使用CGO使用C.CString进行转换,但这个方式会导致内存不能释放。

文中采用了另一种方式,改造结构体的方式,因此采用了另一种结构体MyString。

// 新建类型,代替C.CString// C.CString使用会产生拷贝,并不会自动释放,需要进行free。type MyString struct {    Str unsafe.Pointer    Len int}//类型转换// /x00是必须的,go的string是由首字符指针+长度组成。而C的string只有首字符指针,长度由字节0来确定,即顺序读,直到读到0。/x00即代表字符0func getMyString(s string) *MyString {    s = s + "/x00"    return (*MyString)(unsafe.Pointer(&s))}
包的下载,目前还没有完善,后续会上传一个共享包和示例程序,着急的也可以留言。