Engee 文档
Notebook

为 Arduino 生成代码(有限自动机上的交通灯)

在本示例中,我们将使用有限自动机库在Engee中开发一个模型,用于控制Arduino兼容板上的三段式交通灯。

简介

本示例的目的是根据时序图为三段式交通灯开发一个控制模型。控制算法将通过程序块Chart 实现,输入信号移除和输出信号生成将通过程序块C-Function 实现。

为了全面展示Engee **Engee自动机库的功能,包括随后的代码生成,交通灯控制算法将分为两种模式--工作模式(各部分交替开启)和值班模式(黄灯部分开启和关闭)。模式切换由目标设备输入的离散信号完成。

硬件部分

本演示使用 Amperka 的 Iskra Neo 控制器。受控电路由无线电元件组装而成,包括 LED、按钮触点、电阻器和连接线,安装在无焊面包板上,如下图所示。

plate_2.JPG

打开/关闭待机模式的输入信号通过按钮触点与 Arduino 电路板的数字触点 4 连接。用于打开交通灯部分的输出信号由数字触点 10 - 12 输出到相应颜色的 LED 上。为确保电路正确运行,还使用了限流电阻(330 欧姆)和下拉电阻(10 千欧)。电路由 Arduino 电路板本身供电。

时序图

要实现的算法将根据以下时序图重现交通灯的运行。

在运行模式周期开始时,交通灯的红色部分点亮 10 秒钟。在最后 3 秒钟内,黄色部分同时亮起。之后,绿色部分亮起 10 秒钟。之后,绿色部分闪烁 5 秒,开启和关闭各持续 1 秒。闪烁结束后,黄色部分点亮 3 秒钟。然后运行模式周期重新开始。

为清晰起见,所述示意图如下图所示。

worktimediagram_2.png

占空比由两种交通灯状态来描述:

  • 黄色部分打开 1 秒钟、
  • 所有部分关闭 1 秒钟。

型号说明

本演示的模型由五个区块组成,如下图所示。

image.png

如前所述,控制算法由Chart 模块再现。该程序块有一个输入端mode ,用于操作模式切换信号;两个输出端cntlight ,分别用于输出交通灯计数器信号和照明区段代码。控制器外围的初始化和控制由C Function 块完成:Digital Input - 将控制器数字输入的状态传递给模型,Digital Output - 将各部分的状态输出到控制器的数字输出端,To Serial - 通过控制器的串行端口输出数据。关于这些模块工作原理的详细说明,请参见其代码注释。

要成功生成Digital Input 程序块的代码,必须向其传递输入信号。在示例中,程序块Constant 就用于此目的。



状态图

Chart 程序块的状态图包含六个状态:五个为运行模式 (Red,RedAndYellow,Green,Blink,Yellow) ,一个为服务模式 (BlinkOn) 。

image_2.png

在每个状态下,计数器cnt 都会递减,交通灯区段的状态也会被设置light 。设置计数器和检查交通灯区段状态变化的条件在各状态之间的转换过程中进行Chart

以下是该程序块的信号表Chart

image.png

变量light 发送目标设备输出触点的二进制状态。变量中的第 0 位对应交通灯的绿色部分,第 1 位对应黄色部分,第 2 位对应红色部分。例如:在light = 4 = 0b0110 ,当 1 和 2 位的值为 "1 "时,交通灯的红色和黄色部分将被打开。

根据输入变量mode 的值,红绿灯模式会在RedBlinkOn 之间切换。

变量service 是块Chart 的本地变量,用于切换状态BlinkOn

建模结果

让我们加载所述模型:

In [ ]:
if "arduino_traffic_lights" in [m.name for m in engee.get_all_models()]
    m = engee.open( "arduino_traffic_lights" );
else
    m = engee.load( "$(@__DIR__)/arduino_traffic_lights.engee" );
end

data = engee.run(m);

根据获得的模型数据,绘制计数器变量cnt 和交通灯路段状态代码light

In [ ]:
using Plots
plotlyjs();
plot(data["Chart.cnt"].time, data["Chart.cnt"].value,
    label="Counter", size=(900,300), lw=2, st=:step)
plot!(data["Chart.light"].time, data["Chart.light"].value,
    label="Light", size=(900,300), lw=2, st=:step)
xlims!(0.0,40.0)
Out[0]:

绘制的图形显示了变量的变化。模型中的计算周期设定为 1 秒。红绿灯路段状态变量light 的变化值和持续时间与时间图相对应。

将代码上传到 Arduino

为了将开发的模型传输到目标设备,让我们生成 C 代码:

In [ ]:
engee.generate_code( "$(@__DIR__)/arduino_traffic_lights.engee",
                     "$(@__DIR__)/arduino_traffic_lights_code" )
[ Info: Generated code and artifacts: /user/start/examples/codegen/arduino_traffic_lights/arduino_traffic_lights_code

插件文件已在指定目录arduino_traffic_lights_code 中生成。此外,在演示示例的目录arduino_traffic_lights 中还有一个预先编写的 Arduino 草图,该目录的名称为arduino_traffic_lights.ino 。在该草图中,连接了代码生成过程中获得的头文件,初始化了全局变量,定义了模型计算循环,并调用了模型计算函数。代码注释中对草图进行了详细描述。

要在 Arduino 上执行代码,请下载arduino_traffic_lights 目录,并将草图arduino_traffic_lights.ino 从 Arduino IDE 加载到目标设备上。

在 Arduino 上执行代码

成功编译并将草图加载到目标设备后,调试板的数字输出将产生信号,打开交通灯部分(面包板上的 LED 灯)。为确保模型正常工作,让我们打开 Arduino IDE 中的串行端口监控器。

image.png

可以看出,To Serial 程序块中规定的报文以 1 秒的周期按要求的格式输出到串行端口。

按下按钮触点时,红色 LED 亮起,交通灯切换到待机模式,并向串行端口输出以下信息:

image.png

所需的信息以 1 秒的周期输出到串行端口。因此,交通灯在待机模式下的运行也是正确的,这证明了模型和代码的可操作性。

输出

在本演示中,我们为 Arduino 兼容平台开发了一个三节交通灯模型。控制算法是利用无限自动机库重新创建的。该模型再现了交通灯在两种模式下的运行,不仅提供了路段切换,还提供了闪烁功能。该算法已在目标设备上进行了测试,生成的代码不仅能控制 LED 部分,还能向串行端口发送有关当前模式、点亮部分和部分剩余时间的信息。

示例中使用的块