通信界面的设计,485通信需要有检验位,停止位,数据位,波特率四个参数设置,以及有关于电脑的串口号选择。
设计一个大概的界面,这些中文都可以右击属性,名字直接改就好了。
我们可以对这些选择添加选择项目,并通过索引赋予其初始选择的项,例如波特率有1200,2400,4800,9600,通过this.comboBox2.SelectedIndex刚打开时默认选择9600波特率
private void Form2_Load(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)//最大支持到串口 10,可根据自己需求增加
{
this.comboBox1.Items.Add("COM" + (i + 1).ToString());
}
this.comboBox1.SelectedIndex = 0; //刚开始默认的选择位
this.comboBox2.Items.Add("1200"); //数据通信速率选择
this.comboBox2.Items.Add("4800");
this.comboBox2.Items.Add("9600");
this.comboBox2.Items.Add("115200");
this.comboBox2.SelectedIndex = 2;
this.comboBox5.Items.Add("奇校验"); //检验选择
this.comboBox5.Items.Add("偶校验");
this.comboBox5.Items.Add("无");
this.comboBox5.SelectedIndex = 0;
this.comboBox4.Items.Add("1"); //数据位数选择
this.comboBox4.Items.Add("2");
this.comboBox4.Items.Add("3");
this.comboBox4.Items.Add("4");
this.comboBox4.Items.Add("5");
this.comboBox4.Items.Add("6");
this.comboBox4.Items.Add("7");
this.comboBox4.Items.Add("8");
this.comboBox4.SelectedIndex = 7;
this.comboBox3.Items.Add("1"); //停止位选择
this.comboBox3.Items.Add("2");
this.comboBox3.Items.Add("无");
this.comboBox3.SelectedIndex = 0;
Form1 form = new Form1();
form.ShowDialog();
this.Hide();
}
检测是否有可用 的电脑串口号
我的电脑串口插上去是3,所以1到10够用了,有一些串口的串口号超过10的时候,这里循环检测的时候就要增加 i 的数值了,确保你的串口号在循环检测内,不然会检测不到。这部分代码是在串口检测按钮的代码内,双极检测串口即可进入代码段:
private void button1_Click(object sender, EventArgs e)
{
bool comExistence = false;//有可用串口标志位
this.comboBox1.Items.Clear();
for (int i = 0; i < 10; i++)
{
try
{
SerialPort sp = new SerialPort("COM" + (i + 1).ToString());
sp.Open();
sp.Close();
this.comboBox1.Items.Add("COM" + (i + 1).ToString());
comExistence = true;
}
catch (Exception)
{
continue;
}
}
if (comExistence)
{
this.comboBox1.SelectedIndex = 0;//使 ListBox 显示第 1 个添加的索引
}
else
{
MessageBox.Show("没有找到可用串口!", "错误提示");
}
}
检查完串口之后,我们就根据串口通信要求去设置,串口通信,需要设置波特率,校验位,停止位,数据位。
private void SetPortProperty()//设置串口的属性
{
sp = new SerialPort();
sp.PortName = this.comboBox1.Text.Trim();//设置串口名
sp.BaudRate = Convert.ToInt32(this.comboBox2.Text.Trim());//设置串口的波特率
float f = Convert.ToSingle(this.comboBox3.Text.Trim());//设置停止位
if (f == 0)
{
sp.StopBits = StopBits.None;
}
else if (f == 1)
{
sp.StopBits = StopBits.One;
}
else if (f == 1.5)
{
sp.StopBits = StopBits.OnePointFive;
}
else if (f == 2)
{
sp.StopBits = StopBits.Two;
}
else
{
sp.StopBits = StopBits.One;
}
sp.DataBits = Convert.ToInt16(this.comboBox4.Text.Trim());//设置数据位
string s = this.comboBox5.Text.Trim(); //设置奇偶校验位
if (s.CompareTo("无") == 0)
{
sp.Parity = Parity.None;
}
else if (s.CompareTo("奇校验") == 0)
{
sp.Parity = Parity.Odd;
}
else if (s.CompareTo("偶校验") == 0)
{
sp.Parity = Parity.Even;
}
else
{
sp.Parity = Parity.None;
}
sp.ReadTimeout = -1;//设置超时读取时间
sp.RtsEnable = true;
sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
}
打开串口
设置完之后,就可以去打开串口,这部分代码在打开串口的按钮内:
在打开串口时,同时也检查一遍波特率等是否设置了,没有设置会显示报错
private void button2_Click(object sender, EventArgs e)
{
if (isOpen == false)
{
if (!CheckPortSetting())//检测串口设置
{
MessageBox.Show("串口未设置!", "错误提示");
return;
}
if (!isSetProperty)//串口未设置则设置串口
{
SetPortProperty();
isSetProperty = true;
}
try//打开串口
{
sp.Open();
isOpen = true;
button2.Text = "关闭串口";
//串口打开后则相关的串口设置按钮便不可再用
comboBox1.Enabled = false;
comboBox2.Enabled = false;
comboBox4.Enabled = false;
comboBox5.Enabled = false;
comboBox3.Enabled = false;
}
catch (Exception)
{
//打开串口失败后,相应标志位取消
isSetProperty = false;
isOpen = false;
MessageBox.Show("串口无效或已被占用!", "错误提示");
}
}
else
{
try//打开串口
{
sp.Close();
isOpen = false;
isSetProperty = false;
button2.Text = "打开串口";
//关闭串口后,串口设置选项便可以继续使用
comboBox1.Enabled = true;
comboBox2.Enabled = true;
comboBox4.Enabled = true;
comboBox5.Enabled = true;
comboBox3.Enabled = true;
}
catch (Exception)
{
MessageBox.Show("关闭串口时发生错误!", "错误提示");
}
}
}
发送数据
打开串口后,就是发送数据了
在发送数据那个按钮内:
if (isOpen)
{
if (this.textBox2.Text == "")//写串口数据
{
MessageBox.Show("请输入数据!", "错误提示");
return;
}
else
{
Send_485();
}
}
else
{
MessageBox.Show("串口未打开,请打开串口!", "错误提示");
}
然后写Send_485()函数的代码,这一部分是讲text1里面的文本数值转化为int类型的数组,因为我们写进去是文本类型的,需要转换,而且这个框内只能写0-f,不然会报错, throw new ArgumentException(“hex is not a valid hex number!”, “hex”);跳到这一行函数
public void Send_485()
{
byte[] sendbuf = new byte[textBox2.Text.Length / 2]; //tbxSendData.Text.ToCharArray();
for (int i = 0; i < sendbuf.Length; i++)
{
try
{
// 每两个字符是一个 byte,将字符转化为数值形式,在放入数组内
sendbuf[i] = byte.Parse(textBox2.Text.Substring(i * 2, 2),
System.Globalization.NumberStyles.HexNumber);
}
catch
{
// Rethrow an exception with custom message.
throw new ArgumentException("hex is not a valid hex number!", "hex");
}
}
SendModbusData(ref sendbuf);
}
以下是SendModbusData(ref sendbuf)的代码:
public bool SendModbusData(ref byte[] values)
{
string result = string.Join(",", values);
if (sp.IsOpen)
{
//Clear in/out buffers:
sp.DiscardOutBuffer();//清空发送、接收缓冲区字节
sp.DiscardInBuffer();
textBox1.Text = result;
//Function 3 response buffer:
byte[] response = new byte[values.Length +2];
//把value数组复制到response数组
Array.Copy(values, response, values.Length);
byte[] CRC = new byte[2];
GetCRC(response, ref CRC);
response[0] = Convert.ToByte(response[0]);//地址
response[1] = Convert.ToByte(response[1]);//功能
//values[2] = (byte)(Convert.ToByte(values[2])>>8);//寄存器地址
response[2] = Convert.ToByte(response[2]);
response[3] = Convert.ToByte(response[3]);
//values[2] = (byte)(Convert.ToByte(values[]) >> 8);//寄存器个数
response[4] = Convert.ToByte(response[4]);
response[5] = Convert.ToByte(response[5]);
response[response.Length - 2] = CRC[0];
response[response.Length - 1] = CRC[1];
values = response; //返回带有 CRC 验证的modbus 数据包
Console.WriteLine(values.Length);
for (int i=0;i<values.Length;i++)
{
Console.WriteLine(values[i]);//控制台输出打包后的Modbus数据
}
//Send modbus message to Serial Port:
try
{
sp.Write(values, 0, values.Length);
return true;
}
catch (Exception)
{
return false;
}
}
else
{
MessageBox.Show("串口未打开!");
return false;
}
}
这里需要CRC校验:
public void GetCRC(byte[] message, ref byte[] CRC)
{
ushort CRCFull = 0xFFFF;
byte CRCHigh = 0xFF, CRCLow = 0xFF;
char CRCLSB;
for (int i = 0; i < (message.Length) - 2; i++)
{
CRCFull = (ushort)(CRCFull ^ message[i]);
for (int j = 0; j < 8; j++)
{
CRCLSB = (char)(CRCFull & 0x0001);
CRCFull = (ushort)((CRCFull >> 1) & 0x7FFF);
if (CRCLSB == 1)
CRCFull = (ushort)(CRCFull ^ 0xA001);
}
}
CRC[1] = CRCHigh = (byte)((CRCFull >> 8) & 0xFF);
CRC[0] = CRCLow = (byte)(CRCFull & 0xFF);
}
以上就是通过winform向485通信发送的代码,将485串口插入电脑上,仿真时发送数据即会有数据发出,可以通过示波器去捕捉波形来验证。