简介:
使用C#,开发平台VS2010,功能是串口通讯。功能很简单,仅供大家学习,效果如图:
1.使用控件
控件类型 | 命名 | 用途 | 说明 |
ComboBox | cbSerival | 串口 | |
ComboBox | cbBuadRate | 波特率 | |
ComboBox | cbDataBits | 数据位 | |
ComboBox | cbStop | 停止位 | |
ComboBox | cbParity | 校验位 | |
Button | btnOpen | 打开 | |
Button | btnClear | 清除 | |
Button | btnSend | 发送 | |
RadioButton | rbRecHEX | 接收格式:HEX | |
RadioButton | rbRecASCII | 接收格式:ASCII | |
RadioButton | rbSendHEX | 发送格式:HEX | |
RadioButton | rbSendASCII | 发送格式:ACSII | |
CheckBox | cbRTS | RTS | |
CheckBox | cbDTR | DTR | |
CheckBox | cbNewLine | 换行 | |
TextBox | tbRec | 接收编辑框 | ReadOnly:True,Multiline:True |
TextBox | tbSend | 发送编辑框 | |
SerialPort | serialPort1 | 表示串行端口资源 | |
StatusStrip | statusStrip1 | 状态栏 | LayoutStyle:Table |
ToolStripStatusLabel | tsslDateTime | 时间 | BorderSiders:Right |
ToolStripStatusLabel | toolStripStatusLabel1 | 让R和S在状态栏的右边 | 紧跟tsslDateTime后边,Spring:True |
ToolStripStatusLabel | toolStripStatusLabel2 | 发送字节统计 S: | BorderSiders:Left |
ToolStripStatusLabel | tsslSendCount | 发送总字节数 | BorderSiders:Right |
ToolStripStatusLabel | toolStripStatusLabel3 | 接收字节统计 R: | |
ToolStripStatusLabel | tsslRecCount | 接收总字节数 |
2.操作一波
(1)控件添加
要做到本界面的效果需要把上面控件全添加进去,StatusStrip下面的控件,是在StatusStrip中添加的,小白请勿发懵。
(2)初始化
说明:串口和控件初始化。
private void Form1_Load(object sender, EventArgs e)
{
//串口
string[] str = SerialPort.GetPortNames();
foreach (string port in str)
{
cbSerial.Items.Add(port);
}
cbSerial.SelectedIndex = 0;
//波特率
cbBuadRate.Items.Add("300");
cbBuadRate.Items.Add("1200");
cbBuadRate.Items.Add("2400");
cbBuadRate.Items.Add("4800");
cbBuadRate.Items.Add("9600");
cbBuadRate.Items.Add("19200");
cbBuadRate.Items.Add("38400");
cbBuadRate.Items.Add("43000");
cbBuadRate.Items.Add("56000");
cbBuadRate.Items.Add("115200");
cbBuadRate.SelectedIndex = 4;
//数据位
cbDataBits.Items.Add("5");
cbDataBits.Items.Add("6");
cbDataBits.Items.Add("7");
cbDataBits.Items.Add("8");
cbDataBits.SelectedIndex = 3;
//停止位
cbStop.Items.Add("0");
cbStop.Items.Add("1");
cbStop.Items.Add("1.5");
cbStop.Items.Add("2");
cbStop.SelectedIndex = 1;
//校验位
cbParity.Items.Add("无");
cbParity.Items.Add("奇校验");
cbParity.Items.Add("偶校验");
cbParity.SelectedIndex = 0;
btnSend.Enabled = false;
rbRecASCII.Checked = true;
rbSendASCII.Checked = true;
//状态控件
tsslDateTime.Text = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");
tsslRecCount.Text = "0";
tsslSendCount.Text = "0";
}
(3)“打开”按钮
private void btnOpen_Click(object sender, EventArgs e)
{
if(cbSerial.Items.Count <= 0)
{
MessageBox.Show("当前无可用串口!");
return;
}
if (serialPort1.IsOpen == false)
{
serialPort1.PortName = cbSerial.SelectedItem.ToString();
serialPort1.BaudRate = Convert.ToInt32(cbBuadRate.SelectedItem.ToString());
serialPort1.DataBits = Convert.ToInt32(cbDataBits.SelectedItem.ToString());
serialPort1.StopBits = (StopBits)Convert.ToInt32(cbStop.SelectedItem.ToString());
serialPort1.Parity = (Parity)Convert.ToInt32(cbParity.SelectedIndex);
try
{
serialPort1.Open();
btnSend.Enabled = true;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "打开COM错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
btnOpen.Text = "关闭";
}
else
{
try
{
serialPort1.Close();
btnSend.Enabled = false;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "关闭COM错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
btnOpen.Text = "打开";
}
cbSerial.Enabled = !serialPort1.IsOpen;
cbBuadRate.Enabled = !serialPort1.IsOpen;
cbDataBits.Enabled = !serialPort1.IsOpen;
cbStop.Enabled = !serialPort1.IsOpen;
cbParity.Enabled = !serialPort1.IsOpen;
}
(4)“清除”按钮
private void btnClear_Click(object sender, EventArgs e)
{
tbRec.Clear();
tbSend.Clear();
}
(5)“发送”按钮
private void btnSend_Click(object sender, EventArgs e)
{
byte[] sendData = null;
string strData = null;
if (cbNewLine.Checked)
{
strData = tbSend.Text.Trim() + "\r\n";
}
else
{
strData = tbSend.Text.Trim();
}
if (rbSendHEX.Checked)
{
sendData = strToHexByte(strData);
}
else if (rbSendASCII.Checked)
{
sendData = Encoding.ASCII.GetBytes(strData);
}
if (this.Send(sendData))
{
statusStrip1.Invoke(new MethodInvoker(delegate
{
if (tsslSendCount.Text == "0")
{
tsslSendCount.Text = tbSend.Text.Length.ToString();
}
else
{
try
{
tsslSendCount.Text = (int.Parse(tsslSendCount.Text) + tbSend.Text.Length).ToString();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "发送字节计数错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}));
}
}
(6)格式切换
说明:rbSendHEX和rbSendASCII添加CheckedChanged事件,接收格式同理。
private void rbSendHEX_CheckedChanged(object sender, EventArgs e)
{
byte[] data = null;
data = System.Text.Encoding.Default.GetBytes(tbSend.Text.Trim());
if (rbSendHEX.Checked)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < data.Length; i++)
{
sb.AppendFormat("{0:x2}" + " ", data[i]);
}
tbSend.Text = sb.ToString().ToUpper();
}
}
private void rbSendASCII_CheckedChanged(object sender, EventArgs e)
{
if (rbSendASCII.Checked)
{
byte[] data = null;
if (tbSend.Text == "")
{
return;
}
data = HexStringToByte(tbSend.Text.Trim());
tbSend.Text = new ASCIIEncoding().GetString(data);
}
}
3.数据类型转换
(1) 16进制转字节数组
public static byte[] HexStringToByte(string hexstring)
{
string[] tmpary = hexstring.Trim().Split(' ');
byte[] buff = new byte[tmpary.Length];
for (int i = 0; i < buff.Length; i++)
{
buff[i] = Convert.ToByte(tmpary[i], 16);
}
return buff;
}
(2)字符串转换16进制字节数组
private byte[] strToHexByte(string hexString)
{
hexString = hexString.Replace(" ", "");
if ((hexString.Length % 2) != 0)
hexString += " ";
byte[] returnBytes = new byte[hexString.Length / 2];
try
{
for (int i = 0; i < returnBytes.Length; i++)
returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2).Replace(" ", ""), 16);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "转换成HEX错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
return returnBytes;
}
4.串口
(1)串口发送
public bool Send(byte[] data)
{
if (serialPort1.IsOpen)
{
try
{
serialPort1.Write(data, 0, data.Length);
return true;
}
catch(Exception ex)
{
MessageBox.Show(ex.Message, "发送数据错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
MessageBox.Show("未打开串口", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
return false;
}
(2)串口接收
说明:添加serialPort1的DataReceived事件
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] ReDatas = new byte[serialPort1.BytesToRead];
serialPort1.Read(ReDatas, 0, ReDatas.Length);
this.AddData(ReDatas);
}
public void AddData(byte[] data)
{
if (rbRecHEX.Checked)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < data.Length; i ++ )
{
sb.AppendFormat("{0:x2}" + " ", data[i]);
}
AddContent(sb.ToString().ToUpper());
}
else if (rbRecASCII.Checked)
{
AddContent(new ASCIIEncoding().GetString(data));
}
statusStrip1.Invoke(new MethodInvoker(delegate
{
if (tsslRecCount.Text == "0")
{
tsslRecCount.Text = tbRec.Text.Length.ToString();
}
else
{
try
{
tsslRecCount.Text = (int.Parse(tsslRecCount.Text) + data.Length).ToString();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "接收字节计数错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}));
}
private void AddContent(string content)
{
this.BeginInvoke(new MethodInvoker(delegate
{
tbRec.AppendText(content);
}));
}
5.疑难点
说明:在WinForm中使用多线程时,当在子线程中修改一个控件的值或者进行其它操作时,会出错。不过此工程未使用子线程,可直接对控件控制,效果是等价的。
if (tsslRecCount.Text == "0")
{
tsslRecCount.Text = tbRec.Text.Length.ToString();
}
else
{
tsslRecCount.Text = (int.Parse(tsslRecCount.Text) + data.Length).ToString();
}
statusStrip1.Invoke(new MethodInvoker(delegate
{
if (tsslRecCount.Text == "0")
{
tsslRecCount.Text = tbRec.Text.Length.ToString();
}
else
{
try
{
tsslRecCount.Text = (int.Parse(tsslRecCount.Text) + data.Length).ToString();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "接收字节计数错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}));
注意:BeginInvoke与Invoke一个是同步执行,一个是异步执行。简单的可以理解为,当使用Invoke时,当前线程(非UI线程)会阻塞然后UI线程执行Invoke里传入的方法,之后再继续执行当前线程。而使用BeginInvoke时,当前线程不会阻塞,而是继续执行,当当前线程执行完毕后,UI线程才会执行BeginInvoke传入的方法。