本人做的一個(gè)C#串口上位機程序,最近有空就寫(xiě)了點(diǎn)感悟,見(jiàn)笑大方了。
UM~%259%60@LD(AR%25_M.jpg)
一,軟件概述
本上位機采用Visual C# 2010編寫(xiě),用于與單片機通信,發(fā)送并接收固定格式的數據包。
上位機每次點(diǎn)擊“發(fā)送”按鈕后將發(fā)送18字節給下位機,發(fā)送包的格式為:
包頭:0xAA;命令號:0x01;幀長(cháng):0x0D;幀數據13字節,由界面上的選項決定(如發(fā)射頻率6MHz,代表0x000600三字節),和校驗1字節,包尾:0xa5;共18(3+13+2)字節。此18字節將在發(fā)送區中顯示。具體的包格式就不說(shuō)了,都是自己定義的。
下位機接收到數據后,將發(fā)送5字節的數據,上位機接收到數據后可以判定數據是否正確,校驗是否正確等。下位機發(fā)送的數據由上位機接收后將在接收區中顯示
二,具體實(shí)現核心代碼
1)。運行軟件后,將自動(dòng)顯示本機的串口(如果有的話(huà)),并打開(kāi)。FormLoad代碼為:
//列出本機所有串口
string[] ports = SerialPort.GetPortNames();
Array.Sort(ports);
comboBoxPortSelect.Items.AddRange(ports);
//默認選中第一個(gè)
comboBoxPortSelect.SelectedIndex = 0;
//打開(kāi)本計算機的串口
if (mycomm.IsOpen) {
mycomm.Close();
}
mycomm.PortName = comboBoxPortSelect.Text;
mycomm.ReadTimeout = 32;
try {
mycomm.Open();
btnOpen.Text = "關(guān)閉串口";//按鈕打開(kāi)
lblToolStripStatus.Text = "串口打開(kāi)成功!";//狀態(tài)欄
}catch{
btnOpen.Text = "打開(kāi)串口";
MessageBox.Show("沒(méi)有發(fā)現串口或串口已被占用!");
lblToolStripStatus.Text = "串口打開(kāi)失??!";
}
//添加事件注冊
mycomm.DataReceived += comm_DataReceived;
2)。點(diǎn)擊發(fā)送按鈕,發(fā)送18字節數據,代碼為:
//存放待發(fā)送的一包數據(包括幀頭,命令號,幀長(cháng),幀數據,校驗,幀尾)
byte[] package = new byte[18]; int j = 0; int k = 3;
package[0] = 0xAA;//幀頭
package[1] = 0x01;//命令號
package[2] = 0x0D;//幀長(cháng)
byte parity = 0x01+0x0d;//和校驗:(命令號+幀長(cháng)+幀數據)&7f。
//存放幀數據(13字節)
byte[] realData = new byte[13];
string data = getMode().Append(getTxFre(txtTxFre.Text).Append(getDuoPuLe(txtDuoFre.Text)).Append( getBand()).Append(getTxPower()).Append(getRxFre(txtRxFre.Text)).Append(getRxBandAndDelay()).Append(getAGC())).ToString();//待發(fā)送的二進(jìn)制數據,略。。。
for (int i = 0; i < data.Length; i += 8)
{
//每八位截取,轉為十進(jìn)制的byte,再放入byte數組中
realData[j++] = (byte)Convert.ToInt32(data.Substring(i, 8), 2);
}
foreach (byte b in realData) {
parity += b;//校驗碼計算
package[k++] = b; }
parity &= 0x7F; //校驗碼計算完成
package[16] = parity;//和校驗(1字節)
package[17] = 0xa5;//幀尾
if (!mycomm.IsOpen)
{
MessageBox.Show("串口沒(méi)有打開(kāi),請打開(kāi)串口!");
return;
}
mycomm.Write(package, 0, 18);//向串口發(fā)送一包(18字節)的數據
3)。 接收數據的事件代碼:
private void comm_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
int n = mycomm.BytesToRead;
byte[] buf = new byte[n];//聲明一個(gè)臨時(shí)數組存儲當前來(lái)的串口數據
mycomm.Read(buf, 0, n);//讀取緩沖數據
builder.Clear();//清除字符串構造器的內容
//因為要訪(fǎng)問(wèn)ui資源,所以需要使用invoke方式同步ui。
this.Invoke((EventHandler)(delegate
{ //依次的拼接出16進(jìn)制字符串
foreach (byte b in buf)
{
builder.Append(b.ToString("X2") + " ");
}
//直接按ASCII規則轉換成字符串
//builder.Append(Encoding.ASCII.GetString(buf));
txtGet.Text = "";
txtGet.AppendText(builder.ToString());
}));
//判斷返回正確與否
if (buf[3] == 0x00){
lblToolStripRxStatus.Text = "返回值正確!";
}
else if(buf[3] == 0x01){
lblToolStripRxStatus.Text = "返回值錯誤!";
}
//判斷校驗是否正確
byte rxParity;//接收校驗和:(命令號+幀長(cháng)+幀數據)&7f。
rxParity = 0x01+0x01;
rxParity += buf[3];
rxParity &= 0x7F;
if (rxParity != buf[4]) {
lblToolStripRxStatus.Text += "校驗錯誤!";
}
}
三,總結
總體上說(shuō),C#串口通信比較簡(jiǎn)單,發(fā)送數據用:
byte[] package = new byte[18]{0};
mycomm.Write(package, 0, 18);
接收數據在串口的
private void comm_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
int n = mycomm.BytesToRead;
byte[] buf = new byte[n];
comm.Read(buf, 0, n);
}事件中。主要是Read/Write兩個(gè)方法。
需要注意的地方是每次接收到的數據未必是一個(gè)完整的包(尤其是包較長(cháng)時(shí)),例如剛開(kāi)始本人本機調試(本地收發(fā))時(shí)收到的18字節經(jīng)常是先收到9字節,再收到9字節,后者先2,再8,再8字節。導致接手區的數據不對(沒(méi)有18字節,只有最開(kāi)始的9或2字節),此時(shí)可以用一個(gè)緩沖數組保存接收到的數據,直到接收的長(cháng)度正確或到包尾了再進(jìn)行處理。
以前好像忘記上傳了,最近重寫(xiě)個(gè)。
聯(lián)系客服