初学C#串口编程,接收丢数据,请大家看看问题在哪里
编辑1:修改错别字数据存在1G的flash中,每个subsector是4K字节,包含292条14字节的log数据,每个log数据头部是4字节的时间码(兼做识别用)
通讯过程如下:
上位机发送 rxlog -1
单片机应答 start:0,total:32768,表示从数据从subsector【0】开始,共用32768个subsector
while(total>0)
{
上位机发送rxlog xxx(subsector);
单片机应答一个subsector的数据;
total--;
}
subsector数据包格式如下:
: Head: 0xFA 0xF5 0xAA 0x55 :n*14 bytes of Log data pack : Tail: 0xFA 0xF5 0xCC 0x33 :CRC32 of Head+Logs+Tail
log数据的时间码不会出现Head和Tail,如果接收中log数据的时间码是Tail,表示这个sebsector结束
每个subsector数据包,给1秒时间,超时没有完成,就重来
运行中出现crc err,看内存中数据是串口接收数据不完整,丢数据非常严重
程序如下:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//MessageBox.Show("Please confirm SW1 is all-off", "Notice!");
// got all com ports
btnOpen.Enabled = false;
string[] portStr = SerialPort.GetPortNames();
if (portStr.Length == 0)
{
MessageBox.Show("No COM port found!", "Error");
return;
}
// com ports exist
btnOpen.Enabled = true;
foreach (string s in System.IO.Ports.SerialPort.GetPortNames())
{
cbSerial.Items.Add(s);
}
cbSerial.SelectedIndex = 0;
}
// comport
private SerialPort rs232Port = new SerialPort();
// got from "rxlog -1"
private int startSub, totalSubs;
// thread for rx
private Thread getlogThread;
private delegate void UpdateTextBox(string s);
private UpdateTextBox updateTextBox;
// rx flag
private bool rxLogsInprocess = false;
private bool rxPause = false;
private int subInprocess;
private uint currentDate = 0xFFFFFFFF;
private subPack subpack = new subPack();
private enum subStatus { ssHead, ssLog, ssTail, ssCrc32, ssOver };
private subStatus revSt = subStatus.ssHead;
private int revCnt = 0;
private Queue<byte> rs232Buf = new Queue<byte>();
private void info2textbox(string s)
{
textBox.AppendText(s + "\r\n");
textBox.ScrollToCaret();
}
private void btnOpen_Click(object sender, EventArgs e)
{
if(rs232Port.IsOpen)
{
// if comport is receiving logs, stop it first
rxPause = true;
if (rxLogsInprocess)
{
while (rxLogsInprocess) ;
}
rs232Port.DataReceived -= byteReceived;
btnGetLogs.Enabled = false;
btnOpen.Text = "Open";
rs232Port.Close();
cbSerial.Enabled = true;
return;
}
// clear flags
rxLogsInprocess = false;
rxPause = false;
// init rs232port
rs232Port.PortName = cbSerial.Text;
rs232Port.BaudRate = 115200;
rs232Port.Parity = Parity.None;
rs232Port.StopBits = StopBits.One;
rs232Port.DataBits = 8;
rs232Port.Handshake = Handshake.None;
rs232Port.RtsEnable = false;
rs232Port.ReceivedBytesThreshold = 1;
rs232Port.ReadBufferSize = subPack.SUBPACK_SIZE;
rs232Port.NewLine = "\r\n";
rs232Port.ReadTimeout = 1000;
try
{
rs232Port.Open();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
// clear buffer
rs232Port.DiscardInBuffer();
rs232Port.DiscardOutBuffer();
btnOpen.Text = "Close";
cbSerial.Enabled = false;
textBox.Text = "Connecting...";
// send command "rxlog -1\x0D" to get the log status from module :"Start:0, Total:32767\r\n"
rs232Port.Write("rxlog -1\x0D");
try
{
string rxLog = rs232Port.ReadLine();
int s, t;
s = rxLog.IndexOf("Start:");
t = rxLog.IndexOf(", Total:");
if (s != -1 && t != -1)
{
startSub = Int32.Parse(rxLog.Substring(s+6, t-(s+6)));
totalSubs = Int32.Parse(rxLog.Substring(t+8));
textBox.Text = "Start = " + startSub + ", Total = " + totalSubs+"\r\n";
}
else
{
throw new TimeoutException();
}
}
catch (TimeoutException)
{
textBox.Text ="Connect failed!";
return;
}
// connect successful, ready to get logs
btnGetLogs.Enabled = true;
subInprocess = startSub;
}
// 接收字节
private volatile bool byteRecIsBus = false;
void byteReceived(object sender, SerialDataReceivedEventArgs e)
{
while (byteRecIsBus == true) ; // avoid reentrant
byteRecIsBus = true;
while (rs232Port.BytesToRead > 0) // data to read
{
int n = rs232Port.BytesToRead;
byte[] buf = new byte;
rs232Port.Read(buf, 0, n); // read buffer
for (int i = 0; i < n; i++)
{
rs232Buf.Enqueue(buf); // to queue
}
}
byteRecIsBus = false;
}
// 启动接收
private void btnGetLogs_Click(object sender, EventArgs e)
{
rs232Port.DataReceived += byteReceived; // delegate
btnGetLogs.Enabled = false; // disable the button
getlogThread = new Thread(new ThreadStart(getLogAll)); // new thread to rx
getlogThread.Start();
updateTextBox = new UpdateTextBox(info2textbox);
}
// 接收线程
private void getLogAll()
{
int timeoutCnt = 0;
int crcErrCnt = 0;
while (totalSubs > 0)
{
// to receive a new subsector, clear the buffer first
rs232Buf.Clear();
rs232Port.DiscardInBuffer();
rs232Port.DiscardOutBuffer();
// rxlog sub
rs232Port.Write("rxlog " + subInprocess + "\x0D");
// receive a sub
// set timer
setTimeout(1000);
// waiting for data
Thread.Sleep(300);
try
{
// 1: head : 0xFAF5AA55
byte[] headBuf = new byte { 0, 0, 0, 0 };
while ((headBuf == 0xFA && headBuf == 0xF5 && headBuf == 0xAA && headBuf == 0x55) == false)
{
headBuf = headBuf;
headBuf = headBuf;
headBuf = headBuf;
headBuf = getByte();
}
for (int i = 0; i < subPack.HEAD_SIZE; i++)
{
subpack.head = headBuf;
}
// 2&3: log and tail
// The first 4 Bytes in a Log is timeStamp
// If the timeStamp is 0xFAF5CC33 or LOG_CNT_MAX of Logs have been received, it means the Tail
for (int i = 0; i < subPack.LOG_CNT_MAX + 1; i++)
{
// check first 4-byte
int j;
for (j = 0; j < 4; j++)
{
subpack.logsInSub.log14b = getByte();
}
if ((subpack.logsInSub.getTimestamp() == 0xFAF5CC33) || (i == subPack.LOG_CNT_MAX))
{// tail
for (j = 0; j < 4; j++)
{
subpack.tail = subpack.logsInSub.log14b;
}
subpack.logCnt = i; // how many Logs in this subsector
// goto crc32
break;
}
// receive other 10 bytes
for (; j < log.LOG_SIZE; j++)
{
subpack.logsInSub.log14b = getByte();
}
}
// 4: crc32
for (int i = 0; i < subPack.CRC32_SIZE; i++)
{
subpack.crc32 = getByte();
}
}
catch (TimeoutException)
{
// timeout error, retry this subsector
// update textbox
if(++timeoutCnt==5)
{
//??????
throw new TimeoutException("Timeout 5 times");
}
this.BeginInvoke(updateTextBox, "timeout\r\n");
continue;
}
if (subpack.isCrc32Correct() == false)
{
// crc32 error, retry this subsector
if (++crcErrCnt == 5)
{
//??????
throw new TimeoutException("Crc error 5 times");
}
this.BeginInvoke(updateTextBox, "crc err\r\n");
continue;
}
// got a sub
// save to file
for (int i = 0; i < subpack.logCnt; i++)
{
// check date
uint date = subpack.logsInSub.getDate();
if (currentDate != date)
{// new date, new file
currentDate = date;
}
else
{// same date, same file
}
}
// <- a sub is over
timeoutCnt = 0;
crcErrCnt = 0;
this.BeginInvoke(updateTextBox, "Got subsector:" + subInprocess + "\r\n");
totalSubs--;
subInprocess++;
if (subInprocess >= 32768)
{
subInprocess = 0;
}
}
// over
btnGetLogs.Enabled = false;
// ?????
}
// 从队列中取出字节
byte getByte()
{
while(rs232Buf.Count==0)
{
if (timeoutMsTick)
{
throw new TimeoutException();
}
}
return rs232Buf.Dequeue();
}
private void btnBrowse_Click(object sender, EventArgs e)
{
System.Windows.Forms.FolderBrowserDialog dlg = new System.Windows.Forms.FolderBrowserDialog();
dlg.Description = "Select the directory to save log files";
if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
saveToDir.Text = dlg.SelectedPath.ToString();
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (rs232Port.IsOpen == true)
{
rs232Port.Close();
// close file
}
}
// 定时器 for timeout
private volatile bool timeoutMsTick;
private void timeoutMs_Tick(object sender, EventArgs e)
{
timeoutMsTick = true;
timeoutMs.Stop();
}
private void setTimeout(int ms)
{
timeoutMs.Stop();
timeoutMsTick = false;
timeoutMs.Interval = ms>0?ms:1;
timeoutMs.Start();
}
}
本帖最后由 liuqian 于 2016-1-5 14:44 编辑
接收部分,不用queue,改用数组,改善很明显
之前几乎每个包都有crc err,而且会有多次重发,改良以后,crc err很少出现(几十个包出现一次),没有出现连续2次crc err的
// 接收字节
private volatile bool byteRecIsBus = false;
private volatile int rs232BufferWrIndex = 0;
private volatile byte[] rs232Buffer = new byte;
void byteReceived(object sender, SerialDataReceivedEventArgs e)
{
while (byteRecIsBus == true) ; // avoid reentrant
byteRecIsBus = true;
while (rs232Port.BytesToRead > 0) // data to read
{
int n = rs232Port.BytesToRead;
byte[] buf = new byte;
rs232Port.Read(buf, 0, n); // read buffer
foreach (byte b in buf)
{
rs232Buffer = b;
//rs232Buf.Enqueue(b); // to queue
}
}
byteRecIsBus = false;
}
// 从队列中取出字节
private volatile int rs232BufferRdIndex = 0;
byte getByte()
{
while (rs232BufferWrIndex == rs232BufferRdIndex)
{
if (timeoutMsTick)
{
throw new TimeoutException();
}
}
byte b = rs232Buffer;
return b;
}
private void rs232BufferReset()
{
rs232BufferRdIndex = 0;
rs232BufferWrIndex = 0;
}
本帖最后由 墨非 于 2016-1-5 20:20 编辑
粗扫了一下 ,byteReceived 里while (byteRecIsBus == true) ;占着CPU干等 为何不干脆释放掉CPU呢
要不 用:
private static object byteReceivedLock = new object();
void byteReceived(object sender, SerialDataReceivedEventArgs e)
{
lock (byteReceivedLock)
{
//接收代码
}
}
页:
[1]