[STC32G Application] How does the MCU implement JSON parsing

Tip: After the article is written, the table of contents can be automatically generated. How to generate it can refer to the help document on the right


foreword

SON strings are widely used at the application level, such as Android and cloud platforms. For high-level language developers, it is much simpler than low-level hexadecimal development. Therefore, many protocols between Android and MCU are also based on JSON. It is not difficult for ARM series microcontrollers to parse JSON. Just use the CJSON library. JSON parsing and encapsulation can be realized through some interface calls. However, one thing to note is that this library still consumes resources, and requires malloc to dynamically apply for memory. Compared with ARM's more than ten K, tens of K of RAM, the problem is not big.

But with the past two years, chip prices have changed. For example, the price of ST has fluctuated violently in recent years. Although it has led to the rapid rise of domestic alternatives. Such as: GD, APM and other PINTOPIN alternatives. But the eyes of many users have returned to the 51-series MCU.

Although the development environment is not friendly, the architecture is very different from ARM, and the resources are few, it cannot withstand the temptation of cheap prices. In this era, MCUs that cost a few dollars are still popular, and now 51 single-chip microcomputers, such as STC, are not bad in speed, analog performance, and Flash capacity. There is also on-chip EEPROM, ISP upgrades that support serial port custom commands, and timer resources. etc. The only downside is that the memory is too small. And it is not friendly to some C codes and functions based on pointer operations.
Then in the process of reducing the overall cost of the system, if you want to keep the agreement unchanged. For example, what about JSON?


1. JSON parsing library

It is inappropriate to quote the ready-made CJSON, one is that the support of the whole protocol is not required. The second one is also time-consuming and labor-intensive to crop according to the application. On the contrary, it is not as convenient as simply re-creating a small library.

The overall small library can be roughly divided into three parts:

1. Analysis of serial port protocol (most of them still use serial port);

2. Parsing and encapsulation of JSON;

3. Circular queue QUEUE.

To achieve a less resource-intensive JSON protocol parsing. Of course, there are advantages and disadvantages. Fewer resources will bring more restrictions. For example, the protocol is relatively fixed, and the storage of queues uses fixed-length buffers as units, which cannot achieve high memory usage efficiency.

2. Realization

1. Serial port analysis

The code is as follows (example):

void vACLRcvDataHandle(uint8_t ucData, uint8_t ucOrder)

{
    
    

    static uint16_t usLengthFlag    = 0;

    static uint8_t  *pCmdLine       = NULL;

    static uint8_t  ucACLHead[3]    = {
    
    0};

    static uint16_t usCounter       = 0;

    static ACLRcvSta_e eACLRcvSta   = eACLRcvStaIdle;

    static uint8_t ucCheck          = 0;



    uint8_t ucTemp                  = ucData;



    if(ucOrder == ACL_RCV_STA_CLR) {
    
    



        eACLRcvSta  = eACLRcvStaIdle;

        return;

    }

    switch(eACLRcvSta)

    {
    
    

    case eACLRcvStaIdle:            //serch  head

        ucACLHead[0] = ucACLHead[1];

        ucACLHead[1] = ucACLHead[2];

        ucACLHead[2] = ucTemp;

        // 寻找帧头

        if(memcmp(ucACLHead, GucACLHeadBuf, 3) == 0)

        {
    
    

            eACLRcvSta  = eACLRcvStaType;

            pCmdLine    = GucACLRcvBuf;

            ucACLHead[2] = 0x00;



        }



        break;

    case eACLRcvStaType:        //serch  TYPE

        // 数据类型判断

        if((ucTemp == eACLRcvTypeNormal) || (ucTemp == eACLRcvTypeUpdate))

        {
    
    

            eACLRcvSta = eACLRcvStaLength;

            // 长度标识

            usLengthFlag = 0;

            ucCheck = 0;

            *pCmdLine = ucTemp;

            pCmdLine++;

        }

        else

        {
    
    

            eACLRcvSta = eACLRcvStaIdle;

        }

        break;

    case eACLRcvStaLength:      //serch  Length



        usLengthFlag++;

        *pCmdLine = ucTemp;

        pCmdLine++;

        ucCheck += ucTemp;

        // 数据长度计算

        if(usLengthFlag >= 2)

        {
    
    

            usLengthFlag = (GucACLRcvBuf[1] << 8) + GucACLRcvBuf[2];//计算数据长度,高位移位加低位

            if( usLengthFlag >= 3) {
    
    



                // 数据长度越界检测

                if(usLengthFlag > ACL_COMM_MAX_SIZE ) {
    
    

                    eACLRcvSta = eACLRcvStaIdle;

                } else {
    
    

                    usCounter = 0;

                    eACLRcvSta = eACLRcvStaRecving;

                }

            } else {
    
    

                eACLRcvSta = eACLRcvStaIdle;

            }

        }



        break;

    case eACLRcvStaRecving: //push receive data to cmd line

        //存数据

        *pCmdLine = ucTemp;

        pCmdLine++;

        usCounter++;

        ucCheck += ucTemp;



        if(usCounter >= (usLengthFlag - 2)) {
    
    

            eACLRcvSta = eACLRcvStaCheck;

        }



        break;

    case eACLRcvStaCheck:

        if(ACL_CHECK) {
    
    

            if(ucCheck != ucTemp ) {
    
    

                eACLRcvSta = eACLRcvStaIdle;

            } else {
    
    

                eACLRcvSta = eACLRcvStaEnd;

            }

        } else  {
    
    

            eACLRcvSta = eACLRcvStaEnd;

        }

        break;

    case eACLRcvStaEnd:

        *pCmdLine = '0';        // add string end flag

        if(ucTemp == 0xFF)

        {
    
    

            eACLRcvSta = eACLRcvStaIdle;

            vACLPortQueueSendJson(GucACLRcvBuf);

        } else {
    
    

            eACLRcvSta = eACLRcvStaIdle;

        }



        break;



    default:

        eACLRcvSta = eACLRcvStaIdle;

        break;

    }

}

This can be designed according to their respective protocols. In addition to the JSON string, the DEMO here adds a frame header frame tail frame length check digit frame type.

The analysis method is basically similar to the analysis method of the commonly used AT command, which is relatively simple and reliable. After the parsing is completed, it is stored in a circular queue. The length of the queue can be defined by yourself, and it can be defined longer if there are more resources. If there are fewer resources, the definition should be shorter.

The smallest resource consists of a unit of serial port receive buffer. One unit of receive queue and one unit of send queue. If the unit length is 100 bytes, the minimum consumption of the entire library may be less than 500 bytes. Still pretty friendly for 51.

2. Parsing and encapsulation of JSON

The code is as follows (example):

static ACLBaseType prxJsonParse(uint8_t *pBuf,ACLOrder_s *psACLOrders)

{
    
    

    uint8_t *cmd_json = NULL;

    uint8_t *cmd_type = NULL;

    uint8_t *cmd_var1 = NULL;

    uint8_t *cmd_var2 = NULL;



    // Init

    psACLOrders->ucType = 0xFF;

    psACLOrders->ulVar1 = 0xFF;

    psACLOrders->ulVar2 = 0xFF;

   

    cmd_json = pBuf;



    // 判断是否存在JSON串

    if(cmd_json[0] != '{')

    {
    
    

        return ACL_FALSE;

    }

    else

    {
    
    

        cmd_type = strstr(cmd_json, "type");

        cmd_var1 = strstr(cmd_json, "var1");

        cmd_var2 = strstr(cmd_json, "var2");

        

        // Carefully !! atoi return 0 even if str has no number

        if( !cmd_type)

        {
    
    

            return ACL_FALSE;

        }

        else

        {
    
    

            psACLOrders->ucType = atoi(cmd_type+6);

        }



        if(cmd_var1)

        {
    
    

            psACLOrders->ulVar1 = atoi(cmd_var1+6);

        }



        if(cmd_var2)

        {
    
    

            psACLOrders->ulVar2 = atoi(cmd_var2+6);

        }



    }



    return ACL_TRUE;

}



static ACLBaseType vACLSendJsonConcatenation( ACLSendType_e eACLSendType, ACLOrder_s *pACLOrder,  char *pcBuffer,  uint16_t *pusLen)

{
    
    

    uint8_t *pPoint = pcBuffer;

    BUFFER_LOCAL uint8_t ucTempBuf[ACL_VAR2_BUF_SIZE+7] = {
    
    0};

    uint8_t ucTemp = 0;



    char *pcString = NULL;

    if(((POINTE_CAST)pPoint == NULL)||((POINTE_CAST)pACLOrder == NULL)) {
    
    

        return ACL_FALSE;

    }

    /* 创建JSON根节点 */

    pPoint[0] = '{';

    pPoint++;

    // Add type

    ucTemp = sprintf(ucTempBuf,"\"type\":%d,",(uint16_t)pACLOrder->ucType);



    memcpy(pPoint,ucTempBuf,ucTemp);

    pPoint += ucTemp;



    switch(eACLSendType)

    {
    
    

    case eACLSendVar2None:

        pACLOrder->ulVar2 = 0;

    case eACLSendVar2Int:

    {
    
    

        ucTemp = sprintf(ucTempBuf,"\"var1\":%d,",(uint16_t)pACLOrder->ulVar1);

        memcpy(pPoint,ucTempBuf,ucTemp);

        pPoint += ucTemp;

        ucTemp = sprintf(ucTempBuf,"\"var2\":%d",(uint16_t)pACLOrder->ulVar2);

        memcpy(pPoint,ucTempBuf,ucTemp);

        pPoint += ucTemp;



        break;

    }

    case eACLSendVar2Str:

    {
    
    

        ucTemp = sprintf(ucTempBuf,"\"var1\":%d,",(uint16_t)pACLOrder->ulVar1);

        memcpy(pPoint,ucTempBuf,ucTemp);

        pPoint += ucTemp;

        ucTemp = sprintf(ucTempBuf,"\"var2\":");

        memcpy(pPoint,ucTempBuf,ucTemp);

        pPoint += ucTemp;

        memcpy(pPoint,pACLOrder->ucVar2Buf,strlen(pACLOrder->ucVar2Buf)%(ACL_VAR2_BUF_SIZE+1));

        pPoint += strlen(pACLOrder->ucVar2Buf)%(ACL_VAR2_BUF_SIZE+1);

        break;

    }

    default:

    {
    
    

        break;

    }

    }

    *(pPoint++) = '}';



    *pusLen = (uint16_t)(pPoint - pcBuffer);



    return ACL_TRUE;

}

Parsing and encapsulation are also defined according to their own protocols. Basically, the interface in the standard library function is used. Relatively simple strstr atoi memcpy sprintf. There is no difficulty. Although the library function is referenced, the efficiency is relatively fast. There is no big difference in general applications.

3. Circular queue QUEUE

extern INT8U queueCreate(  DATAQUEUE *Queue,

                               void *pBuf,

                               INT32U ulSizeOfBuf,

                               INT8U (* pfuncReadEmpty)(),

                               INT8U (* pfuncWriteFull)()

                            );

 extern INT8U queueReadNData(DATAQUEUE *Queue, uint8_t *pucDestBuf, uint8_t ucNumber)  ;

 extern INT8U queueWriteNData(DATAQUEUE *Queue, uint8_t *pucSrcBuf, uint8_t ucNumber);

Each part of the queue has its own favorite or commonly used queue library. As long as the queue is initialized and the queue is entered and exited, it will be fine.

Reminder that critical section protection is still necessary.


Summarize

In this way, a simple JSON parsing is simply implemented. The entire library is also very transparent, and there is basically no deep part of the package. It is also relatively simple, according to your own agreement, it will be finished in about 1 day.

And because of the existence of the circular queue, it also supports the buffering of commands to reduce the loss of commands.

Note: The article is original by the author and published at: Z station https://z.zlg.cn/articleinfo?id=852836

Guess you like

Origin blog.csdn.net/lunzilx/article/details/131912509