亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Manufacturing Technology >> 制造工艺

XMOS startKIT:构建 XMOS 和 Raspberry Pi 机器人 XMP-1

简介

Farnell(或 Newark)的 XMOS startKIT 是一种成本非常低(12 英镑,包括增值税)的处理器平台,可与 Raspberry Pi 配合使用。可以一起构建几乎不需要焊接的机器人应用程序。

XMOS startKIT 是一个接近信用卡大小的电路板,上面有一个 XMOS 芯片,带有多个可以用 C 编程的“XMOS 内核”。XMOS 技术允许事物以低抖动高速并行运行。这些正是机器人应用的理想特性。

连同一些在 XMOS startKIT 板和树莓派 (RPI) 上运行的代码,这些板被用来构建一个简单的紧凑型移动平台(我将称它为 XMP,XMOS 移动平台,而不是从现在开始的机器人本着 XMOS 精神,一切都以“X”开头)。

尽管 XMP-1 在拥有一些传感器和更多程序之前算不上机器人,但它可以在未来扩展用于机器人实验。 XMP-1 使用低成本的现成标准硬件,除了螺丝刀、剪线钳和钳子外,没有任何特殊工具。

这篇文章介绍了使用串行外围接口 (SPI) 接口在 RPI 和 XMOS 板之间进行通信,以及如何构建 XMP-1 并通过 Web 浏览器对其进行控制。

此处的视频显示 XMP-1 正在学习路线;第一次尝试使用它!它将受益于更好的用户界面。 XMP-1 可以移动得非常快,但我在这里以低速轻松移动。右侧是浏览器控件,底部是控制台输出,只是生成一些 keep-alive 和状态消息以查看发生了什么。

下面的下一个视频显示了 XMP-1 试图回放路线并在途中造成痛苦和痛苦。我的低成本连续旋转伺服系统(用于驱动车轮)不太好,而且 XMP-1 还没有传感器。

更多细节

这篇文章实际上是一些 XMOS startKIT 实验的第 2 部分。第 1 部分包含 XMOS 介绍、术语、体系结构和带有示例程序的快速入门指南。如果您对这项技术感兴趣,那么先阅读第 1 部分可能会有所帮助,这样第 2 部分会更有意义。第 2 部分旨在为 Raspberry Pi 和 XMOS startKIT 板之间的高速通信构建一个简单的框架。该框架应该足够通用,能够用于许多项目(机器人不是我的本意)。 XMP-1 实际上只是一个副产品,用于测试 Raspberry Pi 到 XMOS 板的通信。记录在此,以备不时之需。 (注意,还有一个名为 XMOS startKIT:XMOS 和 Raspberry Pi Oscilloscope XAE 1000 的第 3 部分,它重用了本文讨论的 SPI 功能,并介绍了如何使用 XMOS 芯片中存在的模数转换器 (ADC) ,以及如何在 Web 浏览器中执行实时图形)。

如果您只对构建和使用 XMP-1 感兴趣,那么您可以直接获取文章底部的代码,编译并存储到 XMOS startKIT 板和 Raspberry Pi 上的 Flash(如第 1 部分所述),然后只需遵循描述 XMP-1 硬件组装的部分,跳过此处的所有其他内容。如果您对使用 Raspberry Pi 和 Web 浏览器控制任何硬件感兴趣,那么这里的一些代码可以重复使用。但是,为了充分利用 Raspberry Pi 和 XMOS startKIT 组合,如果您不熟悉 startKIT,这里的其余信息可能会很有用。

解决方案概述 - 硬件和软件

这是一张完整的 XMP-1 正在充电的照片。对于户外使用,我使用了 802.11 热点类型设备(MiFi),在手机上运行浏览器。

下图显示了从 XMP-1 背面看去的部件的大致布局。您可以看到它非常基础——XMP-1 只是一个快速实验。

Raspberry Pi (RPI) 用于处理所有网络活动。它运行一个小型 Web 服务器,大部分代码是在 Node.js 平台上用 JavaScript 编写的。 RPI 通过串行接口 (SPI) 将电机控制速度(实际上连续旋转伺服系统用于 XMP-1)与 XMOS startKIT 板通信。 XMOS startKIT 负责向电机提供脉宽调制 (PWM) 信号。

RPI 使用 802.11 WiFi USB 适配器连接到网络。

此处显示了完整的接线图。硬件和结构在后面描述。

下图显示了将在 RPI 和 startKIT 上实施的软件。它看起来很多,但实际上不是,可以分解成小部分,下面将进一步描述。如前所述,整个源代码都在这篇文章的底部,因此如果需要,可以不加任何修改地使用它。

简而言之,绿色块处理网络交互并根据用户输入确定电机的速度和方向。绿色块向用户提供包含用户界面的网页 (index.html)。 xmos_servo 程序是一小段用 C 编写的软件,可将所需的速度/方向转换为发送到 startKIT 的串行外设接口字节数据。 startKIT 软件分为三个部分,在不同的 XMOS 内核上同时运行。 spi_process 将 SPI 信号转换为存储在数组中的数据。 data_handler 代码检查数组以决定要做什么(它今天得出的唯一结论是操纵伺服系统)。 servo_handler 进程向舵机输出脉冲流,以便它们可以以所需的速度旋转。下面将更详细地解释所有这些块。

串行外设接口 (SPI)

SPI 依赖于称为 SS、SCLK、MISO 和 MOSI 的四根线,以及为参与通信的两个设备分配主从设备。在RPI和XMOS板的情况下,RPI是主设备,负责产生时钟信号。 RPI 在 MOSI 线上传输数据,并在 MISO 线上接收数据。这意味着 SPI 接口可以同时以双向方式传输数据。实际上,如果需要单向数据,则可以根据感兴趣的方向忽略 MOSI 或 MISO 信号。

此处的示波器屏幕截图(来自泰克 MSO2024B 示波器的单个信号和自动 SPI 解码)显示了使用 Raspberry Pi 的 SPI 通信示例。 SPI 可以通过几种方式进行配置;你可以在这个例子中看到三个字节的数据从主 (RPI) 传输到从 (XMOS 板),它们是 0x02、0x00 和 0x10,并且没有数据或 0x00、0x00、0x00 从从机同时为主机。

SS 线是片选信号(低电平有效)。 RPI 的 26 路连接器上有两个引脚,可用于 SS;它们在下图中以蓝色圆圈显示,标记为 CE0 和 CE1。这意味着如果需要,RPI 可以与两个 SPI 从设备通信。在这种情况下,只使用了一个 CE 引脚 - 我选择了 CE1。

控制 Hobby 伺服电机

Hobby 伺服电机根据输入信号产生运动。它们通常旋转不到一整圈。通常,业余爱好伺服会在大约 180 度的范围内旋转。输出轴可以连接到(比如说)连杆,根据输入信号使车轮完全向左或完全向右(或介于两者之间)转。

下图显示了典型的业余爱好伺服器的内部结构(取自该站点)。左侧(蓝色)是传统的直流电机。它的齿轮减速很多,可以在右侧看到最终轴连接到蓝色臂,例如可以连接到车轮转向机构。最终轴的下方将是一个电位计,可提供有关最终轴准确位置的反馈。因此,爱好伺服是一个闭环系统,如果手臂意外地从所需位置被撞开,它可以自我纠正。

业余舵机通常具有三个连接; 0V、5V 和信号。信号线是伺服的数字输入,它需要一个 PWM 信号。脉冲宽度的大小决定了轴将移动到的角度。 PWM 信号需要每 20 毫秒重复一次,1.5 毫秒的脉冲宽度将导致轴移动到中心位置。 1msec 的宽度会在一个方向上完全移动舵机,而 2 msec 的宽度会在另一个方向上完全移动舵机(再往下会有一些伺服控制的示波器痕迹)。

有一种改进的伺服系统,称为“连续旋转”伺服系统。这是一个改进的伺服系统,电位计与任何终端挡块一起被移除,并且电路被说服认为伺服系统仍处于居中位置。以 1.5 毫秒以外的脉冲宽度发送 PWM 将使机构以取决于脉冲宽度的速度顺时针或逆时针旋转。 XMP-1 使用两个连续旋转的业余舵机,每个轮一个。它们不是获得受控运动的最佳方式(XMP-2 将使用直流有刷电机),因为它们的用途与业余舵机的初衷不同,但它们的优点是它们可以由数字控制逻辑信号,不需要任何外部H桥电路。

根据制造商的不同,Hobby 伺服线可以采用不同的颜色编码。通常中心线是红色的,它变成+5V。黑色或棕色线为 0V。白色或黄色线为PWM信号输入。

开始开发——连接电路板

为了开发软件,RPI 和 startKIT 使用带状电缆和 IDC 连接器组件连接 - 这些可以使用虎钳组装或购买现成的。对于自组装版本,值得购买额外的 IDC 连接器,用作电缆中心的调试连接器,以便在使用万用表或示波器探测信号时更轻松。

在 XMOS startKIT 上实现 SPI(spi_process)

第 1 部分介绍了使用 XMOS 开发环境 (xTIMEcomposer)。下面的屏幕截图显示了 xTIMEcomposer 的 Windows 版本,但 Linux 版本看起来相同(可能 Mac 版本也可能看起来相似)。

此时可以在xSOFTip lab中右键单击SPI Slave Function Library,将库导入到工作区中。我不是 xTIMEcomposer 的专家,所以我可能在这里用错了它,但是库的源代码和头文件出现在项目资源管理器的一个单独的文件夹中(如下蓝色圆圈所示):

这些文件需要位于 spi-test 文件夹中(以便它们显示为上面绿色圆圈所示),以便我手动将 spi_slave.h 和 spi_slave.xc 文件从 module_spi_slave/src 文件夹复制到 spi-test /src 文件夹使用 Windows 资源管理器。

该软件使用端口的概念来控制输出或读取输入。这些逻辑端口与芯片上引脚的物理映射之间存在映射。映射可以按某些组合进行更改(请参阅 XS1 端口简介 PDF 文档中的图 3)。

XMOS 设备上的输入/输出端口可以是 1、4、8、16 或 32 位宽。在设计零件时,您可能希望将某些功能分配给 1 位端口,或将其他功能分配给多位端口,因此图 3 将非常有助于确定使用哪些端口和引脚。

SPI 从机代码现在在 spi-test/src 文件中,此代码略有修改。库代码假设用于 SPI 接口的端口都是 1 位端口,而 Raspberry Pi SPI SS 引脚 (CE1) 连接到 XMOS 板上的 32 位端口。 startKIT 硬件手册 PDF 文档中的图 8 如下所示。在绿色的中心,您可以看到连接 XMOS 板和 Raspberry Pi 的 2×13 路接头。左边和右边的蓝色是芯片上的物理引脚名称(X0D0、X0D11 等)。引脚突出显示的值是逻辑端口号。 P1A、P1D 等是单比特端口。 P32A1是32位端口的第一位二进制数

SPI库改动不少,完整代码附在帖子中,这里只介绍部分代码,无需复制粘贴,完整代码附在文末可以使用。

XMOS 设备上的 SPI 接口按此处所示进行初始化。下面进一步解释。

+ 展开 sourceview 平原
  1. void spi_slave_init(spi_slave_interface &spi_if)
  2. {
  3. int clk_start;
  4. set_clock_on(spi_if.blk);
  5. configure_clock_src(spi_if.blk, spi_if.sclk);
  6. configure_in_port(spi_if.msi, spi_if.blk);
  7. configure_out_port(spi_if.miso, spi_if.blk, 0);
  8. start_clock(spi_if.blk);
  9. return;
  10. }

正如在第 1 部分的帖子中提到的,I/O 可以在精确的时间进出 XMOS 器件。在上面的代码中,set_clock_on 函数(在 XMOS xs1.h 头文件中定义)用于打开 XMOS 芯片中的内置时钟机制之一。下图(来自 XS1 端口简介文档)以黄色显示了这种机制。 configure_clock_src 函数用于选择外部时钟(在图中以蓝色显示)。它将连接到 Raspberry Pi 上的 SCLK 引脚。 configure_in_port 和 configure_out_port 函数用于将端口与时钟机制联系起来。 MOSI 和 MISO 信号(如下图绿色所示)均配置为与时钟机制相关联。

XMOS 设备上处理串行数据的方式非常巧妙。下面进一步解释这里的代码。首先,使用一个结构体来包含有关希望用作 SPI 接口的端口的详细信息。

+ 展开 sourceview 平原
  1. typedef struct spi_slave_interface
  2. {
  3. clock blk;
  4. 在 ss 端口;
  5. 在缓冲端口:8 msi;
  6. out buffered port:8 miso;
  7. 端口 sclk;
  8. } spi_slave_interface;

上面有趣的行是那些引用端口变量 mosi 和 miso 的行。它们已被声明为类型 port:8。如果变量被分配了 1 位端口地址,那么 XMOS 设备将自动将 1 线位流反序列化为 8 位值。

它使 SPI 代码的其余部分变得非常简单。这是管理从 Raspberry Pi 输入的 SPI 数据的代码:

+ 展开 sourceview 平原
  1. void spi_slave_in_buffer(spi_slave_interface &spi_if, unsigned char buffer[], int num_bytes)
  2. {
  3. unsigned int data;
  4. unsigned int vlen=0;
  5. clearbuf(spi_if.miso);
  6. clearbuf(spi_if.mosi);
  7. for (int i =0; i
  8. {
  9. spi_if.mosi :> data;
  10. data=data<<24;
  11. buffer[i]=bitrev(data);
  12. if (i==2)
  13. {
  14. vlen=(((unsigned int)buffer[1])<<8) | (unsigned int)buffer[2];
  15. if (vlen==0)
  16. 休息;
  17. }
  18. if (i>=vlen+2)
  19. {
  20. 休息;
  21. }
  22. }
  23. }

在上面的代码中,您可以看到有一个 for 循环,并且在循环内有一行 spi_if.mosi :> data;用于将MOSI线上的8位信息读入名为data的变量中。

接下来的两行用于翻转字节内的位,然后将数据存储在缓冲区数组中。

接下来的几行需要一些解释;它们与所需的协议有关。它旨在创建一些可用于许多用途的通用代码,而不仅仅是 XMP-1。如果树莓派向 XMOS startKIT 板发送数据,XMOS 板需要知道期望的数据字节数。这可以是硬编码的,但会很不灵活。

决定使用一个非常简单的“标签(或类型)、长度、值”(TLV)协议。 Raspberry Pi 必须传输的第一个字节是 0-255 范围内的标记或标识符(即一个字节)。由用户决定这些值代表什么。例如,值 1 可能表示“设置电机速度”,值 2 可能表示“设置大灯亮度强度”。后两个字节是一个 16 位值,指示要跟随多少个值(即数据)字节。我决定将其限制为 4kbyte(4096 字节),这应该可以满足许多用例,但可以通过调整代码中的 BUFLEN 定义来更改实际值。

因此SPI接口发送的字节数最小为3个(tag,长度为0x0000),最大为4099个tag,长度为0x1000(十六进制为4096)和4096个数据字节。

该协议略有改进,因此奇数标记号表示 Raspberry Pi 期望在当前 TLV 流完成后启动的后续 SPI 通信中得到响应,偶数标记号表示 Raspberry Pi 期望没有响应回来了。

这是一个非常基本的协议,但它应该满足许多通常的要求。下表中也有说明,其中蓝色数字是接收 4099 字节缓冲区的 SPI 字节索引。

回到前面的代码片段,可以看到接下来的几行在接收 SPI 数据时动态检查缓冲区 [1] 和缓冲区 [2] 的内容。内容应该是上图所示的长度(参见蓝色缓冲区索引)。一旦代码确定了余数长度,它就会接受那个数目的数据字节,然后程序退出。

这涵盖了 MOSI 线上 XMOS 板的 SPI 输入。 MISO 线上的 XMOS 设备的 SPI 输出以类似的方式运行,再次在运行中同时检查 MOSI 线上的长度,以便在传输请求的字节数后立即退出函数。

进程间通信

既然已经找到了 SPI 并且已经实现了一个协议来在任一方向上交换可变长度的数据,最长可达 4096 字节,那么对程序的主体进行了一些考虑。很明显,XMOS 内核将专用于处理 SPI 任务,但其余代码可能需要驻留在一个或多个额外的 XMO 分数中。

在第 1 部分中,描述了任务如何在不同的 XMOS 内核上并行运行,以及任务如何通过将值推送到通道来相互通信。内核之间还有另一种通信方式,它使用“通过接口进行交易”而不是通道的概念。它更加灵活,因为您可以将多个不同类型的变量从一个 XMOS 内核发送到另一个。事务类型的定义很像 C 函数原型。看一个例子,这一切就变得更清楚了。

例如,如果一个应用程序有一个控制显示器的任务,那么发送任务可能想要打开或关闭显示器,或者它可能想要绘制一个像素。两个 XMOS 内核之间的通信接口定义可能如下所示:

+ 展开 sourceview 平原
  1. interface program_display
  2. {
  3. void backlight(int state, int color); // 交易类型 1
  4. void plot(int x, int y, int color); // 交易类型 2
  5. };

接口通信是单向的,因此如果显示器想要发送信息,例如(比如)触摸屏状态,则需要在另一个方向上使用另一个接口。由此可见,接口有客户端和服务器端。此处的图表显示了两个 XMOS 内核(紫色)、两个接口(灰色),第一个接口(称为 program_display)允许通过 program_display 接口发生两种不同类型的事务(蓝色)。

使用接口和事务类型的好处在于,就像 C 函数原型一样,您可以有返回值,并且可以传递对变量的引用,这样即使通信始终由接口的客户端发起,数据传输可以双向发生。图中未显示的另一个非常有趣的功能是服务器端能够向客户端发送“通知”。这可以是客户端以通常方式发出事务的信号,以便检索一些数据。此功能将在 XMP-1 代码中使用。因此,将在下面进一步解释有关究竟如何对接口进行编码以及如何发送数据和通知的更多信息。

设计处理 SPI 内容的 IPC 架构

已经描述了 SPI 接口处理。现在,需要以有用的方式将 SPI 消息的内容呈现给任务,以便进行后续处理。有了关于接口和事务的知识,就可以开始为分离的 XMOS 内核分配功能,并设计进程间通信以获得通用框架,该框架允许将有用的消息内容从 RPI 发送到 XMOS板,反之亦然,并进行处理。

这里的图展示了开发的内容(和之前的图类似,只是现在有一个从上到下的时间顺序)。

当树莓派想要向 XMOS 板发送消息时,RPI 会将消息构建为前面描述的 TLV 格式。然后信息在 MOSI 信号线上输出(在上图的顶部以绿色显示)。同时,XMOS 设备需要发回一些信息,但由于还没有信息要发回,MISO 行可能包含垃圾或全零值,如粉红色所示。 spi_process 函数将把消息收集到一个缓冲区(一个无符号字符数组)中,然后它会向一个单独的 data_handler XMOS 内核发起一个事务。 data_handler 负责处理消息的内容,并有选择地将信息发送回 spi_process XMOS 内核,以便任何后续的 SPI 交换都可以将有用的数据而不是垃圾值发送回树莓派。

通过制作缓冲区的副本,可以在 spi_process 和 data_handler 之间发送数据。然而,也可以只传递一个指向缓冲存储器的指针。可以做到这一点的一种方法是将指针和缓冲区内存位置的控制从 spi_process “移动”到 data_handler。一旦 data_handler 完成了消息检查,它就可以使用可能在事务中使用的返回变量将控制权移回 spi_process。这就是为什么上图中有一个名为 array_data 的事务,其参数定义为可移动指针,返回值也定义为可移动指针。这样,任何时候只有一个 XMOS 内核可以访问缓冲存储器。

这些是使用的接口:

+ 展开 sourceview 平原
  1. 接口 to_rpi
  2. {
  3. void code(unsigned char c);
  4. };
  5. 接口 from_rpi
  6. {
  7. unsigned char* movable array_data(unsigned char* movable bufp);
  8. };

spi_handler 代码为缓冲区分配空间,然后使用此处代码中显示的行 buf=c.array_data(move(buf)) 将缓冲区的控制权传递给 data_handler 代码:

+ 展开 sourceview 平原
  1. 无效
  2. spi_process(interface to_rpi server s, interface from_rpi client c)
  3. {
  4. 无符号字符存储[4099];
  5. unsigned char* movable buf=storage;
  6. ...
  7. buf=c.array_data(move(buf));
  8. ...
  9. 选择
  10. {
  11. case s.code(unsigned char c):
  12. if (c==SEND)
  13. {
  14. spi_slave_out_buffer(spi_sif, buf, 4099);
  15. }
  16. 休息;
  17. }
  18. }

data_handler 代码获得缓冲区的控制权,然后如果需要在后续 SPI 事务中将任何响应发送到 RPI,缓冲区将填充响应。最后将缓冲区的控制权交还给 spi_handler 进程。

+ 展开 sourceview 平原
  1. 无效
  2. data_handler(interface to_rpi client c, interface from_rpi server s)
  3. {
  4. 选择
  5. {
  6. case s.array_data(unsigned char* movable vp) -> unsigned char* movable vq:
  7. // vq 包含来自 SPI 的数据。在这里,我们可以随心所欲地使用它。
  8. // 任何响应也是在这里构建的:
  9. vq[0]=0x22; // 标签
  10. vq[1]=0x00; // 长度
  11. vq[2]=0x00; // 长度
  12. vq=move(vp); // 将指针控制传回 spi_process
  13. tosend=1;
  14. 休息;
  15. }
  16. 如果(发送)
  17. {
  18. c.code(SEND); // send a code to spi_process so that it is aware there is data to send to RPI
  19.   }
  20. }

Earlier it was mentioned that if an odd tag value was sent by the RPI then this would be an indication that the RPI expected a response message from the XMOS startKIT board on the subsequent SPI exchange. This is implemented by both the spi_process and data_handler making a note that a return message is expected if the first byte received is an odd value. Once data_handler has finished constructing the return message in the buffer memory it moves the buffer pointer back to the spi_process XMOS core and also sends a code transaction which could contain a message such as “ready to send”. The spi_process XMOS core is now ready for any subsequent SPI exchange. If the data_process doesn’t want to send any message back to the Raspberry Pi (for example if the tag was even valued) then the code transaction is not sent (or a different code could be sent such as “not ready to send”).

In the graphic diagram earlier you can see that the subsequent SPI exchange did transmit data back to the Raspberry Pi on the MISO wire.

To summarize, the spi_process and data_process present a fairly general-purpose capability to exchange data bidirectionally between the RPI and XMOS board.

Implementing PWM (servo_handler) on the startKIT

To test out the general purpose architecture, it was decided to use it to control many devices. The devices ended up being hobby servos because they require very little electrical interfacing effort – no H-bridge or transistor driver is needed – and the servo input wire can be directly connected to an XMOS output pin. I didn’t have many servos, so although the code implements 8 servo control, only two were used for XMP-1.

The code could be modified to provide DC motor control too (with a suitable external H-bridge circuit).

It was decided to use a single XMOS core to handle the eight servos. The diagram below shows the total of three XMOS processes used in the solution. The new addition is the servo_handler task which is shown on the right. This task has an array that stores the current servo values. As soon as the task starts up, the values are initialized to a centered value (or standstill for a continuous rotation servo) and then every microsecond the task wakes up to check if the servo PWM signal needs adjustment.  If it does then the servo port output is toggled. After 20msec the process repeats.

For more detail:XMOS startKIT Building an XMOS and Raspberry Pi Robot XMP-1


制造工艺

  1. NodeMCU 和 Raspberry Pi 3 B+
  2. Python 和 Raspberry Pi 温度传感器
  3. Raspberry Pi 上的简易温度和湿度
  4. Raspberry Pi 温度和光传感器
  5. Raspberry Pi 传感器和执行器控制
  6. 带有 Raspberry Pi 和湿度传感器的 Aeroponics
  7. 新的 RASPBERRY PI 3 型号 B + 功能和购买
  8. 使用 Raspberry Pi 和 Bridge Shield 的机器人
  9. BeagleBone 和 Raspberry Pi 获得 FPGA 附加组件
  10. 构建 MonkMakes Raspberry Pi 机器人套件
  11. Raspberry Pi CD Box Robot
  12. 使用 Raspberry Pi 和 Python 构建机器人