搜索
bottom↓
回复: 2

初学C#串口编程,接收丢数据,请大家看看问题在哪里

[复制链接]

出0入4汤圆

发表于 2016-1-5 12:23:44 | 显示全部楼层 |阅读模式
编辑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数据包格式如下:
[1]: Head: 0xFA 0xF5 0xAA 0x55 [2]:n*14 bytes of Log data pack [3]: Tail: 0xFA 0xF5 0xCC 0x33 [4]:CRC32 of Head+Logs+Tail
log数据的时间码不会出现Head和Tail,如果接收中log数据的时间码是Tail,表示这个sebsector结束

每个subsector数据包,给1秒时间,超时没有完成,就重来

运行中出现crc err,看内存中数据是串口接收数据不完整,丢数据非常严重

程序如下:

  1.     public partial class Form1 : Form
  2.     {
  3.         public Form1()
  4.         {
  5.             InitializeComponent();
  6.             //MessageBox.Show("Please confirm SW1 is all-off", "Notice!");

  7.             // got all com ports
  8.             btnOpen.Enabled = false;
  9.             string[] portStr = SerialPort.GetPortNames();
  10.             if (portStr.Length == 0)
  11.             {
  12.                 MessageBox.Show("No COM port found!", "Error");
  13.                 return;
  14.             }
  15.             // com ports exist
  16.             btnOpen.Enabled = true;
  17.             foreach (string s in System.IO.Ports.SerialPort.GetPortNames())
  18.             {
  19.                 cbSerial.Items.Add(s);
  20.             }
  21.             cbSerial.SelectedIndex = 0;
  22.         }


  23.         // comport
  24.         private SerialPort rs232Port = new SerialPort();
  25.         
  26.         // got from "rxlog -1"
  27.         private int startSub, totalSubs;

  28.         // thread for rx
  29.         private Thread getlogThread;
  30.         private delegate void UpdateTextBox(string s);
  31.         private UpdateTextBox updateTextBox;
  32.         // rx flag
  33.         private bool rxLogsInprocess = false;
  34.         private bool rxPause = false;
  35.         private int subInprocess;
  36.         private uint currentDate = 0xFFFFFFFF;

  37.         private subPack subpack = new subPack();
  38.         private enum subStatus { ssHead, ssLog, ssTail, ssCrc32, ssOver };
  39.         private subStatus revSt = subStatus.ssHead;
  40.         private int revCnt = 0;
  41.         private Queue<byte> rs232Buf = new Queue<byte>();

  42.         private void info2textbox(string s)
  43.         {
  44.             textBox.AppendText(s + "\r\n");
  45.             textBox.ScrollToCaret();
  46.         }

  47.         private void btnOpen_Click(object sender, EventArgs e)
  48.         {
  49.             if(rs232Port.IsOpen)
  50.             {
  51.                 // if comport is receiving logs, stop it first
  52.                 rxPause = true;
  53.                 if (rxLogsInprocess)
  54.                 {
  55.                     while (rxLogsInprocess) ;
  56.                 }
  57.                 rs232Port.DataReceived -= byteReceived;
  58.                 btnGetLogs.Enabled = false;
  59.                 btnOpen.Text = "Open";
  60.                 rs232Port.Close();
  61.                 cbSerial.Enabled = true;
  62.                 return;
  63.             }

  64.             // clear flags
  65.             rxLogsInprocess = false;
  66.             rxPause = false;

  67.             // init rs232port
  68.             rs232Port.PortName = cbSerial.Text;
  69.             rs232Port.BaudRate = 115200;
  70.             rs232Port.Parity = Parity.None;
  71.             rs232Port.StopBits = StopBits.One;
  72.             rs232Port.DataBits = 8;
  73.             rs232Port.Handshake = Handshake.None;
  74.             rs232Port.RtsEnable = false;
  75.             rs232Port.ReceivedBytesThreshold = 1;
  76.             rs232Port.ReadBufferSize = subPack.SUBPACK_SIZE;
  77.             rs232Port.NewLine = "\r\n";
  78.             rs232Port.ReadTimeout = 1000;
  79.             try
  80.             {
  81.                 rs232Port.Open();
  82.             }
  83.             catch(Exception ex)
  84.             {
  85.                 MessageBox.Show(ex.Message);
  86.                 return;
  87.             }

  88.             // clear buffer
  89.             rs232Port.DiscardInBuffer();
  90.             rs232Port.DiscardOutBuffer();

  91.             btnOpen.Text = "Close";
  92.             cbSerial.Enabled = false;
  93.             
  94.             textBox.Text = "Connecting...";

  95.             // send command "rxlog -1\x0D" to get the log status from module :"Start:0, Total:32767\r\n"
  96.             rs232Port.Write("rxlog -1\x0D");

  97.             try
  98.             {
  99.                 string rxLog = rs232Port.ReadLine();
  100.                 int s, t;
  101.                 s = rxLog.IndexOf("Start:");
  102.                 t = rxLog.IndexOf(", Total:");
  103.                 if (s != -1 && t != -1)
  104.                 {
  105.                     startSub = Int32.Parse(rxLog.Substring(s+6, t-(s+6)));
  106.                     totalSubs = Int32.Parse(rxLog.Substring(t+8));
  107.                     textBox.Text = "Start = " + startSub + ", Total = " + totalSubs+"\r\n";
  108.                 }
  109.                 else
  110.                 {
  111.                     throw new TimeoutException();
  112.                 }
  113.             }
  114.             catch (TimeoutException)
  115.             {
  116.                 textBox.Text =  "Connect failed!";
  117.                 return;
  118.             }
  119.             // connect successful, ready to get logs
  120.             btnGetLogs.Enabled = true;
  121.             subInprocess = startSub;
  122.         }

  123.         // 接收字节
  124.         private volatile bool byteRecIsBus = false;
  125.         void byteReceived(object sender, SerialDataReceivedEventArgs e)
  126.         {
  127.             while (byteRecIsBus == true) ;      // avoid reentrant
  128.             byteRecIsBus = true;
  129.             while (rs232Port.BytesToRead > 0)    // data to read
  130.             {
  131.                 int n = rs232Port.BytesToRead;
  132.                 byte[] buf = new byte[n];
  133.                 rs232Port.Read(buf, 0, n);      // read buffer
  134.                 for (int i = 0; i < n; i++)
  135.                 {
  136.                     rs232Buf.Enqueue(buf[i]);   // to queue
  137.                 }
  138.             }
  139.             byteRecIsBus = false;
  140.         }

  141.         // 启动接收
  142.         private void btnGetLogs_Click(object sender, EventArgs e)
  143.         {
  144.             rs232Port.DataReceived += byteReceived;    // delegate
  145.             btnGetLogs.Enabled = false;                // disable the button
  146.             getlogThread = new Thread(new ThreadStart(getLogAll));      // new thread to rx
  147.             getlogThread.Start();
  148.             updateTextBox = new UpdateTextBox(info2textbox);
  149.         }

  150.         // 接收线程
  151.         private void getLogAll()
  152.         {
  153.             int timeoutCnt = 0;
  154.             int crcErrCnt = 0;
  155.             while (totalSubs > 0)
  156.             {
  157.                 // to receive a new subsector, clear the buffer first
  158.                 rs232Buf.Clear();
  159.                 rs232Port.DiscardInBuffer();
  160.                 rs232Port.DiscardOutBuffer();

  161.                 // rxlog sub
  162.                 rs232Port.Write("rxlog " + subInprocess + "\x0D");
  163.                 // receive a sub
  164.                 // set timer
  165.                 setTimeout(1000);
  166.                 // waiting for data
  167.                 Thread.Sleep(300);
  168.                 try
  169.                 {
  170.                     // 1: head : 0xFAF5AA55
  171.                     byte[] headBuf = new byte[4] { 0, 0, 0, 0 };
  172.                     while ((headBuf[0] == 0xFA && headBuf[1] == 0xF5 && headBuf[2] == 0xAA && headBuf[3] == 0x55) == false)
  173.                     {
  174.                         headBuf[0] = headBuf[1];
  175.                         headBuf[1] = headBuf[2];
  176.                         headBuf[2] = headBuf[3];
  177.                         headBuf[3] = getByte();
  178.                     }
  179.                     for (int i = 0; i < subPack.HEAD_SIZE; i++)
  180.                     {
  181.                         subpack.head[i] = headBuf[i];
  182.                     }

  183.                     // 2&3: log and tail
  184.                     // The first 4 Bytes in a Log is timeStamp
  185.                     // If the timeStamp is 0xFAF5CC33 or LOG_CNT_MAX of Logs have been received, it means the Tail
  186.                     for (int i = 0; i < subPack.LOG_CNT_MAX + 1; i++)
  187.                     {
  188.                         // check first 4-byte
  189.                         int j;
  190.                         for (j = 0; j < 4; j++)
  191.                         {
  192.                             subpack.logsInSub[i].log14b[j] = getByte();
  193.                         }
  194.                         if ((subpack.logsInSub[i].getTimestamp() == 0xFAF5CC33) || (i == subPack.LOG_CNT_MAX))
  195.                         {// tail
  196.                             for (j = 0; j < 4; j++)
  197.                             {
  198.                                 subpack.tail[j] = subpack.logsInSub[i].log14b[j];
  199.                             }
  200.                             subpack.logCnt = i; // how many Logs in this subsector
  201.                             // goto crc32
  202.                             break;
  203.                         }
  204.                         // receive other 10 bytes
  205.                         for (; j < log.LOG_SIZE; j++)
  206.                         {
  207.                             subpack.logsInSub[i].log14b[j] = getByte();
  208.                         }
  209.                     }
  210.                     // 4: crc32
  211.                     for (int i = 0; i < subPack.CRC32_SIZE; i++)
  212.                     {
  213.                         subpack.crc32[i] = getByte();
  214.                     }
  215.                 }
  216.                 catch (TimeoutException)
  217.                 {
  218.                     // timeout error, retry this subsector
  219.                     // update textbox
  220.                     if(++timeoutCnt==5)
  221.                     {
  222.                         //??????
  223.                         throw new TimeoutException("Timeout 5 times");
  224.                     }
  225.                     this.BeginInvoke(updateTextBox, "timeout\r\n");
  226.                     continue;
  227.                 }

  228.                 if (subpack.isCrc32Correct() == false)
  229.                 {
  230.                     // crc32 error, retry this subsector
  231.                     if (++crcErrCnt == 5)
  232.                     {
  233.                         //??????
  234.                         throw new TimeoutException("Crc error 5 times");
  235.                     }
  236.                     this.BeginInvoke(updateTextBox, "crc err\r\n");
  237.                     continue;
  238.                 }
  239.                 // got a sub
  240.                 // save to file
  241.                 for (int i = 0; i < subpack.logCnt; i++)
  242.                 {
  243.                     // check date
  244.                     uint date = subpack.logsInSub[i].getDate();
  245.                     if (currentDate != date)
  246.                     {// new date, new file
  247.                         currentDate = date;
  248.                     }
  249.                     else
  250.                     {// same date, same file

  251.                     }
  252.                 }
  253.                 // <- a sub is over
  254.                 timeoutCnt = 0;
  255.                 crcErrCnt = 0;
  256.                 this.BeginInvoke(updateTextBox, "Got subsector:" + subInprocess + "\r\n");
  257.                 totalSubs--;
  258.                 subInprocess++;
  259.                 if (subInprocess >= 32768)
  260.                 {
  261.                     subInprocess = 0;
  262.                 }
  263.             }
  264.             // over
  265.             btnGetLogs.Enabled = false;

  266.             // ?????
  267.         }

  268.         // 从队列中取出字节
  269.         byte getByte()
  270.         {
  271.             while(rs232Buf.Count==0)
  272.             {
  273.                 if (timeoutMsTick)
  274.                 {
  275.                     throw new TimeoutException();
  276.                 }
  277.             }
  278.             return rs232Buf.Dequeue();
  279.         }

  280.         private void btnBrowse_Click(object sender, EventArgs e)
  281.         {
  282.             System.Windows.Forms.FolderBrowserDialog dlg = new System.Windows.Forms.FolderBrowserDialog();
  283.             dlg.Description = "Select the directory to save log files";
  284.             if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
  285.             {
  286.                 saveToDir.Text = dlg.SelectedPath.ToString();
  287.             }
  288.         }

  289.         private void Form1_FormClosing(object sender, FormClosingEventArgs e)
  290.         {
  291.             if (rs232Port.IsOpen == true)
  292.             {
  293.                 rs232Port.Close();
  294.                 // close file


  295.             }
  296.         }
  297.         
  298.         // 定时器 for timeout
  299.         private volatile bool timeoutMsTick;
  300.         private void timeoutMs_Tick(object sender, EventArgs e)
  301.         {
  302.             timeoutMsTick = true;
  303.             timeoutMs.Stop();
  304.         }

  305.         private void setTimeout(int ms)
  306.         {
  307.             timeoutMs.Stop();
  308.             timeoutMsTick = false;
  309.             timeoutMs.Interval = ms>0?ms:1;
  310.             timeoutMs.Start();
  311.         }
  312.         
  313.     }
复制代码


阿莫论坛20周年了!感谢大家的支持与爱护!!

曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……

出0入4汤圆

 楼主| 发表于 2016-1-5 14:37:18 | 显示全部楼层
本帖最后由 liuqian 于 2016-1-5 14:44 编辑

接收部分,不用queue,改用数组,改善很明显
之前几乎每个包都有crc err,而且会有多次重发,改良以后,crc err很少出现(几十个包出现一次),没有出现连续2次crc err的

  1.         // 接收字节
  2.         private volatile bool byteRecIsBus = false;
  3.         private volatile int rs232BufferWrIndex = 0;
  4.         private volatile byte[] rs232Buffer = new byte[subPack.SUBPACK_SIZE];
  5.         void byteReceived(object sender, SerialDataReceivedEventArgs e)
  6.         {
  7.             while (byteRecIsBus == true) ;      // avoid reentrant
  8.             byteRecIsBus = true;
  9.             while (rs232Port.BytesToRead > 0)    // data to read
  10.             {
  11.                 int n = rs232Port.BytesToRead;
  12.                 byte[] buf = new byte[n];
  13.                 rs232Port.Read(buf, 0, n);      // read buffer
  14.                 foreach (byte b in buf)
  15.                 {
  16.                     rs232Buffer[rs232BufferWrIndex++] = b;
  17.                     //rs232Buf.Enqueue(b);   // to queue
  18.                 }
  19.             }
  20.             byteRecIsBus = false;
  21.         }

  22.         // 从队列中取出字节
  23.         private volatile int rs232BufferRdIndex = 0;
  24.         byte getByte()
  25.         {
  26.             while (rs232BufferWrIndex == rs232BufferRdIndex)
  27.             {
  28.                 if (timeoutMsTick)
  29.                 {
  30.                     throw new TimeoutException();
  31.                 }
  32.             }
  33.             byte b = rs232Buffer[rs232BufferRdIndex++];
  34.             return b;
  35.         }

  36.         private void rs232BufferReset()
  37.         {
  38.             rs232BufferRdIndex = 0;
  39.             rs232BufferWrIndex = 0;
  40.         }
复制代码

出0入25汤圆

发表于 2016-1-5 20:18:10 | 显示全部楼层
本帖最后由 墨非 于 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)
    {
         //接收代码
    }
}
           
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-8-25 19:24

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表