在学校里的团队里,遇到需要与下位机实现串口通信的项目,假期里查了很多资料,在此发一篇加深一下印象
需要:
电脑,vs软件(visual studio 2010)
Serial Port Tester(串口调试工具)
友善串口调试助手(选择添加COM1和COM2)
第一步
打开文件,新建,项目
选择Windows窗体应用程序,随意命名
点击视图,选择工具箱(找到button,和serialport,拖动到窗体上)
鼠标右键点击serialport,查看属性,默认值不改变(可以自己更改,选择可用串口);再双击button1进入
在button1点击事件中写简单代码,调试运行,再打开串口助手选择asc2接收(注意,虚拟串口成对出现,这里是COM1和COM2,且目前只能发送一次,多点会出错,后面用try catch 解决)
serialport1.Open();
string str ="easy";
serialport1.write(str);
第二步
退出运行后,继续添加控件,并在属性栏中修改控件名称和控件显示的文本(如将button1改为buttonOpen)
再修改代码,使可以关闭串口,和将文本框的字符发送的功能
第三步
添加接收文本框,修改名称后将属性设置为多行,垂直滚动
双击进入serialport属性里的datareceive事件
在datareceived事件里编写代码,遇到跨线程错误后,在Form1里加入一句代码,使不进行跨线程检查
string str =serialport1.ReadExisisting();
textBoxReceive.AppendText(str);
在form1里添加代码后,用串口助手发送asc2数据,接收文本框显示接收到的数据(还不能发汉字)
public Form1()
{
InitializeComponent();
//防止跨线程错误
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
}
到现在的,基本的发送接收已经完成了,但是可能会出现很多问题,比如说:串口可能多次打开会出错,无法发送和接收文字,无法像助手一样选择发送接收模式…所以继续优化,问题慢慢解决
第四步
添加串口,波特率下拉框,并编辑内容
添加panel容器并往容器上加单选框,更改属性名称和文本
再在打开按钮上加上try catch 语句,避免运行过程中死机,在form1load再给控件初始化
private void Form1_Load(object sender, EventArgs e)
{
radioButtonReceiveHex.Checked = true; //接收单选框默认值为Hex
radioButtonSendHex.Checked = true; //发送单选框默认值为Hex
comboBox1.Text = "COM1"; //默认值
comboBox2.Text = "9600";
}
再对发送和接收事件进行优化,可选择Asc2和Hex模式(代码在最后)
再在form1中改变串口接送的编码,实现汉字转化
serialPort1.Encoding = Encoding.GetEncoding("GB2312");
此时的程序与下位机匹配时,即可实现简单的数据通讯了
本次的完整代码如下
*Form1
public Form1()
{
InitializeComponent();
//串口接收编码
serialPort1.Encoding = Encoding.GetEncoding("GB2312");
//防止跨线程错误
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
}
Form1_Load
private void Form1_Load(object sender, EventArgs e)
{
radioButtonReceiveHex.Checked = true; //默认值为Hex
radioButtonSendHex.Checked = true; //默认值为Hex
comboBox1.Text = "COM1"; //默认值
comboBox2.Text = "9600";
}
打开按钮事件
private void buttonOpen_Click(object sender, EventArgs e)
{
if (buttonOpen.Text == "打开串口")//判断文字
{
try //尝试以下代码,未能完全执行就执行catch
{
serialPort1.PortName = comboBox1.Text; //将串口文本框的COM值传给serialport
serialPort1.BaudRate = Convert.ToInt16(comboBox2.Text); //j将波特率文本框的值转成数字给serialport
serialPort1.Close(); //关闭串口
serialPort1.Open(); //打开串口,若未打开,则不往下执行
buttonOpen.Text = "关闭串口"; //将按钮文本"打开串口"改为"关闭串口"
}
catch (Exception err)
{
MessageBox.Show(err.ToString(), "错误");//弹出消息提示
}
}
else
{
serialPort1.Close(); //关闭串口
buttonOpen.Text = "打开串口"; //将按钮文本"关闭串口"改为"打开串口"
}
}
串口接收事件
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
if (radioButtonReceiveAsc2.Checked) //如果接收模式为字符模式
{
string str = serialPort1.ReadExisting(); //字符串方式读
textBoxReceive.AppendText(str); //将接收文本添加到文本框中
}
else //如果接收模式为数值接收
{
byte data;
data = (byte)serialPort1.ReadByte(); //此处需要强制类型转换,将(int)类型数据转换为(byte类型数据,不必考虑是否会丢失数据
string str = Convert.ToString(data, 16).ToUpper(); //转换为大写十六进制字符串
textBoxReceive.AppendText("0x" + (str.Length == 1 ? "0" + str : str) + " ");//空位补“0”
//上一句等同为:if(str.Length == 1)
// str = "0" + str;
// else
// str = str;
// textBox1.AppendText("0x" + str);
}
}
发送按钮的事件
private void buttonSend_Click(object sender, EventArgs e)
{
if (serialPort1.IsOpen)
{
if (radioButtonSendAsc2.Checked)//如果发送模式是字符模式
{
serialPort1.WriteLine(textBoxSend.Text);//写数据
}
else
{
try
{
byte[] Data = new byte[1]; //用于缓存
for (int i = 0; i < (textBoxSend.Text.Length - textBoxSend.Text.Length % 2) / 2; i++)//取余3运算作用是防止用户输入的字符为奇数个
{
Data[0] = Convert.ToByte(textBoxSend.Text.Substring(i * 2, 2), 16);
serialPort1.Write(Data, 0, 1); //循环发送(如果输入字符为0A0BB,则只发送0A,0B)
}
if (textBoxSend.Text.Length % 2 != 0)//剩下一位单独处理
{
Data[0] = Convert.ToByte(textBoxSend.Text.Substring(textBoxSend.Text.Length - 1, 1), 16);//单独发送B(0B)
serialPort1.Write(Data, 0, 1); //发送
}
}
catch(Exception err)
{
MessageBox.Show(err.ToString(), "错误");//出错提示
serialPort1.Close();
buttonOpen.Text = "打开串口"; //将按钮文本"关闭串口"改为"打开串口"
}
}
}
}
最后的话,自己定义一套通讯协议,与相应的下位机连接试试吧!