选择visual c#---->窗体应用程序
GroupBox
拖拽上去6个Label,位置和显示如下
拖拽上去5个ComboBox,1个Button,位置如下
设置Button的显示改为打开串口
添加ComboBox显示的内容
if (button1.Text == “打开串口”){//如果按钮显示的是打开串口
try{//防止意外错误
serialPort1.PortName = comboBox1.Text;//获取comboBox1要打开的串口号
serialPort1.BaudRate = int.Parse(comboBox2.Text);//获取comboBox2选择的波特率
serialPort1.DataBits = int.Parse(comboBox4.Text);//设置数据位
/设置停止位/
if (comboBox3.Text == “1”) { serialPort1.StopBits = StopBits.One; }
else if (comboBox3.Text == “1.5”) { serialPort1.StopBits = StopBits.OnePointFive; }
else if (comboBox3.Text == “2”) { serialPort1.StopBits = StopBits.Two; }
/设置奇偶校验/
if (comboBox5.Text == “无”) { serialPort1.Parity = Parity.None; }
else if (comboBox5.Text == “奇校验”) { serialPort1.Parity = Parity.Odd; }
else if (comboBox5.Text == “偶校验”) { serialPort1.Parity = Parity.Even; }
serialPort1.Open();//打开串口
button1.Text = "关闭串口";//按钮显示关闭串口
}
catch (Exception err)
{
MessageBox.Show("打开失败"+ err.ToString(), "提示!");//对话框显示打开失败
}
}
else{//要关闭串口
try{//防止意外错误
serialPort1.Close();//关闭串口
}
catch (Exception){}
button1.Text = "打开串口";//按钮显示打开
}
复制代码
提示:serialPort1
3.启动测试
检测串口热插拔
1.在串口模块拔插的时候,重新更新下串口comboBox的内容
然后:
如果热插拔的是用户刚打开的串口,则按钮显示 “打开串口”,comboBox显示初始化的串口号
如果热插拔的不是用户刚打开的串口,则comboBox还是显示咱刚才的串口号
2.用一个变量记录用户打开的串口号
String serialPortName;
serialPortName = comboBox1.Text;
2.添加系统函数 protected override void WndProc(ref Message m)
复制代码
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x0219){//设备改变
if (m.WParam.ToInt32() == 0x8004){//usb串口拔出
string[] ports = System.IO.Ports.SerialPort.GetPortNames();//重新获取串口
comboBox1.Items.Clear();//清除comboBox里面的数据
comboBox1.Items.AddRange(ports);//给comboBox1添加数据
if (button1.Text == “关闭串口”){//用户打开过串口
if (!serialPort1.IsOpen){//用户打开的串口被关闭:说明热插拔是用户打开的串口
button1.Text = “打开串口”;
serialPort1.Dispose();//释放掉原先的串口资源
comboBox1.SelectedIndex = comboBox1.Items.Count > 0 ? 0 : -1;//显示获取的第一个串口号
}
else{
comboBox1.Text = serialPortName;//显示用户打开的那个串口号
}
}
else{//用户没有打开过串口
comboBox1.SelectedIndex = comboBox1.Items.Count > 0 ? 0 : -1;//显示获取的第一个串口号
}
}
else if (m.WParam.ToInt32() == 0x8000){//usb串口连接上
string[] ports = System.IO.Ports.SerialPort.GetPortNames();//重新获取串口
comboBox1.Items.Clear();
comboBox1.Items.AddRange(ports);
if (button1.Text == “关闭串口”){//用户打开过一个串口
comboBox1.Text = serialPortName;//显示用户打开的那个串口号
}
else{
comboBox1.SelectedIndex = comboBox1.Items.Count > 0 ? 0 : -1;//显示获取的第一个串口号
}
}
}
base.WndProc(ref m);
}
复制代码
提示:C#常用事件对应值
View Code
3.启动测试 :用户自己测试热插拔
串口接收数据
1.拖拽上来一个TextBox
2.点击TextBox上面的箭头,选择MultiLine (允许显示多行)
2.拖拽TextBox使其达到自己满意的长宽,
设置TextBox的Scrollbars属性为Vertical (显示竖直滚动条,可以滚动显示接收数据)
3.为了更好看些,设置TextBox的背景色为黑色 (Black)
显示的字体颜色为草绿色 (LawnGreen)
4.选择serialPort1 -> 选择事件 -> 双击DataReceived
生成串口接收回调函数
5.在回调函数中接收数据,然后把接收数据显示在TextBox
复制代码
int len = serialPort1.BytesToRead;//获取可以读取的字节数
byte[] buff = new byte[len];//创建缓存数据数组
serialPort1.Read(buff, 0, len);//把数据读取到buff数组
string str = Encoding.Default.GetString(buff);//Byte值根据ASCII码表转为 String
Invoke((new Action(() => //C# 3.0以后代替委托的新方法
{
textBox1.AppendText(str);//对话框追加显示数据
})));
复制代码
6.测试接收数据
为了测试接收数据,可以安装虚拟串口软件
https://qqqqqbucket.oss-cn-beijing.aliyuncs.com/LearnC%23/VSPD6.9.zip
安装好以后
提示:
按照上面的操作以后
电脑上便会有了COM1和COM2
COM1发送的数据将会自动发送给COM2
COM2发送的数据将会自动发送给COM1
7.测试
8.增加选择16进制显示(CheckBox),增加数据清除按钮
9.关于显示字符串和显示16进制说明
在上面的测试接收中COM1那个串口调试助手发送的
数据为字符串 123456
实际传输是按照ASCii码表传输的对应的16进制
实际上buff数组
buff[0] = 49 = 0x31
执行 string str = Encoding.Default.GetString(buff); 以后
str = 0 (字符串0)
所以接收显示为:123456
当COM1选择16进制传输的时候
假设传输的是0x01 0x02 0x03 0x04 0x05 0x06
COM1实际发送也是:0x01 0x02 0x03 0x04 0x05 0x06
不过ASCII码表中显示的字符是乱码
所以咱们需要另外添加转换程序
程序如下:
复制代码
/// <字节数组转16进制字符串>
///
/// String 16进制显示形式
public static string byteToHexStr(byte[] bytes)
{
string returnStr = “”;
try
{
if (bytes != null)
{
for (int i = 0; i < bytes.Length; i++)
{
returnStr += bytes[i].ToString(“X2”);
returnStr += " ";//两个16进制用空格隔开,方便看数据
}
}
return returnStr;
}
catch (Exception)
{
return returnStr;
}
}
复制代码
实际上就一句话:
bytes[i].ToString(“X2”);
如果变量是 0x01
则经过函数以后会返回 “01” ,字符串形式
10.如果用户选择16进制,则接收的数据按照16进制显示
复制代码
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
int len = serialPort1.BytesToRead;//获取可以读取的字节数
byte[] buff = new byte[len];//创建缓存数据数组
serialPort1.Read(buff, 0, len);//把数据读取到buff数组
Invoke((new Action(() =>{//C# 3.0以后代替委托的新方法
if (checkBox1.Checked){//16进制显示
textBox1.AppendText(byteToHexStr(buff));
}
else{
textBox1.AppendText(Encoding.Default.GetString(buff));//对话框追加显示数据
}
})));
}
/// <字节数组转16进制字符串>
///
/// String 16进制显示形式
public static string byteToHexStr(byte[] bytes)
{
string returnStr = "";
try{
if (bytes != null){
for (int i = 0; i < bytes.Length; i++){
returnStr += bytes[i].ToString("X2");
returnStr += " ";//两个16进制用空格隔开,方便看数据
}
}
return returnStr;
}
catch (Exception){
return returnStr;
}
}
复制代码
11.测试
12.双击清除接收按钮,生成按钮点击事件
写上 textBox1.Clear(); //清除接收对话框显示的数据
串口发送数据
1.页面布局如下:
2.双击点击发送按钮,生成按钮点击事件
3.事件函数中,程序如下
复制代码
String Str = textBox2.Text.ToString();//获取发送文本框里面的数据
try
{
if (Str.Length > 0)
{
serialPort1.Write(Str);//串口发送数据
}
}
catch (Exception){ }
复制代码
4.测试发送数据
5.发送16进制数据说明
首先需要明确
假设文本框里面填写的是:01 02 03 04 05 06
Str获取的是01 02 03 04 05 06 是字符串
咱所希望的是把01转为0x01
02转为0x02
.
.
.
然后发送
首先先说思路
默认把文本框每隔两个字符作为一个16进制数
假设文本框中是
010203040506 (偶数个数据)
则01 ,02 ,03 ,04 ,05 ,06 分别作为16进制
如果文本框中的字符个数是奇数个
1115268
则默认先把前面每隔两个作为一个16进制数
最后一个数前面补零
0x11,0x15,0x26,0x08
封装的解析转换程序如下:
复制代码
/// <字符串转16进制格式,不够自动前面补零>
///
///
///
///
private static byte[] strToToHexByte(String hexString)
{
int i;
hexString = hexString.Replace(" ", “”);//清除空格
if ((hexString.Length % 2) != 0)//奇数个
{
byte[] returnBytes = new byte[(hexString.Length + 1) / 2];
try
{
for (i = 0; i < (hexString.Length - 1) / 2; i++)
{
returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
}
returnBytes[returnBytes.Length - 1] = Convert.ToByte(hexString.Substring(hexString.Length - 1, 1).PadLeft(2, ‘0’), 16);
}
catch
{
MessageBox.Show(“含有非16进制字符”, “提示”);
return null;
}
return returnBytes;
}
else
{
byte[] returnBytes = new byte[(hexString.Length) / 2];
try
{
for (i = 0; i < returnBytes.Length; i++)
{
returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
}
}
catch
{
MessageBox.Show(“含有非16进制字符”, “提示”);
return null;
}
return returnBytes;
}
}
复制代码
6.完善发送16进制程序
复制代码
private void button3_Click(object sender, EventArgs e)
{
String Str = textBox2.Text.ToString();//获取发送文本框里面的数据
try
{
if (Str.Length > 0)
{
if (checkBox2.Checked)//16进制发送
{
byte[] byt = strToToHexByte(Str);
serialPort1.Write(byt, 0, byt.Length);
}
else
{
serialPort1.Write(Str);//串口发送数据
}
}
}
catch (Exception){ }
}
/// <字符串转16进制格式,不够自动前面补零>
///
///
///
///
private static byte[] strToToHexByte(String hexString)
{
int i;
hexString = hexString.Replace(" ", "");//清除空格
if ((hexString.Length % 2) != 0)//奇数个
{
byte[] returnBytes = new byte[(hexString.Length + 1) / 2];
try
{
for (i = 0; i < (hexString.Length - 1) / 2; i++)
{
returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
}
returnBytes[returnBytes.Length - 1] = Convert.ToByte(hexString.Substring(hexString.Length - 1, 1).PadLeft(2, '0'), 16);
}
catch
{
MessageBox.Show("含有非16进制字符", "提示");
return null;
}
return returnBytes;
}
else
{
byte[] returnBytes = new byte[(hexString.Length) / 2];
try
{
for (i = 0; i < returnBytes.Length; i++)
{
returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
}
}
catch
{
MessageBox.Show("含有非16进制字符", "提示");
return null;
}
return returnBytes;
}
}
复制代码
7.测试
8.双击 清除发送 按钮,生成按钮点击事件
textBox2.Clear();//清除发送文本框里面的内容
提醒
提醒
提醒
C#的串口中断回调函数也是不定个数的进入
假设另一个串口发送的数据为 123456
int len = serialPort1.BytesToRead;//获取可以读取的字节数
byte[] buff = new byte[len];//创建缓存数据数组
serialPort1.Read(buff, 0, len);//把数据读取到buff数组
len 可能等于 2
则存入buff 的数据为
buff [0] = ‘1’
buff [1] = ‘2’
然后接着又进来这个函数
len 可能等于 4
则存入buff 的数据为
buff [0] = ‘3’
buff [1] = ‘4’
buff [2] = ‘5’
buff [3] = ‘6’