帖子只用于记录本人的学习记录,算是给未来的自己出一份傻瓜式教学。
在C#语言上,一般上位机是由Winform开发,也有WPF,但是我不会WPF
上位机无非就是发送数据与接收数据,可以使用C#的SerialPort方法
winform自带SerialPort控件,可以将此控件直接拖到页面上,然后实例化该控件,也可以直接实例化SerialPort类,这样就不需要SerialPort控件了,个人建议这样,省去一个控件
下面的代码我是使用SerialPort控件来开发,控件名为:serialPort1
1、定义一个串口参数结构体,写在命名空间
//串口参数结构体
struct COMPORT_ATTRIBUTE
{
public int bandrate;//波特率
public int data_bit;//数据位
public Parity parity_check_bit;//校验位
public StopBits stop_bit;//停止位
public string comport_number;//最新的串口名称
};
2、给串口参数设置一些常用的默认值,我写在了窗体加载事件里
//波特率设置
cb_BoTeLv.DataSource = new string[] { "4800", "9600", "14400", "19200", "38400", "56000", "57600", "115200", "128000", "256000" };
cb_BoTeLv.SelectedIndex = 1;
//初始化波特率
uart_port.bandrate = int.Parse(cb_BoTeLv.Text);
// 初始化数据位
cb_DataBits.DataSource = new string[] { "8", "7", "6", "5" };
cb_DataBits.SelectedIndex = 0;
uart_port.data_bit = int.Parse(cb_DataBits.Text);
// 初始化停止位
cb_StopBits.DataSource = new string[] { "0", "1", "2", "1.5" };
cb_StopBits.SelectedIndex = 1;
uart_port.stop_bit = (StopBits)cb_StopBits.SelectedIndex;
// 初始化校验位
cb_Parity.DataSource = new string[] { "无", "奇校验", "偶校验", "1", "0" };
cb_Parity.SelectedIndex = 0;
uart_port.parity_check_bit = (Parity)cb_Parity.SelectedIndex;
3、设置完参数以后,就可以进行打开或者关闭串口的操作了,打开串口需要选择串口号,需要用户自己去选择,我们应将扫描到的串口绑定在页面上供用户选择,下面是扫描串口和打开、关闭的方法
/// <summary>
/// 扫描串口
/// </summary>
public void InitializePorts()
{
string[] port_names = SerialPort.GetPortNames();
string last_name = "";
cb_DuanKou.Items.Clear();//清除数据
if (port_names == null)
{
cb_DuanKou.Text = "无串口";
return;
}
foreach (string s in SerialPort.GetPortNames())
{
//获取有多少个COM口就添加进COMBOX项目列表
cb_DuanKou.Items.Add(s);
last_name = s;//保存最新的一个
}
cb_DuanKou.Text = last_name;//显示最新的一个串口
uart_port.comport_number = last_name;//赋值变量
}
/// <summary>
/// 打开串口
/// </summary>
private void OpenSerialPort()
{
if (serialPort1.IsOpen)
{
return;
}
try
{
serialPort1.PortName = cb_DuanKou.Text;//端口号
serialPort1.BaudRate = uart_port.bandrate;//波特率
serialPort1.DataBits = uart_port.data_bit; //数据位
serialPort1.Parity = uart_port.parity_check_bit;//校验位
serialPort1.StopBits = uart_port.stop_bit; //停止位
serialPort1.Open();
AddText("端口打开成功");
}
catch(Exception ex)
{
AddText("端口打开失败,"+ex.Message);
}
}
/// <summary>
/// 关闭串口
/// </summary>
private void CloseSerialPort()
{
if (!serialPort1.IsOpen)
{
return;
}
try
{
serialPort1.Close();
AddText("端口关闭成功");
}
catch (Exception ex)
{
AddText("端口关闭失败," + ex.Message);
}
}
其中 AddText()是我定义的一个提示方法,为了更让用户使用方便,我还加了许多颜色和控件提示,在这里的代码中我阉割掉了,开发的时候要站在用户的角度
4、打开串口以后就可以进行收发数据,串口是以byte数组的形式进行传输,我们需要将用户输入的内容或者定好的内容转成byte数字进行发送
4.1、发送
BitConverter.GetBytes(Convert.ToInt16(data))
此段代码是C#封好的,将用户输入的int16类型的数据转换为byte字节,对应的Float32类型和Double类型为ToSingle和ToDouble,别的数据类型也是一样,int8是直接ToByte
特别注意:C#的所有转换,不管是字节转数字还是数字转字节都是按照低位在前来转换的,有些需要高位在前的需要转换完以后再Reverse()一下。
将所有要发送的字节放到一个byte[]数组里,我是先用List<byte>将字节逐个添加,然后直接 byte[] sendData = list.ToArray();
这样sendData就是我们要发送的字节数组,发送方法很简单:
serialPort1.Write(sendData, 0, sendData.Length);
在发送完成以后,我们可以调用
serialPort1.DiscardOutBuffer();
来清除发送缓存区的内容,可以避免一些后续数据发错的问题;
示例:
List<byte> list = new List<byte>();//新建一个list数组临时用
//添加要发送的字节
list.AddRange(BitConverter.GetBytes(Convert.ToInt16(data));
list.AddRange(BitConverter.GetBytes(Convert.ToUInt16(data));
list.AddRange(BitConverter.GetBytes(Convert.ToInt32(data));
list.AddRange(BitConverter.GetBytes(Convert.ToUInt32(data));
list.AddRange(BitConverter.GetBytes(Convert.ToSingle(data));
list.AddRange(BitConverter.GetBytes(Convert.ToDouble(data));
//将list数组放到byte数组里
byte[] sendData = list.ToArray();
//通过打开过的串口发送数据
serialPort1.Write(sendData, 0, sendData.Length);
//给数据发送一些时间然后清除发送缓存区,清除时间自己算一下发送需要多久
Thread.Sleep(300);
serialPort1.DiscardOutBuffer();
//完成
4.2、接收
把接收事件写在serialPort1_DataReceived里,这是控件自带的事件
先让他睡0.1秒,防止我们读取的时候数据还没接收完
Thread.Sleep(100);
随后获取接收缓存区的数据
byte[] b = new byte[serialPort.BytesToRead];
serialPort.Read(b, 0, b.Length);
b就是接收到的字节数据
接下来怎么处理这些数据就看业务需要了,把字节转换为数字的方法是
BitConverter.ToSingle(b, 4).ToString();
这是从下标为4的字节开始,往后取4个字节转换为浮点数,别的数据类型也是一样的格式,接收完毕以后清除一下接收缓存区的内容
serialPort1.DiscardInBuffer();
一次完整的通讯到此结束