深入解析机智云源码

深入解析机智云源码

最近在搞了一块乐鑫的ESP8266模块,实现手机无线控制单片机,模块烧写的固件是机智云32M的,具体烧录过程就不说了,这里解释一下源码
** 队列我就不多说了已经有好多人解释过了,这里引用杰杰的解释,可转去https://www.arduino.cn/thread-80397-1-1.html
这里直接粘贴,下文便是:
ringbuffer.h
先看看头文件:ringbuffer.h。
主要是用宏实现了一个求最小值的函数。
还有就是定义了一个环形缓冲区的结构体。

#define min(a, b) (a)<(b)?(a):(b)                   ///< Calculate the minimum value

typedef struct {
    size_t rbCapacity;
    uint8_t  *rbHead;
    uint8_t  *rbTail;
    uint8_t  *rbBuff;
}rb_t;

复制代码

看英文就能知道意思了,rb是ringbuff的缩写,意思就是环形缓冲区,
结构体中rbCapacity是缓冲区的容量,也就是大小。
结构体中rbHead是缓冲区的头指针,
rbTail是缓冲区的尾指针,
而rBuff是缓冲区的首地址,在创建的时候就用到。
ringbuffer.c
环形缓冲区的创建
下面来看看源文件:

int8_t ICACHE_FLASH_ATTR rbCreate(rb_t* rb)
{
    if(NULL == rb)
    {
        return -1;
    }

    rb->rbHead = rb->rbBuff;
    rb->rbTail = rb->rbBuff;
    return 0;
}

复制代码

这是个创建环形缓冲区的函数,就是初始化了环形缓冲区的头尾指针,这个函数的通用性很强,因为很多时候不只创建一个缓冲区。每个缓冲区的首地址都保存在了rbBuff,这个在后面的通用性会很好用。但是杰杰还是觉得不够好,因为我们在结构体中定义了缓冲区的容量,但是在这里并没有给他初始化,我觉得应该传入应该参数,给缓冲区的容量进行初始化一下。但是无所谓啦。
环形缓冲区的删除

int8_t ICACHE_FLASH_ATTR rbDelete(rb_t* rb)
{
    if(NULL == rb)
    {
        return -1;
    }

    rb->rbBuff = NULL;
    rb->rbHead = NULL;
    rb->rbTail = NULL;
    rb->rbCapacity = 0;
        return 0;
}

复制代码

把这些指针指向NULL,但是环形缓冲区本身地址的数据是不会被清除的,只是表明了这些地址可以被重复使用了而已。

int32_t ICACHE_FLASH_ATTR rbCapacity(rb_t *rb)
{
    if(NULL == rb)
    {
        return -1;
    }

    return rb->rbCapacity;
}

复制代码

获取环形缓冲区的容量

int32_t ICACHE_FLASH_ATTR rbCapacity(rb_t *rb)
{
    if(NULL == rb)
    {
        return -1;
    }

    return rb->rbCapacity;
}

复制代码

因为可能有多个环形缓冲区,但是容量我们不一定会知道,所以还是写一个获取它容量的函数比较好。
环形缓冲区可读数据大小

int32_t ICACHE_FLASH_ATTR rbCanRead(rb_t *rb)
{
    if(NULL == rb)
    {
        return -1;
    }

    if (rb->rbHead == rb->rbTail)
    {
        return 0;
    }

    if (rb->rbHead < rb->rbTail)
    {
        return rb->rbTail - rb->rbHead;
    }

    return rbCapacity(rb) - (rb->rbHead - rb->rbTail);
}

复制代码

如果缓冲区是没有被创建的,那么返回-1,表示非法,如果环形缓冲区的首尾都在一个位置,那么表面环形缓冲区没有数据,那么是不可读的,否则就返回正常的数据,rb->rbTail - rb->rbHead / rbCapacity(rb) - (rb->rbHead - rb->rbTail),请用数学的方法理解这段代码。
获取环形缓冲区可写数据大小
同理获取可写数据也是一样的

int32_t ICACHE_FLASH_ATTR rbCanWrite(rb_t *rb)
{
    if(NULL == rb)
    {
        return -1;
    }

    return rbCapacity(rb) - rbCanRead(rb);
}

复制代码

环形缓冲区读数据

int32_t ICACHE_FLASH_ATTR rbRead(rb_t *rb, void *data, size_t count)
{
    int32_t copySz = 0;

    if(NULL == rb)
    {
        return -1;
    }

    if(NULL == data)
    {
        return -1;
    }

    if (rb->rbHead < rb->rbTail)
    {
        copySz = min(count, rbCanRead(rb));
        memcpy(data, rb->rbHead, copySz);
        rb->rbHead += copySz;
        return copySz;
    }
    else
    {
        if (count < rbCapacity(rb)-(rb->rbHead - rb->rbBuff))
        {
            copySz = count;
            memcpy(data, rb->rbHead, copySz);
            rb->rbHead += copySz;
            return copySz;
        }
        else
        {
            copySz = rbCapacity(rb) - (rb->rbHead - rb->rbBuff);
            memcpy(data, rb->rbHead, copySz);
            rb->rbHead = rb->rbBuff;
            copySz += rbRead(rb, (char*)data+copySz, count-copySz);
            return copySz;
        }
    }
}

复制代码

如果是缓冲区没被创建或者是读数据地址非法(NULL)都将返回错误。如果rb->rbHead < rb->rbTail,就是可读数据的地址是递增的,那么可以直接读数据,读取的最大数据不能超过缓冲区可读最大数据,所以要用copySz = min(count, rbCanRead(rb));限制一下读取数据的大小,因为是直接拷贝数据,所以,在较多数据面前的话,这种做法很好,比如像网络上的数据,更是适合用这种方法。读完之后把rbHead 头指针重新更新,rb->rbHead += copySz;因为环形缓冲区在数据存储(软件地址上)是环形的,所以,假如数据地址不是递增的,那么无法直接拷贝,需要分段拷贝,count < rbCapacity(rb)-(rb->rbHead - rb->rbBuff)如果要读取的数据小于从环形缓冲区的首地址开始到环形缓冲区大小的地址,那么这段地址还是递增的,所以可以直接拷贝过去,并且把头指针更新一下。

copySz = count;

memcpy(data, rb->rbHead, copySz);

rb->rbHead += copySz;

复制代码

最后一种情况就是,需要分段读取了,先把头指针到缓冲区最后一个地址的这部分读取了,再加上从缓冲区首地址开始读取count-copySz那么长数据的数据,就ok了。然后把两端数据拼接起来。数据保存在data中。

copySz = rbCapacity(rb) - (rb->rbHead - rb->rbBuff);
memcpy(data, rb->rbHead, copySz);
rb->rbHead = rb->rbBuff;
copySz += rbRead(rb, (char*)data+copySz, count-copySz);

复制代码

环形缓冲区写数据

int32_t ICACHE_FLASH_ATTR rbWrite(rb_t *rb, const void *data, size_t count)
{
    int32_t tailAvailSz = 0;

    if((NULL == rb)||(NULL == data))
    {
        return -1;
    }

    if (count >= rbCanWrite(rb))
    {
        return -2;
    }

    if (rb->rbHead <= rb->rbTail)
    {
        tailAvailSz = rbCapacity(rb) - (rb->rbTail - rb->rbBuff);
        if (count <= tailAvailSz)
        {
            memcpy(rb->rbTail, data, count);
            rb->rbTail += count;
            if (rb->rbTail == rb->rbBuff+rbCapacity(rb))
            {
                rb->rbTail = rb->rbBuff;
            }
            return count;
        }
        else
        {
            memcpy(rb->rbTail, data, tailAvailSz);
            rb->rbTail = rb->rbBuff;

            return tailAvailSz + rbWrite(rb, (char*)data+tailAvailSz, count-tailAvailSz);
        }
    }
    else
    {
        memcpy(rb->rbTail, data, count);
        rb->rbTail += count;
        return count;
    }
}

复制代码

与读书同理的,将一定长度的数据从某段地(data)址写入环形缓冲区。如果数据地址非法或者是可写数据长度不够,那么就会返回错误代码。先看后面的

else
{
    memcpy(rb->rbTail, data, count);
    rb->rbTail += count;
    return count;
}

复制代码

如果写数据的地址是地址的话,那么是可以直接写的,注意的是,写数据的地址并非读数据的地址,刚好相反的,可读数据的地址是绝对不允许写的。同理,假如写书的地址不是递增的话,那么,也是分成两段,假如写入数据的长度小于从尾指针到环形缓冲区最后一个地址的长度,那么,写入的这段数据其实其地址也是递增的,同样是可以直接写的,然后更新一下尾指针。

memcpy(rb->rbTail, data, count);
  rb->rbTail += count;
  if (rb->rbTail == rb->rbBuff+rbCapacity(rb))
{
    rb->rbTail = rb->rbBuff;
}

复制代码

否则,也需要分段写入,先写入从尾指针到环形缓冲区最后一个地址的长度,然后从环形缓冲区的首地址开始再写入剩下的数据长度count-tailAvailSz,

memcpy(rb->rbTail, data, tailAvailSz);
rb->rbTail = rb->rbBuff;
return tailAvailSz + rbWrite(rb, (char*)data+tailAvailSz, count-tailAvailSz);

复制代码

好了,至此,源码基本分析完毕,现在说说为什么比我的源码写得好,

第一点,代码的效率,我写的源码是一个个数据的写入,而机智云是一系列数据的写入。读数据也是一样,一系列数据读出,而我的源码则是一个个数据读出,并且使用了求模的运算防止指针越界,这在运算中效率是不够高的。

第二代码的健壮性,还是机智云的好,我的代码是没有检查是否真正有有效的数据写入。同样的代码读出也是检查了读出数据的地址是否真正有效,防止数据非法丢失。总的来说,需要不断成长,还是要研究研究别人商业上用的源码,虽然说很多原理我们都知道,但是亲自写的话,不一定能写得出来,

还有就是,重用现有源码比创新的效率更高,因为并不是所有人都能另走捷径,做开拓者的,我们用已有的好东西足以。

接下来说下机智云源码的主体程序

1.文件介绍

Alt text

重要文件解读:

  • gizwits_product.c
    该文件为产品相关处理函数,如gizwitsEventProcess()。

  • gizwits_product.h
    该文件为gizwits_product.c的头文件,如HARDWARE_VERSION、SOFTWARE_VERSION。

  • gizwits_protocol.c
    该文件为SDK API接口函数定义文件。

  • gizwits_protocol.h
    该文件为gizwits_protocol.c对应头文件,相关API的接口声明均在此文件中。

  • 其他文件
    a) User/main.c
    MCU程序入口函数所在文件,入口函数为main(void)。

2.API介绍

void gizwitsInit (void)

gizwits协议初始化接口。

用户调用该接口可以完成Gizwits协议相关初始化(包括协议相关定时器、串口的初始化)。

void gizwitsSetMode (uint8_t mode)

参数mode[in]:仅支持0,1和2,其他数据无效。

参数为0,恢复模组出厂配置接口,调用会清空所有配置参数,恢复到出厂默认配置。

参数为1或2,配置模式切换接口,支持SoftAP和AirLink模式。参数为1时配置模组进入SoftAp模式,参数为2配置模组进入AirLink模式。

void gizwitsHandle(dataPoint_t dataPoint)

参数dataPoint[in]:用户设备数据点。

该函数中完成了相应协议数据的处理即数据上报的等相关操作。

int8_t gizwitsEventProcess(eventInfo_t info, uint8_t data, uint32_t len)

参数info[in]:事件队列

参数data[in]:数据

参数len [in]:数据长度

用户数据处理函数,包括wifi状态更新事件和控制事件。

a). Wifi状态更新事件

WIFI_开头的事件为wifi状态更新事件,data参数仅在WIFI_RSSI有效,data值为RSSI值,数据类型为uint8_t,取值范围0~7。

b). 控制事件

与数据点相关,本版本代码会打印相关事件信息,相关数值也一并打印输出,用户只需要做命令的具体执行即可。

3.代码结构说明#

自动化代码生成工具已经根据用户定义的产品数据点信息,生成了对应的机智云串口协议层代码,用户需要移植代码到自己的工程中,完成设备的接入工作。如图如下:
在这里插入图片描述
代码绿色部分的协议逻辑和程序主流程已经帮用户实现,图中用黄色字体注标的部分待用户实现并完成代码的移植。用户的移植分以下几步进行:

a.搭建最小平台工程(必要)。

b.实现串口驱动(必要):包括通信与打印功能。

c.实现定时器驱动(必要)。

d.实现芯片复位函数(可选)。

e.实现应用层逻辑开发(必要):包括数据上下行、入网配置等。
MAIN函数的主体如下:

* @brief程序入口函数

* 在该函数中完成用户相关的初始化,及主逻辑循环
* @param none
* @return none
*/
int main(void)
{  
	uart1_init();
	uart2_init();
	Timer0_Init();
    userInit();
    gizwitsInit();        
    while(1)
    {
		setkeyscan();					//检测功能键
        userHandle();					//用户数据采集
        gizwitsHandle((dataPoint_t *)&currentDataPoint);      //数据上传与下载
    }
}

这里要说的是作重要的函数就是
gizwitsHandle((dataPoint_t )&currentDataPoint); 我们进去里面看

* @brief Protocol handling function
* @param [in] currentData :The protocol data pointer
* @return none
*/
int32_t gizwitsHandle(dataPoint_t *currentData)
{
    int8_t ret = 0;
#ifdef PROTOCOL_DEBUG
    uint16_t i = 0;
#endif
    uint8_t ackData[RB_MAX_LEN];
    uint16_t protocolLen = 0;
    uint32_t ackLen = 0;
    protocolHead_t *recvHead = NULL;
    char *didPtr = NULL;
    uint16_t offset = 0;


    if(NULL == currentData)
    {
        GIZWITS_LOG("GizwitsHandle Error , Illegal Param\n");
        return -1;
    }

    /*resend strategy*/
    gizProtocolAckHandle();
    ret = gizProtocolGetOnePacket(&pRb, gizwitsProtocol.protocolBuf, &protocolLen);

    if(0 == ret)
    {
        GIZWITS_LOG("Get One Packet!\n");
        
#ifdef PROTOCOL_DEBUG
        GIZWITS_LOG("WiFi2MCU[%4d:%4d]: ", gizGetTimerCount(), protocolLen);
        for(i=0; i<protocolLen;i++)
        {
            GIZWITS_LOG("%02x ", gizwitsProtocol.protocolBuf[i]);
        }
        GIZWITS_LOG("\n");
#endif

        recvHead = (protocolHead_t *)gizwitsProtocol.protocolBuf;
        switch (recvHead->cmd)
        {
            case CMD_GET_DEVICE_INTO:
                gizProtocolGetDeviceInfo(recvHead);
                break;
            case CMD_ISSUED_P0:
                GIZWITS_LOG("flag %x %x \n", recvHead->flags[0], recvHead->flags[1]);
                //offset = 1;
               
                if(0 == gizProtocolIssuedProcess(didPtr, gizwitsProtocol.protocolBuf+sizeof(protocolHead_t)+offset, protocolLen-(sizeof(protocolHead_t)+offset+1), ackData, &ackLen))
                {
                    gizProtocolIssuedDataAck(recvHead, ackData, ackLen,recvHead->flags[1]);
                    GIZWITS_LOG("AckData : \n");
                }
                break;
            case CMD_HEARTBEAT:
                gizProtocolCommonAck(recvHead);
                break;
            case CMD_WIFISTATUS:
                gizProtocolCommonAck(recvHead);
            gizProtocolModuleStatus((protocolWifiStatus_t *)recvHead);
            break;
        case ACK_REPORT_P0:
        case ACK_WIFI_CONFIG:
        case ACK_SET_DEFAULT:
        case ACK_NINABLE_MODE:
        case ACK_REBOOT_MODULE:
            gizProtocolWaitAckCheck(recvHead);
            break;
        case CMD_MCU_REBOOT:
            gizProtocolCommonAck(recvHead);
            GIZWITS_LOG("report:MCU reboot!\n");
            
            gizProtocolReboot();
            break;
        case CMD_ERROR_PACKAGE:
            break;
        case ACK_PRODUCTION_TEST:
            gizProtocolWaitAckCheck(recvHead);
            GIZWITS_LOG("Ack PRODUCTION_MODE success \n");
            break;           
        case ACK_GET_NTP:
            gizProtocolWaitAckCheck(recvHead);
            gizProtocolNTP(recvHead);
            GIZWITS_LOG("Ack GET_UTT success \n");
            break; 
        case ACK_ASK_MODULE_INFO:
            gizProtocolWaitAckCheck(recvHead);
            gizProtocolModuleInfoHandle(recvHead);
            GIZWITS_LOG("Ack GET_Module success \n");
        break;

        default:
            gizProtocolErrorCmd(recvHead,ERROR_CMD);
            GIZWITS_LOG("ERR: cmd code error!\n");
            break;
    }
}
else if(-2 == ret)
{
    //Check failed, report exception
    recvHead = (protocolHead_t *)gizwitsProtocol.protocolBuf;
    gizProtocolErrorCmd(recvHead,ERROR_ACK_SUM);
    GIZWITS_LOG("ERR: check sum error!\n");
    return -2;
}

switch(gizwitsProtocol.issuedFlag)
{
    case ACTION_CONTROL_TYPE:
        gizwitsProtocol.issuedFlag = STATELESS_TYPE;
        gizwitsEventProcess(&gizwitsProtocol.issuedProcessEvent, (uint8_t *)&gizwitsProtocol.gizCurrentDataPoint, sizeof(dataPoint_t));
        memset((uint8_t *)&gizwitsProtocol.issuedProcessEvent,0x0,sizeof(gizwitsProtocol.issuedProcessEvent));  
        break;
    case WIFI_STATUS_TYPE:
        gizwitsProtocol.issuedFlag = STATELESS_TYPE;
        gizwitsEventProcess(&gizwitsProtocol.wifiStatusEvent, (uint8_t *)&gizwitsProtocol.wifiStatusData, sizeof(moduleStatusInfo_t));
        memset((uint8_t *)&gizwitsProtocol.wifiStatusEvent,0x0,sizeof(gizwitsProtocol.wifiStatusEvent));
        break;
    case ACTION_W2D_TRANSPARENT_TYPE:
        gizwitsProtocol.issuedFlag = STATELESS_TYPE;
        gizwitsEventProcess(&gizwitsProtocol.issuedProcessEvent, (uint8_t *)gizwitsProtocol.transparentBuff, gizwitsProtocol.transparentLen);
        break;
    case GET_NTP_TYPE:
        gizwitsProtocol.issuedFlag = STATELESS_TYPE;
        gizwitsEventProcess(&gizwitsProtocol.NTPEvent, (uint8_t *)&gizwitsProtocol.TimeNTP, sizeof(protocolTime_t));
        memset((uint8_t *)&gizwitsProtocol.NTPEvent,0x0,sizeof(gizwitsProtocol.NTPEvent));
        break;
    case GET_MODULEINFO_TYPE:
        gizwitsProtocol.issuedFlag = STATELESS_TYPE;
        gizwitsEventProcess(&gizwitsProtocol.moduleInfoEvent, (uint8_t *)&gizwitsProtocol.wifiModuleNews, sizeof(moduleInfo_t));
        memset((uint8_t *)&gizwitsProtocol.moduleInfoEvent,0x0,sizeof(moduleInfo_t));
        break;
    default:
        break;      
}

gizDevReportPolicy(currentData);

return 0;

}
这个函数有点长,大部分功能都包含在里面了,一步一步来

1.在环形缓冲区取出数据包

  ret = gizProtocolGetOnePacket(&pRb, gizwitsProtocol.protocolBuf, &protocolLen);

这个函数是取包含数,在环形缓冲区pRb取出一个协议包放进gizwitsProtocol.protocolBuf,取得的数据包长度保存在protocolLen

static int8_t gizProtocolGetOnePacket(rb_t *rb, uint8_t *gizdata, uint16_t *len)
{
    int32_t ret = 0;
    uint8_t sum = 0;
    int32_t i = 0;
    uint8_t tmpData;
    uint8_t tmpLen = 0;
    uint16_t tmpCount = 0;
    static uint8_t protocolFlag = 0;
    static uint16_t protocolCount = 0;
    static uint8_t lastData = 0;
    static uint8_t debugCount = 0;
    uint8_t *protocolBuff = gizdata;
    protocolHead_t *head = NULL;

    if((NULL == rb) || (NULL == gizdata) ||(NULL == len))
    {
        GIZWITS_LOG("gizProtocolGetOnePacket Error , Illegal Param\n");
        return -1;
    }

    tmpLen = rbCanRead(rb);
    if(0 == tmpLen)
    {
        return -1;
    }

    for(i=0; i<tmpLen; i++)
    {
    ret = rbRead(rb, &tmpData, 1);
    if(0 != ret)
    {
        if((0xFF == lastData) && (0xFF == tmpData))
        {
            if(0 == protocolFlag)
            {
                protocolBuff[0] = 0xFF;
                protocolBuff[1] = 0xFF;
                protocolCount = 2;
                protocolFlag = 1;
            }
            else
            {
                if((protocolCount > 4) && (protocolCount != tmpCount))
                {
                    protocolBuff[0] = 0xFF;
                    protocolBuff[1] = 0xFF;
                    protocolCount = 2;
                }
            }
        }
        else if((0xFF == lastData) && (0x55 == tmpData))
        {
        }
        else
        {
            if(1 == protocolFlag)
            {
                protocolBuff[protocolCount] = tmpData;
                protocolCount++;

                if(protocolCount > 4)
                {
                    head = (protocolHead_t *)protocolBuff;
                    tmpCount = exchangeBytes(head->len)+4;
                    if(protocolCount == tmpCount)
                    {
                        break;
                    }
                }
            }
        }

        lastData = tmpData;
        debugCount++;
    }
}

if((protocolCount > 4) && (protocolCount == tmpCount))
{
    sum = gizProtocolSum(protocolBuff, protocolCount);

    if(protocolBuff[protocolCount-1] == sum)
    {
        memcpy(gizdata, protocolBuff, tmpCount);
        *len = tmpCount;
        protocolFlag = 0;

        protocolCount = 0;
        debugCount = 0;
        lastData = 0;

        return 0;
    }
    else
    {
        return -2;
    }
}

    return 1;
}

写的有点长了,下篇再发吧。

展开阅读全文

没有更多推荐了,返回首页