客户端封装socketio(socket多个客户端)

菲律宾亚星国际登录 22 2

  2016-11-22 庆科MXCHIP

  基于MiCO的《物联网可编程逻辑控制器》的技术分享文章第二弹。如有问题可登陆MiCO社区(mico.io ),与作者(社区id:IoTMCU)进一步探讨交流。

  OK在今天章节里我们全面进入MiCO3.0时代,我们首先将eCLR运行内核移植到MiCO3.0系统里并让eCLR内核能够在MiCOKit-Nucleo开发板上运行起来并且能够在MULTIPROG中编写一段简单的逻辑代码使其下载到MiCOKit-Nucleo开发板上运行。在开始正式移植工作前,我们还是需要来了解下eCLR内核对于MCU的存储器要求。

客户端封装socketio(socket多个客户端)-第1张图片-亚星国际官网

  前面章节我们已经介绍到了,eCLR内核是由一些源代码还有核心库文件组成,如果需要运行起eCLR内核,那么我们至少需要下图所示存储器空间:

客户端封装socketio(socket多个客户端)-第1张图片-亚星国际官网

  上面图示中说明的eCLR占用存储器并不包含MiCO系统以及其他组件所占用的存储器空间,所以大家应该明白了在前面章节我提到的为啥128KB RAM与512KB FLASH空间并不太足够(大家可以看看默认的MiCO系统编译出来的map文件),下面我们分别介绍下eCLR所需要的这些存储空间的简单分析:

  1.关于eCLR内核静态库的空间占用大概需要10KB的RAM以及80KB的Flash,这也是把eclrlib_M4_VFP_MT_InPlace.a加入到IAR工程中编译看到实际map文件中的结果,这部分空间占用是固定不动的,IAR在链接过程中就会需要这么大的空间。

  2.eCLR Heap是eCLR内核内部的内存管理单元所需要的一个固定的存储器空间,一般情况下,在嵌入式系统上我们会使用eCLR的内存管理机制来进行申请释放RAM空间,对别是针对C++的类的对象申请与释放,我们都会重载下运算符new,delete,这样我们就可以直接通过new和delete来从eCLR内存管理单元里分配和释放内存了,所以这里的大小我们可以根据自己的需求动态来进行调整,16KB是建议的最小值,在MiCOKit-Nucleo开发板上,我们就使用最小值16KB,例如从下面图中我们可以看到这里我们重载了new和delete运算符。

  3.Data memory是我们在MULTIPROG中定义变量所存放的存储器区域。例如下面截图,我们在MULTIPROG定义了变量,当编译好的工程被下载到MiCOKit-Nucleo开发板上时,eCLR内核就会根据工程信息把这些定义在MULTIPROG里面的变量分配在Datamemory上,也就是说,Datamemory区域的大小决定了在MULTIPROG中到底能够定义多少变量,根据我们的经验,一般情况下我们针对简单逻辑控制的小型控制器可以给出8-12KB空间也是足够使用的,当然如果设备RAM足够大,建议还是给大点空间。

  4.Code memory是用来存储MULTIPROG编写的工程。也就是说,MULTIPROG中编写的逻辑代码最终经过编译后,会经过与eCLR内核通讯下载到MiCOKit-Nucleo开发板上的存储器区域里,可以是RAM也可以是FLASH,一般情况下我们要给出足够大小的Codememory,不然是写不了多少逻辑代码的,所以推荐的最小值我们也要保证128KB的存储器空间,很明显仅仅使用Cortex-M4单芯片内部的RAM存储MULTIPROG编译后的工程是不合适的,好在eCLR可以工程直接下载到内部FLASH上并且直接在FLASH上执行MULTIPROG编写的逻辑代码,我们在MiCOKit-Nucleo开发板上也是直接将逻辑代码直接下载到FLASH上执行,这样PLC逻辑执行速度还更快一点。(啰嗦一句,如果万一将来使用的下载逻辑代码到RAM中执行,那么是需要一个外部存储器来存储这个工程镜像,不然掉电了,运行的逻辑代码就会丢失了)下图是我们MULTIPROG编译默认示例工程的数据空间(Datamemory)与程序空间(Codememory)提示信息:

  5.最后是我们PLC任务所需要的堆栈空间了,MULTIPROG中可以定义多个任务,其实这个任务最终在MiCOKit-Nucleo开发板上也会由eCLR内核调用FreeRTOS接口来创建对应的OS任务负责执行PLC的程序,如下图所示这个示例工程中定义了两个周期任务T_100ms与DANCE_R:

  这里每个任务下面都插入了一个或者多个程序实例,这与我们在MiCO系统里创建一个任务并且制定一个入口函数并调用不同的函数是非常类似的概念。看到这里,各位小伙伴是不是觉得MULTIPROG编程相对C/C++很简洁呢,因为在这里我们只是做移植eCLR内核到MiCO3.0系统的工作,还没有涉及到MULTIPROG编程讲解,大家在后面会逐步了解到MULTIPROG相对于C/C++编程的核心优势。

  前面介绍基础概念的工作我们也做了半天了,或多或少也能够猜到eCLR内核移植到MiCO系统上需要做哪些工作:

客户端封装socketio(socket多个客户端)-第1张图片-亚星国际官网

  1.Thread, Event, Mutex接口实现

  Thread,Event,Mutex接口我们需要使用RTOS的API进行实现对应的功能,这样eCLR内核才能够正常去创建PLC工程中所配置的任务以及用于与MULTIPROG联机的通讯任务。这里我们拿出一个代码片段以便于理解eCLR内核通过Thread接口创建任务的过程:

  可以看到eCLR内核是通过调用CThreadImpl类来实现任务创建工作,而在这个接口里面又调用了FreeRTOS的任务接口创建PLC的任务。同样类似的实现还有:

  bool CThreadImpl::SetPriority(int prioThread)

  void CThreadImpl::Start()

  void CThreadImpl::Resume()

  void CThreadImpl::Suspend()

  void CThreadImpl::Sleep(int ms)

  CThreadImpl::ThreadId

CThreadImpl::GetThreadId()

  ……

  这些接口都需要使用FreeRTOS的接口来直接实现,由于MiCO封装的FreeRTOS并不能完全满足这些接口的需求,所以我们就只能直接使用原生的FreeRTOS接口了。

  Event接口主要是用于实现eCLR内部多任务之间的同步,所以我们就使用FreeRTOS的Semaphore实现了,例如通过vSemaphoreCreateBinary创建一个Semaphore,使用xSemaphoreTake来等待一个事件,使用xSemaphoreGive来产生一个事件。由于接口比较简单,主要实现的代码接口是:

  CEventImpl*

CEventImpl::Create(bool initialstate, bool isAutoReset);

  void CEventImpl::Release();

  bool CEventImpl::Set();

  bool CEventImpl::WaitOne();

  Mutex接口主要用于eCLR内部资源的互斥访问,也是对应于FreeRTOS的Mutex接口,使用xSemaphoreCreateMutex接口进行创建,使用xSemaphoreTake与xSemaphoreGive来进如互斥区以及离开互斥区,主要实现的接口是:

  CMutexImpl*CMutexImpl::Create();

  void CMutexImpl::Release();

  void CMutexImpl::Enter();

  void CMutexImpl::Leave();

  2.ImageFile接口实现

  ImageFile接口主要用于读写存储在STM32F411内部FLASH上的PLC工程,所以主要涉及到一些擦写Flash操作,需要特别指出的是,在针对STM32F4的实现里面,最开始我是采用STM32F4标准外设库的API进行擦写内部用于PLC工程的128KB的FLASH区域,在移植过程中想想还是做一个通用的解决方案比较好,所以我这边就借用了MiCO的分区方式,在platform.h里面添加了自己定义的分区:

客户端封装socketio(socket多个客户端)-第1张图片-亚星国际官网

  typedef enum

  {

MICO_PARTITION_FILESYS, MICO_PARTITION_ECLR, MICO_PARTITION_USER_MAX

  }

mico_user_partition_t;

  然后在platform.c的mico_partitions定义里增加eCLR的Partition定义:

  [MICO_PARTITION_ECLR] =

  {

  .partition_owner = MICO_FLASH_EMBEDDED,

  .partition_deion = "ECLR",

  .partition_start_addr = 0x08060000,

  .partition_length = 0x20000, //128k bytes

  .partition_options = PAR_OPT_READ_EN | PAR_OPT_WRITE_EN,

  },

  这样在实现ImageFile读写时就直接调用MiCO的API就可以了,以后我们如果要修改eCLR的PLC工程存储区域也非常方便,直接修改对应的mico_partitions即可。如果是更换其他Cortex-M3芯片也不在话下,因为MiCO已经把这部分API都做好了,我们就直接使用会更加方便。

  3. Filesystem接口实现

  eCLR文件系统接口支持主要是在MULTIPROG里面有文件操作的功能模块可供用户进行调用,所以在eCLR内核里需要适配对应的接口,因为MiCO已经提供了FatFS的移植,并且可以在mico_partitions中定义文件系统区的定义,例如这里我使用的是在外部的SPI FLASH上实现文件系统,那么就需要增加下面文件系统区的定义,1MB空间勉强可以用用,如果成本不是一个大问题的话,希望EMW3166可以有升级模块增加SPI FLASH的存储器空间,例如增加到4-8MB大小,对于物联网应用来说,本地存储其实也是很重要的,有足够大小的文件系统,可以保存更多的数据在本地进行分析而不仅仅是依赖云端:

  [MICO_PARTITION_FILESYS] =

  {

   .partition_owner = MICO_FLASH_SPI,

.partition_deion = "FILESYS", .partition_start_addr = 0x100000, .partition_length = 0x100000, //1M bytes .partition_options = PAR_OPT_READ_EN | PAR_OPT_WRITE_EN,

  }

  4.Clock接口实现

  Clock接口主要是eCLR需要知道当前的系统心跳TickCount,我们可以直接使用MiCO的API来实现即可:

  uint32

EclrEnvironment::GetTickCount() //resulotion is millisecond

客户端封装socketio(socket多个客户端)-第1张图片-亚星国际官网

  {

  return

mico_rtos_get_time();

  }

  uint32

EclrEnvironment::GetMicroTickCount() // resolution is microsecond

  {

  return mico_rtos_get_time()

* 1000;

  }

客户端封装socketio(socket多个客户端)-第1张图片-亚星国际官网

  5.eCLR Socket通讯接口实现

  eCLR Socket通讯接口并不完全是我们说的LwIP的socket接口用于TCP/IP的实现,而是更加通用的一种通信框架,当然我们可以使用LwIP的socket来实现eCLR Socket接口,这样我们就可以通过WIFI将PC上的MULTIPROG与MiCOKit-Nucleo开发板连接起来了。我们新添加的BsdSocket类,就把LwIP的socket接口重新封装了一层,然后注册到eCLR内核中:

  BsdSocket* pBsdSocket = new

BsdSocket();

  if (pBsdSocket != NULL)

  {

  if

(pBsdSocket->Open(BsdSocket::Stream, CRemotingDeamon::ClrServerPort)==true)

  {

  CRemotingDeamon::addSocket(pBsdSocket);

  }

  pBsdSocket->release();

  }

  封装代码太多,不方便贴出来,总之都是那些Open, Close, Send, Recv, Select等接口的二次封装,没有什么技术含量,大家简单理解下就可以了。

  6.系统初始化与调度实现

  最后就是我们eCLR内核初始化还有调度了。eCLR内核初始化大家应该好理解,就是调用eCLR的API把内核启动起来,eCLR内核还需要调度?这里的调度的概念大家需要先明白一点,MULTIPROG中可以定义多个任务,这些任务主要用到的是Cyclic以及Default类型,而这些任务之所以可以受eCLR内核控制来执行,都是需要有一个高优先级的FreeRTOS单独任务来进行整体调度,例如下面这张图片所示例的PLC任务在eCLR内核中执行的方式:

  我们需要一个单独的任务来进行eCLR内核的调度,调度实现也非常简单,创建一个任务来执行eclr_sched_task作为入口函数就可以了:

  void

eclr_sched_task(void* param)

  {

   portTickType xLastWakeTime;

xLastWakeTime = xTaskGetTickCount();

for( ;; )

{

vTaskDelayUntil( &xLastWakeTime, ( portTickType ) 1 );

ClrController::Process();

}

  }

  在Cortex-M4 100MHZ以上的MCU里,我们一般都把eCLR调度周期修改为1ms与FreeRTOS的心跳时间保持一致,这里不使用MiCO的mico_thread_msleep实现延时1ms也是有原因的,因为ClrController::Process();执行也会消耗少许CPU时间,如果使用mico_thread_msleep那么实际一个完整的调度加1ms延时,总时间会稍稍大于1ms,这样eCLR内核时钟就会有少许偏差,所以我们要保证ClrController::Process()严格1ms调用一次。

  在今天这个章节,我们的eCLR内核移植过程就结束了,希望对大家能够有所启发,因为eCLR与MULTIPROG都不是开源免费的软件,所以更多的大家是看看我这边进行一些总结和大体思路的讲解。在整个项目完结后我尽量向老板申请可以给出一个评估版的镜像文件以及MULTIPROG评估版开放给大家玩玩,虽然现在还不确定最后结果,但是我会尽量争取,今天就到这里,我们下期见。

  上海庆科:智能硬件背后的连接者

  上海庆科信息技术有限公司(MXCHIP)作为智能硬件背后的连接者,专注于为硬件厂商及互联网企业提供全方位完整物联网解决方案,核心产品及服务:嵌入式无线模块、物联网操作系统MiCO、移动应用(App)开发以及云端接入服务四个领域。

标签: 客户端封装socketio

发表评论 (已有2条评论)

评论列表

2025-08-15 04:36:34

readId()   ……  这些接口都需要使用FreeRTOS的接口来直接实现,由于MiCO封装的FreeRTOS并不能完全满足这些接口的需求,所以我们就只能直接使用原生的FreeRTOS接口了。  Event接口主要是用于实现eCLR内部多任务之间的同步,所以

2025-08-15 12:02:54

S并不能完全满足这些接口的需求,所以我们就只能直接使用原生的FreeRTOS接口了。  Event接口主要是用于实现eCLR内部多任务之间的同步,所以我们就使用FreeRTOS的Semaph