当前位置:编程学习 > C#/ASP.NET >>

串口编程[Serialport]之this.invoke问题

最近在做个上位机程序 上位机发送指令读取下位机的数据  之前一直没有接触过串口编程这块 所以问题挺多的。。 
 我用一个时钟 定时发送指令去读取下位机数据 然后 显示出来 但是不知道怎么的  运行状态下  程序好像不进入 thi.invoke代码段(因为它不显示出数据,感觉没读取到数据一样),但是我一设置断点调试 它就进去了 然后也能读取到数据显示出来。。。 不怎么是咋么回事:下面是代码段  请指教:


public partial class MainFrame : Form
    {
        private SerialPort comm = new SerialPort();
        private StringBuilder builder = new StringBuilder();//避免在事件处理方法中反复的创建,定义到外面。
        private long received_count = 0;//接收计数
        private long send_count = 0;//发送计数
        private long count = 0;

        private bool Listening = false;//是否没有执行完invoke相关操作
        private bool Closing = false;//是否正在关闭串口,执行Application.DoEvents,并阻止再次invoke
        private List<byte> buffer = new List<byte>(4096);//默认分配1页内存,并始终限制不允许超过
        private byte[] binary_data_1 = new byte[8];//AA 44 05 01 02 03 04 05 EA



        public MainFrame()
        {
            InitializeComponent();
        }

        private void MainFrame_Load(object sender, EventArgs e)
        {
            Helper.initSkin(skinEngine1);//初始化皮肤
            //初始化下拉串口名称列表框
            string[] ports = SerialPort.GetPortNames();
            Array.Sort(ports);
            //初始化SerialPort对象
            comm.NewLine = "\r\n";
            //comm.RtsEnable = true;//根据实际情况吧。
            //添加事件注册
            comm.DataReceived += comm_DataReceived;
            this.toolTip1.SetToolTip(this.lblSum, "XXXX");
        }
        void comm_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (Closing) return;//如果正在关闭,忽略操作,直接返回,尽快的完成串口监听线程的一次循环
            try
            {
                Listening = true;//设置标记,说明我已经开始处理数据,一会儿要使用系统UI的。
                int n = comm.BytesToRead;//先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致
                byte[] buf = new byte[n];//声明一个临时数组存储当前来的串口数据
                received_count += n;//增加接收计数
                comm.Read(buf, 0, n);//读取缓冲数据

                //<协议解析>
                bool data_1_catched = false;//缓存记录数据是否捕获到
                //1.缓存数据
                buffer.Clear();
                buffer.AddRange(buf);
                //2.完整性判断
                if (buffer.Count >= 27)//1个起始位、2个从机位、2个命令位、2个字节计数位。
                {
                    //判断数据的正确性:起始位、从机地址、结束位
                    if (buffer[0] == 0x3A && buffer[1] == 0x30 && buffer[2] == 0x31 && buffer[n - 2] == 0x0D && buffer[n - 1] == 0x0A)
                    {
                        if (this.InvokeRequired)
                        {
                            this.Invoke((EventHandler)(delegate
                            {
                                string gross = Helper.GetLongData(buf, 7).ToString();
                                string net = Helper.GetLongData(buf, 15).ToString();
                                lblWeight.Text = string.Format("{0:n}", (Convert.ToDouble(net) / 100));
                            }));
                        }
                        // buffer.CopyTo(0, binary_data_1, 15, 8);//复制一条完整数据到具体的数据缓存
                        data_1_catched = true;
                        //buffer.RemoveRange(0, len + 7);//正确分析一条数据,从缓存中移除数据。
                    }
                    else
                    {
                        //这里是很重要的,如果数据开始不是头,则删除数据
                        buffer.Clear();
                    }

                }
                else
                {
                    buffer.Clear();
                }
                builder.Clear();//清除字符串构造器的内容
            }
            finally
            {
                Listening = false;//我用完了,ui可以关闭串口了。
            }
        }

 private void open()
        {
            //根据当前串口对象,来判断操作
            if (comm.IsOpen)
            {
                //打开时点击,则关闭串口
                comm.Close();
                this.timer1.Enabled = false;
                count = 0;
            }
            else
            {
                //关闭时点击,则设置好端口,波特率后打开
                if (SendValue.returnValue == null || SendValue.returnValue.Trim()=="")
                {
                    MessageBox.Show("请在设置里先设置好端口和波特率");
                    return;
                }
                var returnValue = SendValue.returnValue.Split('|');
                comm.PortName = returnValue[0].ToString();// comboPortName.Text;
                comm.BaudRate = int.Parse(returnValue[1].ToString());// int.Parse(comboBaudrate.Text);
                
                //comm.Parity = Parity.Odd;//设置奇偶校验
                try
                {
                    comm.Open();
                    //writeToPort();

                    this.timer1.Enabled = true;
                    this.timer1.Interval = 1000;
                }
                catch (Exception ex)
                {
                    //捕获到异常信息,创建一个新的comm对象,之前的不能用了。
                    comm = new SerialPort();
                    //现实异常信息给客户。
                    MessageBox.Show(ex.Message);
                }
            }
            //设置按钮的状态
            this.OnToolStripMenuItem.Text = comm.IsOpen ? "关 闭" : "启 动";
        }
private void timer1_Tick(object sender, EventArgs e)
        {
            writeToPort();
        }

 private void writeToPort()
        {
            byte[] m = { 0x3A, 0x30, 0x31, 0x30, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x34 };
            byte ddd = Convert.ToByte(Helper.DescToAscii(Helper.GetLRC(m) / 16));//70
            byte abc = Convert.ToByte(Helper.DescToAscii(Helper.GetLRC(m) % 16));//56
            byte[] bb = { 0x3A, 0x30, 0x31, 0x30, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x34, ddd, abc, 0x0D, 0x0A };
            if (!comm.IsOpen)
                comm.Open();
            comm.DiscardInBuffer();
            comm.Write(bb, 0, bb.Length);
        }



因为不太熟悉winform 以及串口编程这一块 所以 代码写的估计有点乱  请多多指教。
--------------------编程问答-------------------- 试试用ReadLine()
string str = this.serialPort1.ReadLine();
base.Invoke(this.updateText, new string[] { str });

这种方式来使用看看 --------------------编程问答-------------------- 必须顶一下。。。。 求更多高手指点。。 --------------------编程问答--------------------

buffer.Clear(); //这种做法是错误的,因为每次触发comm_DataReceived并不代表已经接收到完整的数据,
//很可能只接收到一部分,你把之前的都清除后就会破坏数据的完整性,难怪无法收到完整的数据,
buffer.AddRange(buf); //

。。。。。。
//这里是很重要的,如果数据开始不是头,则删除数据
buffer.Clear(); //你这里不合法就全部抛弃也是不好的,应该保留到下一次再比较

//至于测试时可以,那是因为测试环境下,代码运行效率很低,所以每次都能等到足够的数据来判断。
//所以最简单的办法是在comm_DataReceived事件,读取数据之前,使用Thread.Sleep(1000);
//等待到足够数据后再做处理。
--------------------编程问答--------------------
引用 3 楼 jimh 的回复:
C# code

buffer.Clear(); //这种做法是错误的,因为每次触发comm_DataReceived并不代表已经接收到完整的数据,
//很可能只接收到一部分,你把之前的都清除后就会破坏数据的完整性,难怪无法收到完整的数据,
buffer.AddRange(buf); //

。。。。。。
//这里是很重要的,如果数据开始不是头,则删除数据
buffer.Clear……


恩恩  好像有道理  试试再说。。。 非常感谢。。。 如果你有这种串口的例子 希望你可以发我一个 学习学习。。。感谢。
补充:.NET技术 ,  C#
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,