界面部分使用krytonToolKit开发,整个软件实现了串口通信,并且控制四个自由度的舵机运动。
整个软件使用多线程开发,采集舵机指令保存到TXT文档中,然后调用TXT文档再现舵机运动过程,软件界面如下:
优化之后的界面 如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Threading;
using System.IO;
//DotNetMagic
using Crownwood.DotNetMagic;
using Crownwood.DotNetMagic.Common;
using Crownwood.DotNetMagic.Controls;
using Crownwood.DotNetMagic.Docking;
using Crownwood.DotNetMagic.Forms;
using System.Runtime.Interop
Services;
using System.Collections;
namespace ComAssistant
{
public partial
class ComAssistantForm : DotNetMagicForm
{
private int received_count = 0;//接收计数
private long send_count = 0;//发送计数
private bool Listening = false;//是否没有执行完invoke相关操作
//private bool Closing = false;//是否正在关闭串口,执行Application.DoEvents,并阻止再次invoke
private bool Closing = false;//是否正在关闭串口,执行Application.DoEvents,并阻止再次invoke
private bool Query = false;//查询状态
private List<byte> buffer = new List<byte>(4096);//默认分配1页
内存,并始终
限制不允许超过
private byte[] binary_data_1 = new byte[10];//FF FF 01 06 00 B1 0A 00 00 3D
List<Array> dataList = new List<Array>(); //数据再现
string FILE_INFO_NAME = "FileInfo.txt";
private bool kaiguan = true;
private bool openClose = true;
string strShow = "";
public ComAssistantForm()
{
InitializeComponent();
}
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//返回指令格式:FF FF 01 06 00 B1 0A 00 00 3D , 其中B1 0A为ID为1的舵机当前位置,00 00为其当前速度
// 第2位是舵机,5、6位是位置,7、8位为速度
if (Closing) return;//如果正在关闭,忽略操作,直接返回,尽快的完成串口
监听线程的一次
循环
try
{
Listening = true;//设置标记,说明我已经开始处理数据,一会儿要使用系统UI的。
int n = serialPort1.BytesToRead;//先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致
byte[] buf = new byte[n];//声明一个临时数组存储当前来的串口数据
serialPort1.Read(buf, 0, n);//读取缓冲数据
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
//<
协议解析>
//1.缓存数据
buffer.AddRange(buf);
//2.完整性判断
while (buffer.Count >= 10)//至少要包含头(2字节)+长度(7字节)+校验(1字节)
{
if (buffer[0] == 0xFF && buffer[1] == 0xFF && buffer[2] == 0xFF)
{
buffer.RemoveRange(0, 10);//从缓存中移除数据。
continue;
}
//2.1 查找数据头
if (buffer[0] == 0xFF && buffer[1] == 0xFF&& buffer[2] != 0xFF)
{
received_count++;
//至此,已经被找到了一条完整数据。我们将数据直接分析,或是缓存起来一起分析
buffer.CopyTo(0, binary_data_1, 0, 9);//复制一条完整数据到具体的数据缓存
//3.分析数据
//根据舵机编号分类并存入TXT文件
string zhuanbian = binary_data_1[7].ToString("X2");
if (zhuanbian == "00")
{
zhuanbian = "01";
}
//我们的数据都是定好格式的,所以当我们找到分析出的数据1,就知道固定位置一定是这些数据,我们只要显示就可以了
string data = binary_data_1[2].ToString("X2") + " " +
binary_data_1[5].ToString("X2") + " " + binary_data_1[6].ToString("X2") + " " +
zhuanbian + " " + binary_data_1[8].ToString("X2");
//更新界面
this.Invoke((EventHandler)(delegate
{
kryptonRichTextBox1.Text += data + "**";
WriteFile(data);
}));
if (received_count == 4)
{
WriteFileLine();
received_count = 0;
//更新界面
this.Invoke((EventHandler)(delegate
{
kryptonRichTextBox1.Text += "\r\n";
}));
}
buffer.RemoveRange(0, 10);//正确分析一条数据,从缓存中移除数据。
}
else
{
//这里是很重要的,如果数据开始不是头,则删除数据
buffer.RemoveAt(0);
}
}
}
catch (Exception)
{
MessageBox.Show("串口接收数据出错,请查看串口接收指令!", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Error);
}
finally
{
Listening = false;//我用完了,ui可以关闭串口了。
}
}
//初始化
private void ComAssistantForm_Load(object sender, EventArgs e)
{
int ii = 0;
foreach (string s in SerialPort.GetPortNames())
{
if (ii >= 1)
{
ktComboBoxCom.Items.Add(s);
}
ii++;
}
if (SerialPort.GetPortNames().Length != 0)
{
ktComboBoxCom.Text = (string)ktComboBoxCom.Items[0];
}
//string[] strCom = new string[] { "COM1", "COM2", "COM3", "COM4" };
//ktComboBoxCom.DataSource = strCom;
string[] ss = new string[] {"2000000" , "9600", "19200", "57600" };
ktComboBoxBount.DataSource = ss;
ktComboBoxJiaoyan.DataSource = Enum.GetNames(typeof(Parity));
ss = new string[] { "5", "6", "7", "8" };
ktComboBoxData.DataSource = ss;
ktComboBoxData.Text = "8";
ktComboBoxStop.DataSource = Enum.GetNames(typeof(StopBits));
ktComboBoxStop.Text = Enum.Format(typeof(StopBits), StopBits.One, "G");
kryptonTextBox1.Text = "FF FF 04 04 03 18 00 DC";
try
{
if (!serialPort1.IsOpen)
{
serialPort1.PortName = ktComboBoxCom.Text;
serialPort1.BaudRate = Convert.ToInt32(ktComboBoxBount.Text);
serialPort1.Parity = (Parity)Enum.Parse(typeof(Parity), ktComboBoxJiaoyan.Text);
serialPort1.DataBits = Int32.Parse(ktComboBoxData.Text);
serialPort1.StopBits = (StopBits)Enum.Parse(typeof(StopBits), ktComboBoxStop.Text);
serialPort1.Encoding = Encoding.GetEncoding("Gb2312");
serialPort1.Open();
pictureBox1.Image = Image.FromFile(Application.StartupPath + "\\kai.jpg");
}
}
catch (Exception)
{
MessageBox.Show("初始化出问题,请检查串口
设备", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Error);
}
}
//连接串口
private void ktBtnLink_Click(object sender, EventArgs e)
{
try
{
if (!serialPort1.IsOpen)
{
serialPort1.PortName = ktComboBoxCom.Text;
serialPort1.BaudRate = Convert.ToInt32(ktComboBoxBount.Text);
serialPort1.Parity = (Parity)Enum.Parse(typeof(Parity), ktComboBoxJiaoyan.Text);
serialPort1.DataBits = Int32.Parse(ktComboBoxData.Text);
serialPort1.StopBits = (StopBits)Enum.Parse(typeof(StopBits), ktComboBoxStop.Text);
serialPort1.Encoding = Encoding.GetEncoding("Gb2312");
serialPort1.Open();
pictureBox1.Image = Image.FromFile(Application.StartupPath + "\\kai.jpg");
}
}
catch (Exception)
{
MessageBox.Show("初始化出问题,请检查串口设备", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Error);
}
}
//关闭串口
private void ktBtnClose_Click(object sender, EventArgs e)
{
try
{
if (serialPort1.IsOpen)
{
Closing = true;
while (Listening) Application.DoEvents();
serialPort1.Close();
pictureBox1.Image = Image.FromFile(Application.StartupPath + "\\guan.jpg");
//打开时点击,则关闭串口
Closing = false;
}
}
catch (System.Exception ex)
{
MessageBox.Show("串口关闭出错,请检查串口连接!", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
}
}
//发送数据
private void ktBtnSend_Click(object sender, EventArgs e)
{
if (!serialPort1.IsOpen)
{
MessageBox.Show("请打开串口!", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
}
else
{
string content = kryptonTextBox1.Text;
byte[] bytes = MyClass.StringToHex(content);
serialPort1.Write(bytes, 0, bytes.Length);
}
}
//接收数据
private void ktBtnAccept_Click(object sender, EventArgs e)
{
//Closing = false;
int n = serialPort1.BytesToRead;
byte[] dbs = new byte[n];
int len = serialPort1.Read(dbs, 0, n);
int[] num = new int[len];
string show = "";
show = MyClass.byteToHexStr(dbs);
kryptonRichTextBox1.Text += show;
}
//清空数据
private void ktBtnClear_Click(object sender, EventArgs e)
{
strShow = "";
kryptonRichTextBox1.Text = "";
}
//急停指令
private void ktBtnStop_Click(object sender, EventArgs e)
{
try
{
if (serialPort1.IsOpen)
{
Closing = true;
while (Listening) Application.DoEvents();
serialPort1.Close();
pictureBox1.Image = Image.FromFile(Application.StartupPath + "\\guan.jpg");
//打开时点击,则关闭串口
Closing = false;
}
}
catch (System.Exception ex)
{
MessageBox.Show("串口紧急关闭!", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
}
}
//查询舵机状态
private void ktBtnQuery_Click(object sender, EventArgs e)
{
try
{
FileStream stream2 = File.Open(FILE_INFO_NAME, FileMode.OpenOrCreate, FileAccess.Write);
stream2.Seek(0, SeekOrigin.Begin);
stream2.SetLength(0); //清空txt文件
stream2.Close();
}
catch (System.Exception ex)
{
}
finally
{
MessageBox.Show("缓存数据已经清空!", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
}
//if (!Query)
//{
// ktBtnQuery.Text = "停止查询舵机";
// Query = true;
// if (!serialPort1.IsOpen)
// {
// MessageBox.Show("请打开串口!", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
// }
// else
// {
// for (int i = 0; i < 10; i++)
// {
// //这是舵机返回指令,发送指令没有加入
// //舵机1
// byte[] bytes = MyClass.StringToHex("FF FF 01 06 00 B1 0A 01 01 3D");
// serialPort1.Write(bytes, 0, bytes.Length);
// //舵机2
// bytes = MyClass.StringToHex("FF FF 02 06 00 B1 0A 02 02 3D");
// serialPort1.Write(bytes, 0, bytes.Length);
// //舵机3
// bytes = MyClass.StringToHex("FF FF 03 06 00 B1 03 03 03 3D");
// serialPort1.Write(bytes, 0, bytes.Length);
// //舵机4
// bytes = MyClass.StringToHex("FF FF 04 06 00 B1 04 04 04 3D");
// serialPort1.Write(bytes, 0, bytes.Length);
// //Thread.Sleep(5000);
// }
// }
//}
//else
//{
// ktBtnQuery.Text = "查询舵机状态";
// Query = false;
//}
}
/// <summary>
/// 写入舵机位置速度信息
/// </summary>
private void WriteFile(string input)
{
using (StreamWriter sw = File.AppendText("FileInfo.txt"))
{
sw.Write(input + " ** ");
}
///文件目录
//FileStream fs = new FileStream("FileInfo.txt", FileMode.Create);
//StreamWriter sw = new StreamWriter(fs);
//sw.WriteLine(input + "\n");
////清空缓冲区
//sw.Flush();
////关闭流
//sw.Close();
//fs.Close();
///写入内容并换行
}
/// <summary>
/// 换行
/// </summary>
private void WriteFileLine()
{
using (StreamWriter sw = File.AppendText(FILE_INFO_NAME))
{
sw.WriteLine();
}
}
//采集指令
private void ktBtnCollection_Click(object sender, EventArgs e)
{
if (kaiguan)
{
Closing = false;
//这里创建一个线程,使之执行Alpha类的
Beta()方法
Thread newThread = new Thread(new ThreadStart(caijiThread));
newThread.Start();
ktBtnCollection.Text = "停 止";
kaiguan = false;
}
else
{
kaiguan = true;
ktBtnCollection.Text = "采 集";
}
}
//创建采集新线程
public void caijiThread()
{
if (!serialPort1.IsOpen)
{
MessageBox.Show("请打开串口!", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
}
else
{
try
{
while (true)
{
if (kaiguan == true)
{
break;
}
//采集[指令固定]
byte[] bytes = MyClass.StringToHex("FF FF FE 14 83 18 01 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00 2D");
serialPort1.Write(bytes, 0, bytes.Length);
//舵机01的指令FF FF 01 04 02 24 04 D0
byte checksum = 0;
for (int QueryNum = 1; QueryNum < 5; QueryNum++)
{
//求校验和,方法一
checksum = ((byte)(QueryNum + 0X04 + 0X02 + 0X24 + 0X04));
byte[] ab = new byte[1];
ab[0] = (byte)~(0xff & checksum);
string mySum = MyClass.byteToHexStr(ab);
//////求校验和,方法二
//byte[] tempab = new byte[] { 0X01, 0X04, 0X02, 0X24, 0X04 };
//byte strab = MyClass.CheckSum(tempab);
byte[] bytesCaiji = MyClass.StringToHex("FF FF " + QueryNum.ToString("X2") + " 04 02 24 04 " + mySum.Trim());
serialPort1.Write(bytesCaiji, 0, bytesCaiji.Length);
Thread.Sleep(1 / 50);
}
Thread.Sleep(50);
}
}
catch (System.Exception ex)
{
MessageBox.Show("采集过程中出错!", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
}
}
}
//再现过程
private void ktBtnAgain_Click(object sender, EventArgs e)
{
if (openClose)
{
//这里创建一个线程,使之执行Alpha类的Beta()方法
Thread newThread = new Thread(new ThreadStart(zaixianThread));
newThread.Start();
ktBtnAgain.Text = "执行中 • • •";
openClose = false;
}
else
{
openClose = true;
ktBtnAgain.Text = "再 现";
}
}
//创建再现新线程
public void zaixianThread()
{
if (!serialPort1.IsOpen)
{
MessageBox.Show("请打开串口!", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
}
else
{
try
{
//获取数据
dataList = ReadFile();
//FF FF FE 18 83 1E 04 01 00 00 11 11 02 22 22 33 33 03 44 44 55 55 04 66 66 77 99 18
foreach (Array ar in dataList) //获取一条四个舵机数据
{
string zuhe = "";
byte[] xiafa = new byte[28];
foreach (string str in ar) //获取一个舵机数据
{
zuhe += str.Trim() + " ";
}
string hh = zuhe.Trim();
byte[] temp = MyClass.StringToHex(hh);
byte[] tempOne = new byte[26];
for (int i = 0; i < 20; i++)
{
xiafa[i + 7] = temp[i];
}
xiafa[0] = 0XFF;
xiafa[1] = 0XFF;
xiafa[2] = 0XFE;
xiafa[3] = 0X18;
xiafa[4] = 0X83;
xiafa[5] = 0X1E;
xiafa[6] = 0X04;
Array.Copy(xiafa, 2, tempOne, 0, 26);//复制数组
xiafa[27] = MyClass.CheckSum(tempOne);
serialPort1.Write(xiafa, 0, xiafa.Length);
Thread.Sleep(65);
}
if (openClose == false)
{
openClose = true;
ktBtnAgain.Text = "再 现";
}
}
catch (System.Exception ex)
{
MessageBox.Show("串口紧急关闭!", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
}
}
}
//读取文件
private List<Array> ReadFile()
{
List<Array> myArray = new List<Array>();
if (File.Exists(FILE_INFO_NAME))
{
FileStream myFile = new FileStream(FILE_INFO_NAME, FileMode.Open, FileAccess.Read);
using (StreamReader sr = new StreamReader(FILE_INFO_NAME, UnicodeEncoding.GetEncoding("GB2312")))
{
string Line = null;
string[] duoji = new string[4]; //存储一条数据
for (Line = sr.ReadLine(); Line != null; Line = sr.ReadLine())
{
if (!(Line.Trim() == ""))
{
duoji = Line.Trim().Split(new string[] { "**" }, StringSplitOptions.RemoveEmptyEntries);
myArray.Add(duoji);
}
}
}
}
return myArray;
#region
//备份读取文件的执行过程
//using (StreamReader sr = new StreamReader(FILE_INFO_NAME, UnicodeEncoding.GetEncoding("GB2312")))
//{
// string Line = null;
// string[] duoji = new string[4]; //存储一条数据
// for (Line = sr.ReadLine(); Line != null; Line = sr.ReadLine())
// {
// if (!(Line.Trim() == ""))
// {
// duoji = Line.Trim().Split(new string[] { "**" }, StringSplitOptions.RemoveEmptyEntries);
// myArray.Add(duoji);
// }
// }
//}
//使用每次读取固定长度的字节,遇到 问题是加车符号的处理
//int FileSize = 172;
//if (FileSize < myFile.Length)
//{
// byte[] buffer = new byte[FileSize];
// int copy = 0;
// string[] duoji = new string[4]; //存储一条数据
// string temp1;
// ArrayList myAL = new ArrayList();
// while (copy <= myFile.Length)
// {
// myFile.Read(buffer, 0, FileSize);
// myFile.Flush();
// copy += FileSize;
// myFile.Position = copy;
// temp1 = System.Text.Encoding.
Default.GetString(buffer);
// //myAL = MyClass.Str16ToArrayList(temp1);
// //duoji = MyClass.byteToHexStr(buffer).Trim().Split(new string[] { "**" }, StringSplitOptions.RemoveEmptyEntries);
// string jiequ = temp1.Replace("\r\n", "");
// duoji = jiequ.Trim().Split(new string[] { "**" }, StringSplitOptions.RemoveEmptyEntries);
// myArray.Add(duoji);
// }
//}
#endregion
}
//复位选择
private void ktBtnFuwei_Click(object sender, EventArgs e)
{
if (!serialPort1.IsOpen)
{
MessageBox.Show("请打开串口!", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
}
else
{
Closing = true;
byte[] bytes = MyClass.StringToHex("FF FF FE 18 83 1E 04 01 00 08 0A 00 02 00 08 0A 00 03 00 08 0A 00 04 00 08 0A 00 F2");
serialPort1.Write(bytes, 0, bytes.Length);
}
}
}
}
********************************************************************************************************************************************************
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Collections;
namespace System
{
class MyClass
{
public MyClass()
{
}
/// <summary>
/// 字符串转16进制字节数组
/// </summary>
/// <param name="hexString"></param>
/// <returns></returns>
public static byte[] StringToHex(string str)
{
string[] strs = str.Split(' ');
byte[] bytes = new byte[strs.Length];
int index = 0;
foreach (string s in strs)
{
int hex = Convert.ToInt32(s, 16);
bytes[index++] = (byte)hex;
}
return bytes;
}
/// <summary>
/// 字节数组转16进制字符串
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static string byteToHexStr(byte[] bytes)
{
string returnStr = "";
if (bytes != null)
{
for (int i = 0; i < bytes.Length; i++)
{
//ToString("X2") 为C#中的字符串格式控制符
//假设有两个数10和26,正常情况十六进制显示0xA、0x1A,这样看起来不整齐,为了好看,可以指定"X2",这样显示出来就是:0x0A、0x1A。
returnStr += bytes[i].ToString("X2");
returnStr += " ";
}
}
return returnStr;
}
public static byte CheckSum(byte[] temp)
{
byte sum;
byte jia=0X00;
for (int i = 0; i < temp.Length;i++ )
{
jia += temp[i];
}
sum= (byte)~(0xff & jia);
return sum;
}
public static ArrayList Str16ToArrayList(string strIn)
{
string sParse = "";
ArrayList myAL = new ArrayList();
int i = 0;
foreach (char cc in strIn)
{
i++;
if (cc == ' ' || sParse.Length == 2)
{
myAL.Add(sParse);
if (sParse.Length == 2 && cc != ' ')
{
sParse = Convert.ToString(cc);
}
else
{
sParse = "";
}
}
else
{
sParse += Convert.ToString(cc);
if (i == strIn.Length && cc != ' ')
{
myAL.Add(sParse);
}
}
}
return myAL;
}
}
}
********************************************************************************************************************************************************
第一次软件界面:
软件由主程序 和myClass类两部分组成,其中myClass 类主要处理字符与字节之间转换及求取校验位。
主程序:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Threading;
using System.IO;
//DotNetMagic
using Crownwood.DotNetMagic;
using Crownwood.DotNetMagic.Common;
using Crownwood.DotNetMagic.Controls;
using Crownwood.DotNetMagic.Docking;
using Crownwood.DotNetMagic.Forms;
using System.Runtime.InteropServices;
namespace ComAssistant
{
public partial class ComAssistantForm : DotNetMagicForm
{
private StringBuilder builder = new StringBuilder();//避免在事件
处理方法中反复的创建,定义到外面。
private int received_count = 0;//接收计数
private long send_count = 0;//发送计数
private bool Listening = false;//是否没有执行完invoke相关操作
private bool Closing = false;//是否正在关闭串口,执行Application.DoEvents,并阻止再次invoke
private bool Query = false;//查询状态
private List<byte> buffer = new List<byte>(4096);//默认分配1页内存,并始终限制不允许超过
private byte[] binary_data_1 = new byte[10];//FF FF 01 06 00 B1 0A 00 00 3D
List<Array> dataList = new List<Array>(); //数据再现
string FILE_INFO_NAME = "FileInfo.txt";
private bool kaiguan = true;
private bool openClose = true;
string strShow = "";
public ComAssistantForm()
{
InitializeComponent();
}
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//返回指令格式:FF FF 01 06 00 B1 0A 00 00 3D , 其中B1 0A为ID为1的舵机当前位置,00 00为其当前速度
// 第2位是舵机,5、6位是位置,7、8位为速度
if (Closing) return;//如果正在关闭,忽略操作,直接返回,尽快的完成串口监听线程的一次循环
try
{
Listening = true;//设置标记,说明我已经开始处理数据,一会儿要使用系统UI的。
int n = serialPort1.BytesToRead;//先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致
byte[] buf = new byte[n];//声明一个临时数组存储当前来的串口数据
serialPort1.Read(buf, 0, n);//读取缓冲数据
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
//<协议解析>
//1.缓存数据
buffer.AddRange(buf);
//2.完整性判断
while (buffer.Count >= 10)//至少要包含头(2字节)+长度(7字节)+校验(1字节)
{
//2.1 查找数据头
if (buffer[0] == 0xFF && buffer[1] == 0xFF)
{
received_count++;
/*
//校验
byte checksum = 0;
for (int i = 0; i < len + 3; i++)//len+3表示校验之前的位置
{
checksum ^= buffer[i];
}
if (checksum != buffer[len + 3]) //如果数据校验失败,丢弃这一包数据
{
buffer.RemoveRange(0, len + 4);//从缓存中删除
错误数据
continue;//继续下一次循环
}
*/
//至此,已经被找到了一条完整数据。我们将数据直接分析,或是缓存起来一起分析
buffer.CopyTo(0, binary_data_1, 0, 9);//复制一条完整数据到具体的数据缓存
//3.分析数据
//根据舵机编号分类并存入TXT文件
string zhuanbian = binary_data_1[7].ToString("X2");
if (zhuanbian == "00")
{
zhuanbian = "01";
}
//我们的数据都是定好格式的,所以当我们找到分析出的数据1,就知道固定位置一定是这些数据,我们只要显示就可以了
string data = binary_data_1[2].ToString("X2") + " " +
binary_data_1[5].ToString("X2") + " " + binary_data_1[6].ToString("X2") + " " +
zhuanbian + " " + binary_data_1[8].ToString("X2");
//更新界面
this.Invoke((EventHandler)(delegate
{
kryptonRichTextBox1.Text += data + "**";
WriteFile(data);
}));
if (received_count == 4)
{
WriteFileLine();
received_count = 0;
//更新界面
this.Invoke((EventHandler)(delegate
{
kryptonRichTextBox1.Text += "\r\n";
}));
}
buffer.RemoveRange(0, 10);//正确分析一条数据,从缓存中移除数据。
}
else
{
//这里是很重要的,如果数据开始不是头,则删除数据
buffer.RemoveAt(0);
}
}
}
finally
{
//写入文档
Listening = false;//我用完了,ui可以关闭串口了。
}
#region
/*
//接收指令函数
int n = serialPort1.BytesToRead;
byte[] dbs = new byte[n];
int len = serialPort1.Read(dbs, 0, n);
int[] num = new int[len];
string show = "";
show = MyClass.byteToHexStr(dbs);
strShow += show + "\r\n";
this.Invoke((EventHandler)delegate { kryptonRichTextBox1.Text = strShow; });
*/
/*
//查询01舵机
//try
//{
Listening = true;//设置标记,说明我已经开始处理数据,一会儿要使用系统UI的。
int nn = serialPort1.BytesToRead;//先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致
byte[] buf = new byte[nn];//声明一个临时数组存储当前来的串口数据
received_count += nn;//增加接收计数
serialPort1.Read(buf, 0, nn);//读取缓冲数据
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
//<协议解析>
bool data_1_catched = false;//缓存记录数据是否捕获到
//1.缓存数据
buffer.AddRange(buf);
//2.完整性判断
//while (buffer.Count >= 5)//至少要包含头(2字节)+长度(1字节)+校验(1字节)
//{
//请不要担心使用>=,因为>=已经和>,<,=一样,是独立
caozuofu.html" target="_blank">操作符,并不是解析成>和=2个符号
//2.1 查找数据头
if (buf[0] == 0xFF && buf[1] == 0xFF)
{
int len = buf[2];//数据长度
if (1 == len)
{
binary_data_1[0] = buffer[5];
binary_data_1[1] = buffer[6];
binary_data_1[2] = buffer[7];
binary_data_1[3] = buffer[8];
}
//更新界面
this.Invoke((EventHandler)delegate { kryptonRichTextBox1.Text = MyClass.byteToHexStr(binary_data_1); });
}
*/
// this.Invoke((EventHandler)(delegate { txData.Text = data; }));
//strShow = ;
//strShow = "舵机1接收到的指令:" + binary_data_1[0].ToString() + " " + binary_data_1[1].ToString() + " " + binary_data_1[2].ToString() + " " + binary_data_1[3].ToString();
// buffer.CopyTo(0,binary_data_1,0,4);//复制一条完整数据到具体的数据缓存
// buffer.CopyTo(0, binary_data_1, 5, 4);//复制一条完整数据到具体的数据缓存
//data_1_catched = true;
//buffer.RemoveRange(0, len + 4);//正确分析一条数据,从缓存中移除数据。
// }
// else
// {
// //这里是很重要的,如果数据开始不是头,则删除数据
// buffer.RemoveAt(0);
// }
// }
// //分析数据
// if (data_1_catched)
// {
// //我们的数据都是定好格式的,所以当我们找到分析出的数据1,就知道固定位置一定是这些数据,我们只要显示就可以了
// string data = binary_data_1[5].ToString("X2") + " " + binary_data_1[6].ToString("X2") + " " +
// binary_data_1[7].ToString("X2") + " " + binary_data_1[8].ToString("X2");
// //更新界面
// this.Invoke((EventHandler)(delegate { kryptonRichTextBox1.Text = data; }));
// }
// //如果需要别的协议,只要扩展这个data_n_catched就可以了。往往我们协议多的情况下,还会包含数据编号,给来的数据进行
// //编号,协议优化后就是: 头+编号+长度+数据+校验
// //</协议解析>
// /////////////////////////////////////////////////////////////////////////////////////////////////////////////
// //因为要访问ui资源,所以需要使用invoke方式
同步ui。
// this.Invoke((EventHandler)(delegate
// {
// //判断是否是显示为16禁止
////依次的拼接出16进制字符串
// foreach (byte b in buf)
// {
// builder.Append(b.ToString("X2") + " ");
// }
// //追加的形式添加到文本框末端,并滚动到最后。
// this.kryptonRichTextBox1.AppendText(builder.ToString());
// //修改接收计数
// }));
// }
// finally
// {
// Listening = false;//我用完了,ui可以关闭串口了。
// }
#endregion
}
//初始化
private void ComAssistantForm_Load(object sender, EventArgs e)
{
int ii = 0;
foreach (string s in SerialPort.GetPortNames())
{
if (ii >= 1)
{
ktComboBoxCom.Items.Add(s);
}
ii++;
}
if (SerialPort.GetPortNames().Length != 0)
{
ktComboBoxCom.Text = (string)ktComboBoxCom.Items[0];
}
//string[] strCom = new string[] { "COM1", "COM2", "COM3", "COM4" };
//ktComboBoxCom.DataSource = strCom;
string[] ss = new string[] {"1000000" , "9600", "19200", "57600" };
ktComboBoxBount.DataSource = ss;
ktComboBoxJiaoyan.DataSource = Enum.GetNames(typeof(Parity));
ss = new string[] { "5", "6", "7", "8" };
ktComboBoxData.DataSource = ss;
ktComboBoxData.Text = "8";
ktComboBoxStop.DataSource = Enum.GetNames(typeof(StopBits));
ktComboBoxStop.Text = Enum.Format(typeof(StopBits), StopBits.One, "G");
kryptonTextBox1.Text = "FF FF 04 04 03 18 00 DC";
//打开串口
try
{
if (!serialPort1.IsOpen)
{
serialPort1.PortName = ktComboBoxCom.Text;
serialPort1.BaudRate = Convert.ToInt32(ktComboBoxBount.Text);
serialPort1.Parity = (Parity)Enum.Parse(typeof(Parity), ktComboBoxJiaoyan.Text);
serialPort1.DataBits = Int32.Parse(ktComboBoxData.Text);
serialPort1.StopBits = (StopBits)Enum.Parse(typeof(StopBits), ktComboBoxStop.Text);
serialPort1.Encoding = Encoding.GetEncoding("Gb2312");
serialPort1.Open();
ktLabMessage.Text = "当前连接状态: 已经连接";
}
}
catch (Exception)
{
MessageBox.Show("初始化出问题,请检查串口设备", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Error);
}
}
//连接串口
private void ktBtnLink_Click(object sender, EventArgs e)
{
try
{
if (!serialPort1.IsOpen)
{
serialPort1.PortName = ktComboBoxCom.Text;
serialPort1.BaudRate = Convert.ToInt32(ktComboBoxBount.Text);
serialPort1.Parity = (Parity)Enum.Parse(typeof(Parity), ktComboBoxJiaoyan.Text);
serialPort1.DataBits = Int32.Parse(ktComboBoxData.Text);
serialPort1.StopBits = (StopBits)Enum.Parse(typeof(StopBits), ktComboBoxStop.Text);
serialPort1.Encoding = Encoding.GetEncoding("Gb2312");
serialPort1.Open();
ktLabMessage.Text = "当前连接状态: 已经连接";
}
}
catch (Exception)
{
MessageBox.Show("初始化出问题,请检查串口设备", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Error);
}
}
//关闭串口
private void ktBtnClose_Click(object sender, EventArgs e)
{
if (serialPort1.IsOpen)
{
serialPort1.Close();
ktLabMessage.Text = "当前连接状态: 已经关闭";
}
}
//发送数据
private void ktBtnSend_Click(object sender, EventArgs e)
{
if (!serialPort1.IsOpen)
{
MessageBox.Show("请打开串口!", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
}
else
{
string content = kryptonTextBox1.Text;
byte[] bytes = MyClass.StringToHex(content);
serialPort1.Write(bytes, 0, bytes.Length);
}
}
//接收数据
private void ktBtnAccept_Click(object sender, EventArgs e)
{
int n = serialPort1.BytesToRead;
byte[] dbs = new byte[n];
int len = serialPort1.Read(dbs, 0, n);
int[] num = new int[len];
string show = "";
show = MyClass.byteToHexStr(dbs);
kryptonRichTextBox1.Text += show;
}
//清空数据
private void ktBtnClear_Click(object sender, EventArgs e)
{
strShow = "";
kryptonRichTextBox1.Text = "";
}
private void timer1_Tick(object sender, EventArgs e)
{
kryptonRichTextBox1.Text = strShow;
}
//急停指令
private void ktBtnStop_Click(object sender, EventArgs e)
{
if (!serialPort1.IsOpen)
{
MessageBox.Show("请打开串口!", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
}
else
{
byte[] bytes = MyClass.StringToHex("FF FF FE 1C 83 20 02 01 01 00 02 01 00 03 01 00 04 01 00 05 01 00 06 01 00 07 01 00 08 01 00 14");
serialPort1.Write(bytes, 0, bytes.Length);
}
}
//查询舵机状态
private void ktBtnQuery_Click(object sender, EventArgs e)
{
try
{
FileStream stream2 = File.Open(FILE_INFO_NAME, FileMode.OpenOrCreate, FileAccess.Write);
stream2.Seek(0, SeekOrigin.Begin);
stream2.SetLength(0); //清空txt文件
stream2.Close();
}
catch (System.Exception ex)
{
}
finally
{
MessageBox.Show("缓存数据已经清空!", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
}
//if (!Query)
//{
// ktBtnQuery.Text = "停止查询舵机";
// Query = true;
// if (!serialPort1.IsOpen)
// {
// MessageBox.Show("请打开串口!", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
// }
// else
// {
// for (int i = 0; i < 10; i++)
// {
// //这是舵机返回指令,发送指令没有加入
// //舵机1
// byte[] bytes = MyClass.StringToHex("FF FF 01 06 00 B1 0A 01 01 3D");
// serialPort1.Write(bytes, 0, bytes.Length);
// //舵机2
// bytes = MyClass.StringToHex("FF FF 02 06 00 B1 0A 02 02 3D");
// serialPort1.Write(bytes, 0, bytes.Length);
// //舵机3
// bytes = MyClass.StringToHex("FF FF 03 06 00 B1 03 03 03 3D");
// serialPort1.Write(bytes, 0, bytes.Length);
// //舵机4
// bytes = MyClass.StringToHex("FF FF 04 06 00 B1 04 04 04 3D");
// serialPort1.Write(bytes, 0, bytes.Length);
// //Thread.Sleep(5000);
// }
// }
//}
//else
//{
// ktBtnQuery.Text = "查询舵机状态";
// Query = false;
//}
}
/// <summary>
/// 写入舵机位置速度信息
/// </summary>
private void WriteFile(string input)
{
using (StreamWriter sw = File.AppendText("FileInfo.txt"))
{
sw.Write(input + " ** ");
}
///文件目录
//FileStream fs = new FileStream("FileInfo.txt", FileMode.Create);
//StreamWriter sw = new StreamWriter(fs);
//sw.WriteLine(input + "\n");
////清空缓冲区
//sw.Flush();
////关闭流
//sw.Close();
//fs.Close();
///写入内容并换行
}
/// <summary>
/// 换行
/// </summary>
private void WriteFileLine()
{
using (StreamWriter sw = File.AppendText(FILE_INFO_NAME))
{
sw.WriteLine();
}
}
//读取文件
private List<Array> ReadFile()
{
List<Array> myArray = new List<Array>();
if (File.Exists(FILE_INFO_NAME))
{
using (StreamReader sr = new StreamReader(FILE_INFO_NAME, UnicodeEncoding.GetEncoding("GB2312")))
{
string Line = null;
string[] duoji = new string[4]; //存储一条数据
for (Line = sr.ReadLine(); Line != null; Line = sr.ReadLine())
{
if (!(Line.Trim() == ""))
{
duoji = Line.Trim().Split(new string[] { "**" }, StringSplitOptions.RemoveEmptyEntries);
myArray.Add(duoji);
}
}
}
}
return myArray;
}
//再现过程
private void ktBtnAgain_Click(object sender, EventArgs e)
{
if (openClose)
{
//这里创建一个线程,使之执行Alpha类的Beta()方法
Thread newThread = new Thread(new ThreadStart(zaixianThread));
newThread.Start();
ktBtnAgain.Text = "停 止";
openClose = false;
}
else
{
openClose = true;
ktBtnAgain.Text = "再 现";
}
}
//创建再现新线程
public void zaixianThread()
{
if (!serialPort1.IsOpen)
{
MessageBox.Show("请打开串口!", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
}
else
{
while (true)
{
if (openClose == true)
{
break;
}
//获取数据
dataList = ReadFile();
//FF FF FE 18 83 1E 04 01 00 00 11 11 02 22 22 33 33 03 44 44 55 55 04 66 66 77 99 18
foreach (Array ar in dataList) //获取一条四个舵机数据
{
string zuhe = "";
byte[] xiafa = new byte[28];
foreach (string str in ar) //获取一个舵机数据
{
zuhe += str.Trim() + " ";
}
string hh = zuhe.Trim();
byte[] temp = MyClass.StringToHex(hh);
byte[] tempOne=new byte[26];
for (int i = 0; i < 20; i++)
{
xiafa[i + 7] = temp[i];
//tempOne[i+5]=temp[i];
}
xiafa[0] = 0XFF;
xiafa[1] = 0XFF;
xiafa[2] = 0XFE;
xiafa[3] = 0X18;
xiafa[4] = 0X83;
xiafa[5] = 0X1E;
xiafa[6] = 0X04;
Array.Copy(xiafa, 2, tempOne, 0, 26);//复制数组
xiafa[27] = MyClass.CheckSum(tempOne);
serialPort1.Write(xiafa, 0, xiafa.Length);
Thread.Sleep(100);
}
}
}
}
//采集指令
private void ktBtnCollection_Click(object sender, EventArgs e)
{
if (kaiguan)
{
//这里创建一个线程,使之执行Alpha类的Beta()方法
Thread newThread = new Thread(new ThreadStart(caijiThread));
newThread.Start();
ktBtnCollection.Text = "停 止";
kaiguan = false;
}
else
{
kaiguan = true;
ktBtnCollection.Text = "采 集";
}
}
//创建采集新线程
public void caijiThread()
{
if (!serialPort1.IsOpen)
{
MessageBox.Show("请打开串口!", "软件提醒", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
}
else
{
while (true)
{
if (kaiguan == true)
{
break;
}
//采集[指令固定]
byte[] bytes = MyClass.StringToHex("FF FF FE 14 83 18 01 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00 2D");
serialPort1.Write(bytes, 0, bytes.Length);
//舵机01的指令FF FF 01 04 02 24 04 D0
byte checksum = 0;
for (int QueryNum = 1; QueryNum < 5; QueryNum++)
{
//求校验和,方法一
checksum = ((byte)(QueryNum + 0X04 + 0X02 + 0X24 + 0X04));
byte[] ab = new byte[1];
ab[0] = (byte)~(0xff & checksum);
string mySum = MyClass.byteToHexStr(ab);
//////求校验和,方法二
//byte[] tempab = new byte[] { 0X01, 0X04, 0X02, 0X24, 0X04 };
//byte strab = MyClass.CheckSum(tempab);
byte[] bytesCaiji = MyClass.StringToHex("FF FF " + QueryNum.ToString("X2") + " 04 02 24 04 " + mySum.Trim());
serialPort1.Write(bytesCaiji, 0, bytesCaiji.Length);
Thread.Sleep(6);
}
Thread.Sleep(100);
}
}
}
}
}
*******************************************************************************************************************************************************
myClass类:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Collections;
namespace System
{
class MyClass
{
public MyClass()
{
}
/// <summary>
/// 字符串转16进制字节数组
/// </summary>
/// <param name="hexString"></param>
/// <returns></returns>
public static byte[] StringToHex(string str)
{
string[] strs = str.Split(' ');
byte[] bytes = new byte[strs.Length];
int index = 0;
foreach (string s in strs)
{
int hex = Convert.ToInt32(s, 16);
bytes[index++] = (byte)hex;
}
return bytes;
}
/// <summary>
/// 字节数组转16进制字符串
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static string byteToHexStr(byte[] bytes)
{
string returnStr = "";
if (bytes != null)
{
for (int i = 0; i < bytes.Length; i++)
{
//ToString("X2") 为C#中的字符串格式控制符
//假设有两个数10和26,正常情况十六进制显示0xA、0x1A,这样看起来不整齐,为了好看,可以指定"X2",这样显示出来就是:0x0A、0x1A。
returnStr += bytes[i].ToString("X2");
returnStr += " ";
}
}
return returnStr;
}
public static byte CheckSum(byte[] temp)
{
byte sum;
byte jia=0X00;
for (int i = 0; i < temp.Length;i++ )
{
jia += temp[i];
}
sum= (byte)~(0xff & jia);
return sum;
}
public static ArrayList Str16ToArrayList(string strIn)
{
string sParse = "";
ArrayList myAL = new ArrayList();
int i = 0;
foreach (char cc in strIn)
{
i++;
if (cc == ' ' || sParse.Length == 2)
{
myAL.Add(sParse);
if (sParse.Length == 2 && cc != ' ')
{
sParse = Convert.ToString(cc);
}
else
{
sParse = "";
}
}
else
{
sParse += Convert.ToString(cc);
if (i == strIn.Length && cc != ' ')
{
myAL.Add(sParse);
}
}
}
return myAL;
}
}
}