前一篇文章讲了利用Arduino mega2560来设计上下位机串口通信系统的下位机,这一章则要讲如何设计上位机界面软件,这里我们先选用了VS2013自带的Visual C#。这是微软公司开发的C#编程语言规格之集成开发环境使用者接口,可以帮助开发者快速地设计出一款适合自己的界面软件。
using System.IO.Ports;
接着在代码区public Form1()这个函数里编写串口相关代码,因为串口操作需要界面软件初始化就开始使用,所以直接将这部分代码写在InitializeComponent()之后即可。当然如果有需求的,可以在打开串口操作成功后加上状态按钮,显示更加清楚。namespace SimpleHostPrint
{
public partial class BOTDR : Form
{
//通过System.IO.Ports实例化一个串口对象s,这一步要定义为全局变量,后面各个函数才能使用
SerialPort ss = new SerialPort();
public BOTDR() //这里我把图形界面名Form1改名成了自己需要的名字,相应的类名也会跟着变化
{
InitializeComponent();
Control.CheckForIllegalCrossThreadCalls = false; //放置跨线程访问出错
SerialSwitch.Text = "打开串口"; // "SerialSwitch"Button的初始状态
//定义一个Item数组,遍历item中每一个波特率变量a,增加到"BaudRate"ComboBox的列表中
int[] item = {
9600, 115200 };
foreach (int a in item)
{
BaudRate.Items.Add(a.ToString());
}
BaudRate.SelectedItem = BaudRate.Items[0]; //默认为列表第1个变量
}
//这个函数界面软件启动时会载入
private void Form1_Load(object sender, EventArgs e)
{
//获取当前的所有的串口名字,存入字符串数组
String[] ports = SerialPort.GetPortNames();
//把这个串口名字符串数组全都加载进"SerialChoose"ComboBox里
SerialChoose.Items.AddRange(ports);
//"SerialChoose"ComboBox默认选择第一个串口名
SerialChoose.SelectedItem = SerialChoose.Items[0];
}
//"SerialSwitch"Button对应的代码,点击该Button即触发此段代码
private void SerialSwitch_Click(object sender, EventArgs e)
{
try
{
if (!ss.IsOpen) //检查串口是否打开
{
//将选择的串口名赋给实例化的串口ss
ss.PortName = SerialChoose.SelectedItem.ToString();
//将选择的波特率赋给实例化的串口ss
ss.BaudRate = Convert.ToInt32(BaudRate.SelectedItem.ToString());
//串口打开
ss.Open();
//串口接收数据的函数
ss.DataReceived += ss_DataReceived;
SerialSwitch.Text = "关闭串口";
}
else
{
ss.Close();
ss.DataReceived -= ss_DataReceived;
SerialSwitch.Text = "打开串口";
}
}
catch (Exception ee)
{
MessageBox.Show(ee.ToString());
}
}
//异或校验函数,用于命令字当中
private byte CalBIP8(byte[] data)
{
byte bip16 = Convert.ToByte(data[0] ^ (data[1] & 0x00) ^ data[2] ^ data[3] ^ data[4] ^ data[5] ^ data[6] ^ data[7]);
byte bip8 = Convert.ToByte(((bip16 & 0xf0) >> 4) ^ (bip16 & 0x0f));
return bip8;
}
}
}
界面软件启动后串口的初始状态:
选择好串口名和波特率,点击"打开串口"按钮后的状态:
private void OpticalSwitchSetting_Click(object sender, EventArgs e) //设置光开关通道号
{
if (ss.IsOpen)
{
if (SetChannelNumber.Text.Trim() != "") //当输入框有数据输入时
{
//自定义的带有命令字和数值的数据帧
byte [] txdata = {
0xfb, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0xfe};
//将设置的光开关通道号数值写入数据帧对应位置
txdata[4] = Convert.ToByte(SetChannelNumber.Text.Trim());
//生成异或校验码
txdata[1] = CalBIP8(txdata);
int IsCompleted = 0;
for(int i=0;i<8;i++)
{
//串口发送函数
ss.Write(txdata, i, 1);
IsCompleted += 1;
}
//验证发送的字节数
if (IsCompleted == txdata.Length)
{
MessageBox.Show("下发数据成功");
}
else
{
MessageBox.Show("下发数据失败"); return;
}
}
else
{
MessageBox.Show("未设置通道号"); return;
}
}
else
{
MessageBox.Show("串口未打开"); return;
}
}
ss.DataReceived += ss_DataReceived;
写到ss.DataReceoved += 时,可以根据提示按“TAB”键,自动链接串口接收函数 ss_DataReceived(),即ss这个串口的数据接收应当由函数ss_DataReceived()来处理,剩下的就是编写这个函数内的代码,如下所示。void s_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//读取串口缓冲区的数据长度
int count = ss.BytesToRead;
string str = null;
//将串口缓冲区的数据全部存入buff数组
byte[] buff = new byte[count];
ss.Read(buff, 0, count);
//这部分代码是做了一个RichTextBox并以十六进制显示,并提供清除数据功能
foreach (byte item in buff)
{
//读取buff中存的数据,转换成显示的十六进制数
if (HEX2.Checked)
{
str += item.ToString("X2") + " ";
}
else
{
str += item.ToString() + " ";
//Convert.ToByte(buff[0]);
}
}
//这是跨线程访问RichTextBox,原程序和DataReceived事件是两个不同的线程同时在执行
ReceivedDataTextBox.Text += System.DateTime.Now.ToString() + ": " + Encoding.ASCII.GetString(buff,0,1) + str.Remove(0, 2) + "\n";
//统计接收的数据字节数
ReceivedDataCount.Invoke(new MethodInvoker(delegate
{
ReceivedDataCount.Text = (int.Parse(ReceivedDataCount.Text) + count).ToString(); }));
//返回的数据帧自定义,根据各自需要自行处理
if (buff[0] == 'D')
{
if (buff[1] == OpticalSwitch)
{
//"ChannelNumber" TextBox里显示收到的参数值
ChannelNumber.Text = buff[2].ToString();
}
}
else if (buff[0] == 'S')
{
MessageBox.Show("下位机设置成功!");
}
else if (buff[0] == 'W')
{
MessageBox.Show("警告!模块电流或温度超过阈值!");
}
else if (buff[0] == 'E')
{
MessageBox.Show("模块设置错误!");
}
}
//点击"清除接收区"Button按钮清空串口接收缓冲区的数据
private void ClearReceivedData_Click(object sender, EventArgs e)
{
ReceivedDataTextBox.Clear();
ReceivedDataCount.Invoke(new MethodInvoker(delegate {
ReceivedDataCount.Text = "0"; }));
}
当所有的工作做完之后,点击工具栏的"启动"按钮,排除错误和报警后,即可运行一个Debug下的界面程序(也可以在配置管理器里选择生成Release版本的),在"设置通道号"里输入数值3,整个上下位机串口通信系统运行成功后,显示结果如下: