Engee 文档
Notebook

为 Arduino 生成代码(有限自动机上闪烁的 LED 灯)

在本示例中,我们将使用有限自动机库在Engee中开发一个简单的模型,用于控制 Arduino 兼容板的内置 LED。

简介

Engee** 无限自动机库是开发控制算法(包括目标平台)的有用而高效的工具。将算法表示为具有若干定义状态和状态间条件转换的图形,可使其更加直观、易于调试和现代化。因此,控制程序的开发速度得到了提高,出错的概率也降低了。
<br

本示例旨在展示使用无限自动库为 Arduino 兼容平台创建闪烁 LED 控制算法的过程。 该示例基本上是演示示例arduino_blink 的变体,包括对模型blink_chart 的描述和演示。在该模型中,LED 的状态(开/关)和开关延迟由块chart 决定。

硬件

任何与 Arduino 兼容的平台都可用于测试算法。engee 模型之所以具有多平台特性,是因为生成的 C 代码是作为 C 插件文件导入的,而硬件配置是在第三方软件中进行的。

在本例中,我们使用一块调试板 Amperka 的 Iskra Neo。它通过 USB 转 USB 微电缆与电脑连接。为了编译用户程序并将其加载到控制器中,我们使用了 Arduino IDE 并需要接口芯片的驱动程序。为了演示程序在硬件上的工作情况,我们使用了便携式数字示波器Seeed Studio 的 DSO Quad Alloy Black

hardware.png

型号说明

Arduino 板的内置 LED 控制模型由一个块Chart (再现控制算法)和两个输出引脚Outportout_LED_BUILTINСhart 组成。第一个信号在 Arduino 草图中作为变量与 13 号输出引脚(GPIO)相连,该引脚也是集成 LED 的控制引脚。第二个信号输出模块内部计数器的状态Chart 。这两个信号也用于记录模型。

modelscreeen.JPG

chart 块中使用了两个输出信号:out初始值为 "true",而cnt

状态图

状态图(块Chart 的内容)有 4 种状态:
1.

  1. init - 算法初始状态,进入状态cnt = 0; 时设置计数器的初始值。
  2. increment - 算法的主状态,计数器值与转换条件(编号为 1、2、3 的转换)进行比较,进入状态cnt = cnt + 1; 时计数器值增加。计数器值增加 "1 "实际上相当于增加 1 毫秒,因为算法计算将在 1 毫秒后进行,这将在 Arduino 草图代码中进一步定义。
  3. ON - 如果计数器的值小于 499 毫秒,则计数器会递增。当满足该条件时,控制 LED 的输出信号将设置为 "true"。
  4. OFF - 如果计数器的值大于或等于 499 毫秒,则计数器递增。满足此条件时,控制 LED 的输出信号将设置为 "假"。

当计数器的值大于或等于 998 ms 时,图表进入状态init 。由于计算初始状态还需要两个 2 毫秒的周期,而初始状态并不直接控制 LED,因此计数器被限制在 998 毫秒内。因此,LED 闪烁周期的总持续时间为 1000 ms。

image.png

开发模型时需要考虑的另一个要点是转换的执行顺序。这是从生成的 C 文件的条件构造if(){} elseif(){} ... else{} 中的状态过渡条件的规定顺序。



例如,在此模型中,首先检查周期结束条件[cnt >= 998] ,但如果其顺序为 3,那么成功执行条件[cnt >= 499] 将阻止检查周期结束条件,周期计数器将不会重置。另一方面,在每个计算周期中检查周期结束条件的效率不高,因此应将此转换的阶数定为 3,并将OFF 的转换条件改为[(cnt >= 499) && (cnt < 998)]

模拟结果

让我们加载所述模型:

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

data = engee.run(m);

根据获得的模型数据,绘制计数器数值的变化图:

In [ ]:
using Plots
plot(data["Cnt"].time, data["Cnt"].value,
    label="Cnt", size=(900,300), lw=2)
xlims!(0.0,3.0)
Out[0]:

可以看出,计数器的值在 0 - 1000 之间变化,周期为 1000 毫秒。现在绘制控制 Arduino 内置 LED 的信号out_LED_BUILTIN 的曲线图。

In [ ]:
plot(data["out_LED_BUILTIN"].time, data["out_LED_BUILTIN"].value,
    label="out_LED_BUILTIN", size=(900,300), lw=2)   
xlims!(0.0,3.0)
Out[0]:

我们得到了一个周期为 1000 毫秒、填充因子为 50%的周期性矩形信号。

在 Arduino 上执行算法

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

In [ ]:
engee.generate_code( "$(@__DIR__)/blink_chart.engee",
                     "$(@__DIR__)/sketch_blink_chart_custom/blink_chart_code" )
Out[0]:
"Created directory - /user/start/examples/codegen/arduino_blink_chart/sketch_blink_chart_custom/blink_chart_code"

插件文件已在指定目录下生成。此外,在sketch_blink_chart_custom 目录中还有一个预先编写好的 Arduino 草图,其名称为sketch_blink_chart_custom.ino 。在该草图中,连接了代码生成过程中获得的头文件,初始化了微控制器的外围设备,并调用了控制 LED 的函数。代码注释中对草图进行了详细描述。 要在 Arduino 上执行代码,需要下载sketch_blink_chart_custom 目录,并将sketch_blink_chart_custom.ino 草图从 Arduino IDE 加载到目标设备中。在我们的例子中,如前所述,目标设备是 Amperka 的 Iskra Neo。 编译草图后,会显示操作成功和输出文件大小的信息:

compilation.JPG

捕捉 Arduino 板的信号

将代码加载到 Arduino 后,可以观察到调试板上的 LED 指示灯闪烁。为了使示例描述更加清晰,让我们将示波器的测量触点连接到调试板上的 13 号引脚,并绘制示波器图。

firstoscillogramma.jpg

从捕捉到的振荡图可以看出,调试板的 13 引脚上形成了一个填充时间为 500 毫秒、周期为 1000 毫秒的周期性矩形信号。

输出

在本示例中,我们使用 Engee****无限自动机库为 Arduino 兼容平台开发了一个数字输出控制模型,并介绍了代码生成过程中正确高效的操作原则。 代码由开发的模型生成,并上传到目标设备。程序在调试板上的执行结果与建模结果完全一致。

示例中使用的块