ZHCAAA2A April   2019  – May 2021 AM5706 , AM5708 , AM5716 , AM5718 , AM5726 , AM5728 , AM5729 , AM5746 , AM5748 , AM5749 , TIOL111 , TIOL1113 , TIOL1115 , TPS2660

 

  1.   商标
  2. 1引言
  3. 2实现
  4. 3测试结果
  5. 4备选器件建议
  6.   参考文献
  7. 5修订历史记录

实现

为了将 IO-Link 集成到 Linux,此处选择了第三个方案,因为它不会增加 Arm Cortex-A15 的 CPU 负载,并且计时不会因 Linux 上的 CPU 负载而发生干扰或改变。随后,该项目本身分为两个子项目,一个在 A15 内核上基于 Linux 运行,另一个作为 RTOS 项目在 Arm Cortex-M4 内核之一上运行。这两个子项目共享通用的命令定义,并通过处理器间通信 (IPC) 进行通信。

在 Linux 一端,编译到内核中的 Remoteproc 框架负责加载 Arm Cortex M4。必须将主内存配置为具有分割区,以使 Linux 不会接触到 Arm Cortex-M4 使用的内存,否则它们会相互干扰。此配置在 Linux dts 文件 am57xx-beagle-x15-common.dtsi 中完成,ipu2_cma_pool 部分针对第二个 IPU(这是 Arm Cortex-M4 子系统之一)进行此配置。

ipu2_cma_pool: ipu2_cma@95800000 {
compatible = "shared-dma-pool";
reg = <0x0 0x95800000 0x0 0x3800000>;
reusable;
status = "okay";
};

必须在 Arm Cortex-M4 项目的资源配置中完成相同的配置。此资源表会编译到项目中,并由 Remoteproc 驱动程序在启动时正确配置 MMU,从而向 IPU 提供所有必要的资源。

Arm Cortex-M4 在逻辑地址 0x0000 0000 处启动。该逻辑地址由 MMU 映射到分割区域中的相应物理 DDR 内存地址 (0x9580 0000)。Linux 驱动程序将二进制文件加载到该 DDR 位置,并且 Arm Cortex-M4 会因复位被释放。

Arm Cortex-M4 启动后,可在两个内核之间建立通信通道。这基本上就是一个客户端/服务器模型。Arm Cortex-M4 内核提供了一个服务器,可打开消息队列。在 Arm Cortex-A15 上的 Linux 中,可打开此消息队列,并传递数据。

Arm Cortex-M4 上的 RTOS 有两项任务,一项是处理 IO-Link 通信,另一项是等待从 Linux 接收到通信队列中的数据。一旦接收到数据,就可对数据进行处理并传递给 IO-Link 任务。

仅有少量数据被交换,因此所有内容均以 32 位字进行编码。Arm Cortex-M4 上的一个名为 void Server_handle_cmd(unsigned int *cmd) 的函数会处理从主机处理器接收到的命令(存储在 cmd 中)并将响应写入相同的位置。此后会发送回去。如果是发出读取 ISDU 请求,可能看起来像这样:

void Server_handle_cmd(unsigned int *cmd) {
switch ((*cmd) & App_CMD_MASK) {
...
case App_CMD_GET_ISDU_START: 
MOD_Read_req((*cmd & 0x00f00000)>>20, (*cmd & 0x0000ffff), (*cmd & 0x000f0000)>>16);
isdu_read_state = 1; 
break;
...
}
}

上面的代码片段会分析收到的命令,并将数据传递到堆栈。32 位宽命令的高 8 位对应用命令进行编码,此处的命令为 App_CMD_GET_ISDU_START,用于请求读取 ISDU。命令的其余位包括端口、索引以及子索引。通过调用 MOD_Read_req,可提取这些参数并将它们传递给 IO-Link 堆栈的读取函数。除了设置内部标志 isdu_read_state 之外,还可由其他应用程序命令将其用于传输读取请求的状态。此处的状态包括该请求是否已由堆栈处理以及数据是否已传递到 Linux 主机。

Linux 主机必须将所有命令编码为 32 位字并将它们传递到通信队列中。一个开始在指定端口上读取 ISDU 的函数如下所示:

void isdu_read_start(int port, int index, int subindex){
App_Msg *msg;
/* allocate message */
msg = (App_Msg *)MessageQ_alloc(Module.heapId, Module.msgSize);
/* fill in message payload */ 
msg->cmd = App_CMD_GET_ISDU_START | port<<20 | subindex<<16 | index;
/* send message */ 
MessageQ_put(Module.slaveQue, (MessageQ_Msg) msg);  
/* wait for return message */
MessageQ_get(Module.hostQue, (MessageQ_Msg *) &msg, MessageQ_FOREVER);
/* free memory */
MessageQ_free((MessageQ_Msg)msg);
}

MessageQ_get 返回后,来自 Arm Cortex-M4 的响应将存储在 msg 中,并接受进一步处理。例如,在此处执行读取命令后,可以存储已回读的值。

除了用于启动读取请求的函数之外,还实现了一个用于检查状态并回读缓冲区的函数。

启动读取请求后,主机处理器必须检查读取命令是否已完成,然后从读取缓冲区中获取数据。如下所示的例子中实现了一个完整的读取函数,用于读取字符串数据,例如产品名称。

void isdu_read_char(int port, int index, int subindex, char *buf, int *len){
isdu_read_start(port, index, subindex);
while(isdu_read_state() != READ_COMPLETED);
isdu_read_data(buf, len);
buf[*len] = '\0';
}

在此抽象级上,可在用户应用中使用诸如 get_port_state、isdu_read_char 和 isdu_write_char 之类的简单函数来从 Linux 用户空间控制 IO-Link。

此处的实现示例在机器视觉应用中使用这些函数。用户应用会利用 HALCON 库,将 GigE Vision 摄像机连接到 Sitara AM5728 处理器的以太网端口之一。同样的应用控制着一个塔灯以发出信号,指示是否正确识别了某个物体,并将结果通过以太网发送到服务器进行监控。