天天看點

.Net Micro Framework研究—序列槽操作

.Net Micro Framework研究—序列槽操作

試驗平台:Digi MF開發闆

Digi提供的示例中包含了序列槽的示例程式,主要代碼如下:

public bool EchoByte()

        {

            SerialPort serial;

            bool exceptionRaised = false;

            bool testResult = true;

            string message = " This is an echo test.  Enter the character to echo, or ESC to exit. ";

            byte[] encodedMessage = c_encoding.GetBytes(message);

            byte[] buffer = new byte[1];

            try

            {

                serial = new SerialPort(new SerialPort.Configuration(Cpu.Serial.COM1, Cpu.BaudRate.Baud115200, false));

                serial.Write(encodedMessage, 0, message.Length);

                while (buffer[0] != 0x1b)

                {

                    serial.Read(buffer, 0, buffer.Length, Timeout.Infinite);

                    serial.Write(buffer, 0, buffer.Length);

                }

                serial.Dispose();

            }

            catch

                exceptionRaised = true;

            if (exceptionRaised == true)

                testResult = false;

            return testResult;

        }

部署運作後,你可以用超級終端進行測試,測試圖如下:

(圖MF10280001.JPG)

注意:如果序列槽程式非正常退出,有可能導緻開發闆無法發送資料(接收倒是正常),重新開機開發闆即可。

用測試程式還是展現不出.Net Micro Framework的優勢,我決定用MF實作Modbus Rtu Slave服務端(支援Modbus Rtu 3号指令),并且位址為0的資料存放了GPIO入的資訊,這樣在上位機就很方面的檢測IO信号了。

用了大約15分鐘,就把我以前用C++開發的Modbus Rtu Slave程式移植到MF平台上來的,我想如果用單片來開發,雖然也有可能借用以前的代碼,但很友善的把IO信号也非常快捷的內建進來,恐怕不容易。

值得一提的是VS2005 的調試功能非常強大,很容易添加斷點及監控目前變量的值,同時用debug.print()指令也非常好使,這樣調試程式絕對比調試單片舒服。

下面貼出我寫的Modbus RtuSlave代碼

using System;

using Microsoft.SPOT;

using System.Threading;

using Microsoft.SPOT.Hardware;

namespace MFModbus

{

    public class ModbusRtu

    {

        private Thread m_worker;

        private bool m_RunFlag;

        private byte bytRtuDataFlag=0;

        private byte bytRtuDataIdx;

        private byte[] bytRtuData = new byte[8];

        //裝置位址,預設為1    

        private byte ModbusAddr = 1;

        //資料區(注意,Modbus讀寫是以字(雙位元組)為機關的)

        private byte[] DataBuff = new byte[128];

        SerialPort serial = null;

        InputPort[] input = new InputPort[5];

        Cpu.Pin[] pin = new Cpu.Pin[5] { (Cpu.Pin)0, (Cpu.Pin)1, (Cpu.Pin)2, (Cpu.Pin)5, (Cpu.Pin)6 };

        public ModbusRtu(byte mModbusAddr)

            ModbusAddr=mModbusAddr;

            for (int i = 0; i < 5; i++)

                input[i] = new InputPort(pin[i], false, Port.ResistorMode.PullUp);

        ~ModbusRtu()

            Stop();

        //CRC16校驗

        private UInt16 GetCheckCode(byte[] buf, int nEnd)

            UInt16 crc = (UInt16)0xffff;

            int i, j;

            for (i = 0; i < nEnd; i++)

                crc ^= (UInt16)buf[i];

                for (j = 0; j < 8; j++)

                    if ((crc & 1) != 0)

                    {

                        crc >>= 1;

                        crc ^= 0xA001;

                    }

                    else

            return crc;

        //啟動Modbus服務

        public void Run()

                //僅有波特率選項,竟然沒有奇偶校驗控制

                serial = new SerialPort(new SerialPort.Configuration(Serial.COM1, BaudRate.Baud9600, false));

                Debug.Print("Open Serial OK");

                m_worker = new Thread(new ThreadStart(this.ModbusThreadProc));

                m_RunFlag = true;

                m_worker.Start();

                Debug.Print("Serial Error");

            }           

        //停止Modbus服務

        public void Stop()

            m_RunFlag = false;

            if (serial != null)

        //Modbus Slave服務

        private void ModbusThreadProc()

            Debug.Print("Start Modbus Slave");

            byte[] bytData=new byte[1];

            while (m_RunFlag)

                serial.Read(bytData, 0, bytData.Length, Timeout.Infinite);

                RtuSlave(bytData[0]);

        //序列槽資料處理

        private void RtuSlave(byte bytData)

            //Debug.Print(bytRtuDataIdx.ToString() + " - " + bytData.ToString());

            if (bytRtuDataFlag == 0)

                //如果資料為首位址

                if (bytData == ModbusAddr)

                    bytRtuDataFlag = 1;

                    bytRtuDataIdx = 0;

                    bytRtuData[bytRtuDataIdx++] = bytData;

            else

                bytRtuData[bytRtuDataIdx++] = bytData;

                if (bytRtuDataIdx >= 8)

                    //資訊處理

                    UInt16 intCRC16 = GetCheckCode(bytRtuData, 8 - 2);

                    //Debug.Print("CRC:" + bytRtuData[8 - 2].ToString() + " " + ((byte)(intCRC16 & 0xFF)).ToString() +"|" + bytRtuData[8 - 1].ToString() + " " + ((byte)((intCRC16 >> 8) & 0xff)).ToString());

                    //CRC16校驗檢驗

                    if (bytRtuData[8 - 2] == (intCRC16 & 0xFF) && bytRtuData[8 - 1] == ((intCRC16 >> 8) & 0xff))

                        byte[] bytSendData = new byte[255];

                        byte bytErrorFlag = 0;

                        byte bytErrorNo = 1;

                        //Debug.Print("CRC OK");

                        //讀資料

                        if (bytRtuData[1] == 3)

                        {

                            UInt16 lngDataAddr = bytRtuData[2];

                            lngDataAddr = (UInt16)((lngDataAddr << 8) + bytRtuData[3]);  //位址

                            UInt16 lngDataNum = bytRtuData[4];

                            lngDataNum = (UInt16)((lngDataNum << 8) + bytRtuData[5]);    //數量

                            if (lngDataAddr * 2 + lngDataNum * 2 > 1024 || lngDataNum > 120)

                            {

                                bytErrorNo = 2;

                                bytErrorFlag = 0;

                            }

                            else

                                bytSendData[0] = bytRtuData[0];

                                bytSendData[1] = bytRtuData[1];

                                bytSendData[2] = (byte)(lngDataNum * 2);

                                //讀GPIO信号

                                DataBuff[0] = 0;

                                DataBuff[1] = (byte)((input[0].Read() ? 1 : 0) | (input[1].Read() ? 2 : 0) | (input[2].Read() ? 4 : 0) | (input[3].Read() ? 8 : 0) | (input[4].Read() ? 16 : 0));

                                for (int i = 0; i < bytSendData[2]; i++)

                                {

                                    bytSendData[3 + i] = DataBuff[lngDataAddr * 2 + i];

                                }

                                intCRC16 = GetCheckCode(bytSendData, 3 + lngDataNum * 2);

                                bytSendData[3 + lngDataNum * 2] = (byte)(intCRC16 & 0xFF);                    //CRC校驗低位

                                bytSendData[4 + lngDataNum * 2] = (byte)((intCRC16 >> 8) & 0xff);             //CRC校驗高位                  

                                //發送資料

                                int intRet=serial.Write(bytSendData, 0, 5 + lngDataNum * 2);

                                //Debug.Print("SendData OK " + intRet.ToString() );

                                bytErrorFlag = 1;

                        }

                        if (bytErrorFlag == 0)

                            //協定不支援

                            bytSendData[0] = bytRtuData[0];

                            bytSendData[1] = (byte)(bytRtuData[1] | 0x80);

                            bytSendData[2] = bytErrorNo;

                            intCRC16 = GetCheckCode(bytSendData, 3);

                            bytSendData[3] = (byte)(intCRC16 & 0xFF);                       //CRC校驗低位

                            bytSendData[4] = (byte)((intCRC16 >> 8) & 0xff);                //CRC校驗高位

                            //發送資料

                            serial.Write(bytSendData, 0, 5);

                    bytRtuDataFlag = 0;

            return;

        //序列槽号

        public static class Serial

            public const SerialPort.Serial COM1 = (SerialPort.Serial)0;

            public const SerialPort.Serial COM2 = (SerialPort.Serial)1;

        //序列槽波特率

        public static class BaudRate

            public const SerialPort.BaudRate Baud4800 = (SerialPort.BaudRate)4800;

            public const SerialPort.BaudRate Baud9600 = (SerialPort.BaudRate)9600;

            public const SerialPort.BaudRate Baud19200 = (SerialPort.BaudRate)19200;

            public const SerialPort.BaudRate Baud38400 = (SerialPort.BaudRate)38400;

            public const SerialPort.BaudRate Baud57600 = (SerialPort.BaudRate)57600;

            public const SerialPort.BaudRate Baud115200 = (SerialPort.BaudRate)115200;

            public const SerialPort.BaudRate Baud230400 = (SerialPort.BaudRate)230400;

    }

}

程式部署運作後,直接用标準的Modbus Rtu用戶端程式測試即可,我用的是我以前編寫的Modbus Rtu Client程式,測試如下: 

 (圖MF10280002.JPG) 

這時候,你直接操作SW2的撥碼,該數字就會發生變化(前提SW1的撥碼都撥到右邊)。

缺點:很奇怪的是序列槽的參數僅能配置波特率,奇偶校驗,資料位卻無法配置。

總的印象:用MF開發嵌入式系統還是非常有前景的,至少使産品的開發周期大大縮短,并且代碼更新維護友善。

繼續閱讀