WM_CH 发表于 2016-4-13 02:40:06

微信宠物屋v2.3源码分析+RingBuffer源代码分析

这是我在ickey论坛发布的帖子。板子在ickey申请到的。。。
就不再重新整理编辑了,直接给大家带注释的源码和文档。

有兴趣的可以去icKey找132654这个用户的帖子:)






如果说之前发的帖子都是水贴,这一炮,必须是干货!!额、大神可能又是看30分钟就能看懂,我是足足看了3个下午。。。个人笨
宠物屋的源代码这里分析的是STM32底板的V2.3版本。可以在本帖直接下载附件。硬件原理图什么的,就请大家去自行搜索下载吧。下载的时候注意看自己板子的版本号,下载对应的原理图。
像代码中那些HAL库,我就不分析了,硬件不一样的HAL库中的函数需要改几个引脚号。有兴趣的可以自己写这些HAL,等自己添加了外设的时候,要写自己的外设的HAL添加到工程中。这里主要分析让人头疼的Protocol。顺便分析程序的运行流程。
环形缓冲区的代码RingBuffer已经分析过了,见跟帖


擦,我发现在这里根本没办法一一细说。。。算了我发几个图给大家意淫一下,具体的看代码吧
这个程序还是有几个小bug的,而且操作都是用的全局变量、全局数据结构体。加之一些memcpy、memcmp等函数,各种报文的结构体,把大伙吓到了。【也许只是把我这种菜鸡吓到了。。】试着捋一捋就能捋顺了。大家多用"Ctrl+F中的MarkAll",和"在整个工程中搜索"来看代码。那只笔写写画画。


推荐大家先搞清楚两个全局变量的意思,他们分别作为标志位:
//WiFi控制设备命令,已经下达的标志

重发机制结构体
    uint32_t      SendTime;      //重发时记录的时间戳
    uint8_t                        SendNum;       //重发次数
    uint8_t                        Flag;          //1、作为需要等待WiFi应答的标志!!!!!
                                             //2、这个标志位也限制MCU上报数据!!!!!
                                             //   只要此标志置位,暂停上报
                                             //   复位标志,则重新允许上报
    uint16_t      ResendBufLen;//长度
    uint8_t                        Cmd_Buff;   //重发数据缓冲区

Pro_Wait_AckTypeDef         Wait_AckStruct;


这些个搞清楚了,再看代码就不乱了。



再来说说协议:
MCU回复WiFi模组要用的 通用协议帧:</span>


大致的流畅:1、按键处理2、串口信息处理3、如果接收到WiFi模组控制MCU的命令,则更新MCU状态,并立即上报MCU状态4、每隔1s,采集一次MCU状态

具体来说明第2条,串口信息处理:u8 GizWits_MessageHandle(u8 * Message_Buf, u8 Length_buf)这里如果收到的是4.10,WiFi控制MCU的命令,则把命令的数据内容传给Message_Buf进来之后,抓取一包数据。当WiFi的应答非法,或没收到WiFi应答时,启动重发机制。但这里是Bug。具体见代码抓取数据包成功,判断校验位,校验失败直接扔掉数据帧。判断收到WiFi模组的Ack信息,是不是正确的Ack下面是重头戏了,根据收到的命令码进行对应的操作:其他的略过,只说接收到4.8和4.10时的情况。//4.8WiFi读取MCU. Cmd=0x03
//4.10 WiFi控制MCU. Cmd=0x03
      case Pro_W2D_P0_Cmd:   //就是这里
      {
            switch(UART_HandleStruct.Message_Buf)   //标准报头后紧跟一个action(1B)
            {
                  //4.10 WiFi控制MCU. Cmd=0x03 ActionBit=0x01
                  case P0_W2D_Control_Devce_Action:
                  {
                        Pro_W2D_CommonCmdHandle();//回复通用协议帧
                        //储存ActionBit之后的信息到Message_Buf,最终传给WriteTypeDef来更改MCU设备状态
                        memcpy(Message_Buf, UART_HandleStruct.Message_Buf+sizeof(Pro_HeadPartP0CmdTypeDef), Length_buf);
                        p0Flag = 1;   //main()里,依靠此标志,和WriteTypeDef来控制更改设备状态
                        break;
                        }
                        //4.8 WiFi读取MCU. Cmd=0x03 ActionBit=0x02
                        case P0_W2D_ReadDevStatus_Action:
                        Pro_W2D_ReadDevStatusHandle();
                        break;
                        default:
                            break;
            }
    }
      break;


废话不多说,上几张图,大家看代码吧。




艾玛,忘了说最最重要的事。不想了解以上代码的同学,可以直接忽略什么上边。下面告诉大家在写自己的智能硬件时,需要在源代码基础之上更改的地方。分别是Gokit.h中ReadTypeDef 结构WriteTypeDef 结构Main.c中GatherSensorData 函数ControlDeviceHandle 函数对了,别忘了修改代码的ProductKey,在Protocol.h中。每个产品都有属于自己唯一的ProductKey。

其他的都不用动,可复用性还是超高的!但是可读性太差、还有Bug作为开源的代码。。。咳咳、跑题了。



下面是我的智能灯炮弄好了的情况:



再再最后,本次发帖遇到了很多次发帖失败。浏览器地址栏显示http://bbs.ickey.cn/index.php?app=group&ac=topic&id=Ickey%20notice:Illegal%20operation!我找到这个问题了:只要别在你写的文本中或者插入的代码中出现如下图所示的东东即可!

要是有人说,不是这个问题,只能说明你还没碰到提交之后浏览器卡住不刷新的情况。等你碰到了,试试无妨:)

WM_CH 发表于 2016-7-25 15:41:10

【机智云Gokit开发套件试用体验】+ RingBuffer源代码分析

大家都知道,环形缓冲区是比较常用的数据结构,正好机智云“微信宠物屋源代码v2.3”中也用到了。下面给大家分析一下。
首先是数据结构:“RingBuffer.h”注意是head指向了读区域,tail指向了写区域!注意是head指向了读区域,tail指向了写区域!注意是head指向了读区域,tail指向了写区域!
typedef struct {
size_t rb_capacity; //缓冲区容量
char *rb_head; //用于读出的指针
char *rb_tail; //用于写入的指针
char rb_buff; //缓冲区实体
}RingBuffer;
下面分析他的几个函数:“RingBuffer.c”
//用来比较最小值的宏
#define min(a, b) (a)<(b)?(a)b)

//新建RingBuffer,给成员赋值
//MAX_RINGBUFFER_LEN 这个宏,被定义为"P0数据最大长度"的2倍
//head/tail 两个指针,都指向缓冲区实体(数组rb_buff)的首地址
void rb_new(RingBuffer* rb)
{
rb->rb_capacity = MAX_RINGBUFFER_LEN; //capacity;
rb->rb_head = rb->rb_buff;
rb->rb_tail = rb->rb_buff;
};
获得缓冲区总容量Capacity:size_t rb_capacity(RingBuffer *rb)
{
return rb->rb_capacity;
}
获得缓冲区可读区域,返回可读区域大小:
三种情况:1、head与tail都指向同一个地方时,可读区域大小为0【这种情况只会在缓冲区还未使用时出现,开始使用之后,不会出现head/tail重合的现象,即tail永远不会等于head,否则head指向的数据还未读走就被覆盖了!】2、head < tail,说明tail没有写到缓冲区末尾,从缓冲区开头重新开始。可读的区域自然为(tail - head)3、head > tail,说明tail已经从缓冲区末尾写完,并从开头处重新准备写了。插入图片给大家看看:rb_buff是数组名,因此可以作为缓冲实体首地址的指针。http://s1.ickimg.com/bbs/plugins/pubs/kindeditor/attached/image/20160409/20160409204351_43256.jpg
size_t   rb_can_read(RingBuffer *rb){    if (rb->rb_head == rb->rb_tail) return 0;    if (rb->rb_head < rb->rb_tail) return rb->rb_tail - rb->rb_head;    return rb_capacity(rb) - (rb->rb_head - rb->rb_tail);}

获得可写区域大小,就可以用总容量 减去 可读区域大小来计算了:
size_t   rb_can_write(RingBuffer *rb){    return rb_capacity(rb) - rb_can_read(rb);}

读数据,从head指向的地址开始,读到data指向的地址处,读count个数据。返回读的个数三种情况:1、head < tail,此时要从count 和"可读区域大小"中选一个较小的值,作为读操作的次数。避免了count 大于“可读区域”的错误。2、head > tail且 count 的个数 小于“从head到缓冲区末尾的数据个数”图中蓝色。直接复制内存,再修改head 指针即可。3、head > tail且 count 的个数 大于“从head到缓冲区末尾的数据个数”。此时,先把从head到缓冲区末尾的值蓝色复制到data处,再把剩余的绿色复制过去。注意两个值:copy_sz 和*(data + copy_sz)如图http://s1.ickimg.com/bbs/plugins/pubs/kindeditor/attached/image/20160409/20160409211112_69909.jpg这种情况下,问题来了,要是绿色的区域超过了tail 怎么办?:)所以,应该加了一个判断,这个在写操作中做了,但这里没做。即要读的个数count 要小于可读区域的大小。不然会出现head > tail 但head 指向的数据以及head 后边的数据又不是有效数据,这个问题。代码:
size_t rb_can_read(RingBuffer *rb)
{
if (rb->rb_head == rb->rb_tail) return 0;
if (rb->rb_head < rb->rb_tail) return rb->rb_tail - rb->rb_head;
return rb_capacity(rb) - (rb->rb_head - rb->rb_tail);
}


获得可写区域大小,就可以用总容量 减去 可读区域大小来计算了:

size_t rb_can_write(RingBuffer *rb)
{
return rb_capacity(rb) - rb_can_read(rb);
}



写数据,把数据从data指向的地址,写到tail 指向的地址,写count个。返回写的个数。这里进来直接判断,要写入的内容大小 要小于可写区域大小,防止造成数据覆盖。写入合法。下面写入分了三种情况:1、2 需要计算tail_avail_sz,这个值为tail 到缓冲区末尾的数据区域大小。1、head < tail,count < tail_avail_sz。直接复制内容。假如tail 到了缓冲区末尾,让tail 回到缓冲区首地址。2、head < tail,count > tail_avail_sz。先写入 tail_avail_sz 个数据,tail 回到缓冲区首地址,再写入剩余的部分。3、head > tail,这种情况最简单,由于已经做了写入合法判断,所以直接复制内容,修改tail 即可。代码:
size_t rb_read(RingBuffer *rb, void *data, size_t count)
{
if (rb->rb_head < rb->rb_tail)
{
int copy_sz = min(count, rb_can_read(rb));
memcpy(data, rb->rb_head, copy_sz);
rb->rb_head += copy_sz;
return copy_sz;
}
else
{
if (count < rb_capacity(rb)-(rb->rb_head - rb->rb_buff))
{
int copy_sz = count;
memcpy(data, rb->rb_head, copy_sz);
rb->rb_head += copy_sz;
return copy_sz;
}
else
{
int copy_sz = rb_capacity(rb) - (rb->rb_head - rb->rb_buff);
memcpy(data, rb->rb_head, copy_sz);
rb->rb_head = rb->rb_buff;
copy_sz += rb_read(rb, (char*)data+copy_sz, count-copy_sz);
return copy_sz;
}
}
}


对于源程序中的,指针不为NULL判断,其实是必须要加上的,不知道为什么,我下载的代码,这些部分都被注释掉了。


另附我在编辑中遇到的“不合理”的问题:(吐槽)1、不知道什么情况下,编辑的时候,进入了“修改”模式,就是不能在随意的插入内容,这个怎么切换回Insert模式?2、修改字号、字体大小时,编辑框乱跳。Ctrl + V 粘贴时,编辑框乱跳。拖拉编辑框改变编辑框大小时,编辑框乱跳。这些乱跳现象虽然不影响编辑,但是着实让人难受。3、代码区域编辑时不可见,不知道我是在代码框编辑,还是在文本框中编辑。看我帖子后边的几个空的代码框,就是当时插入了代码,后敲了回车想输入文本时出现的。想删也找不到地方。搞的只能编辑的时候,先写几个字,站住位置,告诉自己这一行是文本。。。再放心的插入代码。。。4、图片大小不能调整,这个貌似有点苛刻了。但是论坛的水印太大,容易影响阅读。我插入图片时,都是留出来余量编辑的图片。5、代码框,虽然可以选择不同的语种,比如C、Java,然而没有区别。。。我还以为会有语法高亮呢、6、英文、数字字符与汉字之间间隔太小,我有敲一个或两个空格。看着舒服些。。。

jipin 发表于 2016-7-25 22:40:04

让管理员送一板,必须的。

zhangke0504 发表于 2016-5-11 13:47:44

看看支持就

飞翔中的语言 发表于 2016-5-19 19:12:33

请问你可以实现两个led灯的控制吗?第二个估计不简单

crossok 发表于 2016-6-12 09:07:56

mark下,感谢楼主分享

钱昊 发表于 2016-6-26 10:30:46

论坛越来越火啦

k7arm 发表于 2016-7-2 17:26:51

多谢楼主分享啊

molo 发表于 2016-7-5 08:53:56

学习一下,多谢分享。

月下 发表于 2016-7-18 17:18:59

支持~~~~~~~

jipin 发表于 2016-7-20 23:31:18

3.0的源码发布了,楼主加注释啊。给大家科普一下。

聪聪聪 发表于 2016-7-28 13:45:39

楼主是不是要用机智云的软件,那个mcu程序是不是得植入机智云的通讯协议

WM_CH 发表于 2016-8-4 20:43:51

聪聪聪 发表于 2016-7-28 13:45
楼主是不是要用机智云的软件,那个mcu程序是不是得植入机智云的通讯协议

不晓得你说的是啥啊:D

admin 发表于 2016-8-4 21:57:06

赞赞赞楼主 参加开源大赛吗》???

林光光1号 发表于 2016-8-7 01:17:20

很详细,赞一个:lol

淡忘瞬间 发表于 2016-10-2 13:41:44

WM_CH 发表于 2016-7-25 15:41
【机智云Gokit开发套件试用体验】+ RingBuffer源代码分析

大家都知道,环形缓冲区是比较常用的数据结构, ...

厉害 佩服

maomaodemao 发表于 2016-12-9 00:26:03

向您学习

初进茅庐 发表于 2017-4-26 09:54:18

楼主,这个版本的协议是不是不适合乐鑫的板子?
页: [1]
查看完整版本: 微信宠物屋v2.3源码分析+RingBuffer源代码分析