最近在做学校的课设,用qt做一个简易的无人机地面站,需要使用串口接收单片机发来的数据。我实现串口的读取是开启一个线程来不断读取串口,有并发安全的需求,所以对serial库进行了简易的封装。

导包

import (
	"github.com/tarm/goserial"
	"io"
	"time"
)

创建一个串口连接器(封装了操作串口的相关变量)

//串口连接器
type SerialConnection struct {
	S      *io.ReadWriteCloser
	Ch     *chan []byte//开启线程用于往外发送数据的channel
	StopCh *chan struct{}//用于管理线程的channel,关闭这个channel就关闭了串口的读取线程
}

连接到串口
连接到串口号为name,波特率为baud的串口,超时时间设定为100ms
超时时间的设定关乎到数据是否能成帧显示。连接串口成功后要对两个channel进行初始化,否则会出现空指针。这两个channel是控制线程的重要工具,同时也是极易出现错误的地方

//连接串口
func (sc *SerialConnection) ConnectToSerial(name string, baud int) (err error) {

	//设置串口编号
	c := &serial.Config{Name: name, Baud: baud, ReadTimeout: time.Millisecond * 100}
	ch := make(chan []byte, 128)
	c2 := make(chan struct{}, 10)
	sc.Ch = &ch

	//打开串口,初始化指针
	s, err := serial.OpenPort(c)
	if err != nil {
		return err
	}
	sc.S = &s
	sc.StopCh = &c2
	return nil
}

读取串口内容

func (sc *SerialConnection) ReadSerial() {

	var num int
	for {

		select {
		case <-(*sc.StopCh): //关闭线程
			return
		default:
			buffer := make([]byte, MAXRWLEN) //优化,如何在运行过程中清空数组从而不用重复分配内存
			num, _ = (*sc.S).Read(buffer)
			if num > 0 {
				(*sc.Ch) <- buffer
				//fmt.Println(string(buffer))
			}
		}
	}
}

func (sc *SerialConnection) ReadSerialLoop() {
	go func() {
		sc.ReadSerial()
	}()
}

接收信息
这个函数要开一个协程来跑

//监听串口信息,并打印到TextBrowser
func (c *MainWindow) ListenShow(s *serialController.SerialConnection) {
	var buffer []byte
	for {
		select {
		case <-(*s.StopCh):
			c.TextBrowser.Append("close Serial Port")
			return
		case buffer = <-(*s.Ch):
			c.TextBrowser.Append(string(buffer)) //显示通道的信息
		}
	}
}