Protocol_Lib.c 8.08 KB
Newer Older
时昊's avatar
时昊 committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
/**
 * @file        Protocol_Lib.c
 * @brief       串口协议解析
 * @details     串口协议解析
 * @author      myliu
 * @date        2022.05.09
 * @version     V1.0
 * @copyright   myiu
 */

#include "stdio.h"
#include "string.h"
#include "Protocol_CRC16.h"
// #include "debugger.h"
static UARTOpen          UARTOpen_Cbk;
static UARTSend          UARTSend_Cbk;
static UARTRead          UARTRead_Cbk;
static ProcParse         ProcParseCbk;
static UARTClose         UARTClose_Cbk;
static ProtocolSetData   ProtocolSetData_Cbk;
static Protocol_uint8_t *mDataBufPtr   = Protocol_NULL;
static Protocol_uint16_t  mDataBufLen   = 0;
static Protocol_uint32_t DataBufMaxLen = 0;
//#define DEBUG_PRO_DATA 0
/**
 * @brief 初始化函数
 * @param[in] pMemSpace 分配给协议库的内存空间,用来缓存串口数据
 * @param[in] MemLen    分配的内存空间大小
 * @param[in] pFunc     回调函数,包括串口打开、发送、读取、关闭,也包括解析后数据回传
 * @param[in] ProcParseCbk 此回调函数,返回数据从FrameNo开始到CRC16之前,和ProtocolSetData_Cbk二选一
 * @param[in] ProtocolSetData_Cbk 此回调函数,返回数据区分命令字、电源状态等,和ProcParseCbk二选一,详见结构体Protocol_Data_t
 *
 * @warning 此函数KL30和Wakeup都要调用,否则未分配内存,功能不好使,也有可能造成野指针复位
 *
 * @since 1.0.0
 */
void Protocol_Init(Protocol_uint8_t *pMemSpace, Protocol_uint32_t MemLen, Protocol_Func_t *pFunc)
{
    mDataBufPtr         = pMemSpace;
    DataBufMaxLen       = MemLen;
    UARTOpen_Cbk        = pFunc->UARTOpen_Cbk;
    UARTSend_Cbk        = pFunc->UARTSend_Cbk;
    UARTRead_Cbk        = pFunc->UARTRead_Cbk;
    ProcParseCbk        = pFunc->ProcParseCbk;
    UARTClose_Cbk       = pFunc->UARTClose_Cbk;
    ProtocolSetData_Cbk = pFunc->ProtocolSetData_Cbk;

    if ( UARTOpen_Cbk != Protocol_NULL )
    {
        UARTOpen_Cbk( );
    }

    return;
}

/**
 * @brief 串口协议服务函数,包括读取数据,解析数据,如在外部读取数据,可不调用此函数
 *
 * @warning 此函数可自定义周期调用,建议20ms周期调用,最大不可超过协议的最小发送周期
 *
 * @since 1.0.0
 */
void Protocol_Service(void)
{
    int               len;
    Protocol_uint32_t readNum = 0;

    if ( UARTRead_Cbk != Protocol_NULL )
    {
        readNum = UARTRead_Cbk(mDataBufPtr + mDataBufLen, 256 - mDataBufLen);
        if ( readNum > 0 )
        {
            mDataBufLen += readNum;
            // 解析协议
            len = Protocol_Parse(mDataBufPtr, mDataBufLen);
            if ( (len > 0) && (len < mDataBufLen) )
            {
                // 将未解析的数据移到头部
                // Move unparsed data to the head
                memcpy(mDataBufPtr, mDataBufPtr + len, mDataBufLen - len);
            }

            mDataBufLen -= len;
        }
    }
}

/**
 * @brief 协议解析函数,如外部获取数据(例如中断),可直接调用此函数解析数据
 * @param[in] pData     协议数据内容
 * @param[in] len       需要处理的协议数据长度
 *
 * @return    剩余已处理的数据长度
 *
 * @since 1.0.0
 */
Protocol_uint32_t Protocol_Parse(const Protocol_uint8_t *pData, Protocol_uint32_t len)
{
    Protocol_uint32_t remainLen = len;    // 剩余数据长度 Remaining data length
    Protocol_uint32_t dataLen;            // 数据包长度 Packet length
    Protocol_uint32_t frameLen;           // 帧长度 Frame length
    Protocol_uint32_t frameSum;
    Protocol_Data_t   ProcData;
    int               i = 0;
    int               dataBuf [ 256 ];

    /**
     * 以下部分需要根据协议格式进行相应的修改,解析出每一帧的数据
     */
    while ( remainLen >= DATA_PACKAGE_MIN_LEN )
    {
        // 找到一帧数据的数据头
        // Find the data header of a frame of data
        while ( (remainLen >= 2) && ((pData [ 0 ] != CMD_HEAD1) || (pData [ 1 ] != CMD_HEAD2)) )
        {
            pData++;
            remainLen--;
            continue;
        }

        if ( remainLen < DATA_PACKAGE_MIN_LEN )
        {
#ifdef DEBUG_PRO_DATA

            // d_printf("too short!!!!!!\n");
          #endif
            break;
        }

        dataLen  = pData [ 2 ];
        frameLen = dataLen + DATA_PACKAGE_FIXED_LEN;
        if ( frameLen > remainLen )
        {
            // 数据内容不全
#ifdef DEBUG_PRO_DATA

            // d_printf("Incomplete data packet!!!!!!\n");
#endif
            break;
        }

        // 打印一帧数据,需要时在CommDef.h文件中打开DEBUG_PRO_DATA宏
#ifdef DEBUG_PRO_DATA
        for (  i = 0; i < frameLen; ++i )
        {
            // d_printf("%x ", pData [ i ]);
        }
        // d_printf("\n");
#endif

        // 检测校验码 Checksum
        frameSum = (pData [ frameLen - 2 ] << 8) | (pData [ frameLen - 1 ]);
        if ( getCheckSum(pData + 2, frameLen - 4) == frameSum )
        {
            // 解析一帧数据
            if ( ProcParseCbk != Protocol_NULL )
            {
                ProcParseCbk(pData + 3, dataLen - 2);
            }

            if ( ProtocolSetData_Cbk != Protocol_NULL )
            {
                ProcData.FrameNo  = pData [ 3 ];
                ProcData.PowerSts = (pData [ 4 ] >> 6) & 0x03;
                ProcData.CmdID    = pData [ 4 ] & 0x3F;
                if ( ProcData.CmdID == 0x10 )
                {
                  #ifdef DEBUG_PRO_DATA
                    for ( i = 0; i < frameLen; ++i )
                    {
                    //   d_printf("%x ", pData [ i ]);
                    }
                    // d_printf("\n");
                  #endif
                }
                if ( ProcData.CmdID == 0x12 )
                {
                  #ifdef DEBUG_PRO_DATA
                    for ( i = 0; i < frameLen; ++i )
                    {
                    //   d_printf("%x ", pData [ i ]);
                    }
                    // d_printf("\n");
                  #endif
                }
                ProcData.DataLen = dataLen - 4;
                memcpy(ProcData.Data, pData + 5, ProcData.DataLen);
                ProtocolSetData_Cbk(&ProcData);
            }
        }
        else
        {
            for ( i = 0; i < frameLen; ++i )
            {
                dataBuf [ i ] = pData [ i ];
            }
            i = 0;
#ifdef DEBUG_PRO_DATA

            // d_printf("CheckSum error: new = %x, old = %x!!!!!!\n", getCheckSum(pData + 2, frameLen - 4), frameSum);
#endif
        }

        pData += frameLen;
        remainLen -= frameLen;
    }

    return len - remainLen;
}

/**
 * 根据协议格式进行拼接
 */
/**
 * @brief 串口协议数据拼接,如初始化发送函数,调用此函数后,数据已通过串口发送
 * @param[in] cmdID     命令字
 * @param[in] pData     协议数据内容(不包括协议头、长度、帧序号、命令字、校验和,从数据域算起)
 * @param[in] len       数据域长度
 *
 * @return    已发送的数据长度
 *
 * @since 1.0.0
 */
Protocol_uint32_t Protocol_Send(const Protocol_uint16_t cmdID, const Protocol_uint8_t *pData, Protocol_uint8_t len)
{
    int               i        = 0;
    Protocol_uint16_t checksum = 0;
    Protocol_uint8_t  dataBuf [ 256 ];
    Protocol_uint32_t frameLen;

    if ( len + DATA_PACKAGE_MIN_LEN > 256 )
    {
        // printf("sendProtocol data is too len !!!\n");
        return 0;
    }

    dataBuf [ 0 ] = CMD_HEAD1;
    dataBuf [ 1 ] = CMD_HEAD2;    // 同步帧头 Sync frame header

    dataBuf [ 2 ] = len + 4;
    dataBuf [ 3 ] = 0;    // 命令字节 Command byte

    dataBuf [ 4 ] = ( Protocol_uint8_t )cmdID;

    frameLen = 5;

    // 数据 Data
    for ( i = 0; i < len; ++i ) 
    {
        dataBuf [ frameLen ] = pData [ i ];
        frameLen++;
    }

    // 校验码 Checksum
    checksum             = getCheckSum(dataBuf + 2, frameLen - 2);
    dataBuf [ frameLen ] = (checksum >> 8) & 0x00FF;
    frameLen++;
    dataBuf [ frameLen ] = checksum & 0x00FF;
    frameLen++;

    if ( UARTSend_Cbk != Protocol_NULL )
    {
        return UARTSend_Cbk(dataBuf, frameLen);
    }
    else
    {
        return 0;
    }
}