Puertos con C#. Tramas y CRC.

Cuando necesitamos conectar con un dispositivo externo a través del puerto COM empiezan las peleas. Según el fabricante del dispositivo con el que queremos comunicarnos necesitamos construir una trama u otra. Cada una de las tramas con las que me he encontrado son de su padre y de su madre, algunas tramas exigen LSB(byte menos significativo)  y MSB(byte más significativo), otras CheckSum o CRC.  Las que piden  CRC suelen tener un algoritmo específico, CRC – 16,  CRC-CCITT, etc…

Otra cosa curiosa es el tema de la transparencia, método por el cual cuando te encuentras un carácter reservado dentro de la trama si va acompañado por un byte de escape hace que no utilicemos dicho carácter reservado como carácter especial, Inicio de datos, Fin de campo, etc…, sino como un carácter normal dentro de la trama, por ejemplo parte de una frase.

Pero gracias a la gente que publica su sapiencia en la red al final encuentras lo necesario para construirlas. El motivo de este artículo es intentar hacer un resumen para que aquel que se encuentre con las situaciones que yo me he encontrado no tenga que leer 35 post distintos, además de servirme a mí para futuras consultas.

Construir tramas

Según las especificaciones tendremos que construir una cosa u otra. Normalmente las tramas vienen definidas por un inicio y un fin, el inicio es llamado STX, 2 HEX, y el final de la trama un ETX,  3 HEX. Nosotros vamos a especificar una de ejemplo:

STX LBS MBS FLD Campo01 FLD CampoN CHK ETX

Trasparencia en las tramas

Este termino viene dado a la aplicación de bytes de escape para enmascarar bytes de control. En una trama en la que tenemos STX como byte de inicio y ETX como byte de fin de trama no podemos encontrarnos un 02 o un 03 dentro de dicha trama, ya que lo confundiríamos con un byte de control y entenderámos, por ejemplo, al encontrarnos un 03 que la trama ya ha finalizado, no siendo esto correcto.

Si utilizamos el byte de escape 1B Hex, siempre que nos encontremos este byte sabremos que el siguiente byte no es de control y que hay que eliminar de la trama el 1B encontrado.

Para el siguiente ejemplo:

02 1D 2A 3C 4E 02 05 03 DB 03

para enviar esta trama tendremos que aplicar la trasparencia quedando:

02 1D 2A 3C 4E 1B 02 05 1B 03 DB 03

Cálculo LSB y MSB

En algunas ocasiones nos piden que incluyamos dos byte de control siendo estos el byte mas significativo de la trama y el byte menos significativo de la misma. No voy a entrar a explicar que significa exactamente porque necesitaríamos un tema solo para ello, pero si voy a poner un ejemplo de su calculo:

        private byte DevolverByte(int Dato, byte nByte)
        {

            byte Resultado;
            string CadenaHexadecimal = "0xFF";

            if (nByte > 1)
            {
                Resultado = (byte)((Dato & Convert.ToInt64(CadenaHexadecimal.PadRight((nByte * 2) - 2, '0'), 16)) >> (8 * (nByte - 1)));
            }
            else
            {
                Resultado = (byte)(Dato & Convert.ToInt64("0x00FF", 16));
            }

            return Resultado;
        }

Donde Dato es el numero de bytes de la trama y nByte es el tipo, donde 1 es LSB y 2 es MSB.

CheckSum y CRC

Con mucha frecuencia en las tramas nos obligan a darles códigos de control para la prevención de errores aleatorios en la transmisión, donde se hace una serie de cálculos con la trama y el resultado se envía para que el receptor lo compruebe. Hay múltiples métodos, pero los más utilizados son dos, un simple ChecSum o CRC.

ChecSum es básicamente una suma de todos o de los primeros 8, 16, 32 o 64 bytes, según el tamaño de la trama que se utiliza. Este ejemplo hace una suma de todos

        private byte[] CheckSum(byte[] buffer)
        {
            List Resultado = new List();

            ushort longSum = 0;

            for (int i = 0; i < buffer.Length; ++i)
            {
                longSum += (ushort)buffer[i];
            }

            return  BitConverter.GetBytes(longSum);
        }

CRC (comprobación de redundancia cíclica) es una operatoria más compleja que el ChekSum, no voy a explicar como funciona, porque ni yo me entero, jejeje, os dejo una web que me ayudo a testear la función que os voy a dejar, ya que tiene un simulador de tipos de CRC, en concreto elque yo utilizo es el CRC-CCITT:
http://www.lammertbies.nl/comm/info/crc-calculation.html


 private static ushort[] table = { 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
                                          0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
                                          0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E,
                                          0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
                                          0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD,
                                          0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
                                          0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C,
                                          0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
                                          0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,
                                          0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
                                          0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A,
                                          0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
                                          0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9,
                                          0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
                                          0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738,
                                          0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
                                          0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7,
                                          0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
                                          0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
                                          0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
                                          0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
                                          0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
                                          0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
                                          0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
                                          0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
                                          0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
                                          0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
                                          0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
                                          0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
                                          0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
                                          0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
                                          0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78};

        private ushort ComputeChecksum(params byte[] buffer)
        {
            if (buffer == null) throw new ArgumentNullException();
            ushort crc = 0;
            for (int i = 0; i < buffer.Length; ++i)
            {
                crc = (ushort)((crc >> 8) ^ table[(crc ^ buffer[i]) & 0xff]);
            }
            return crc;
        }

 

Deja un comentario