为 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。

型号说明
Arduino 板的内置 LED 控制模型由一个块Chart
(再现控制算法)和两个输出引脚Outport
:out_LED_BUILTIN
和Сhart
组成。第一个信号在 Arduino 草图中作为变量与 13 号输出引脚(GPIO)相连,该引脚也是集成 LED 的控制引脚。第二个信号输出模块内部计数器的状态Chart
。这两个信号也用于记录模型。
chart
块中使用了两个输出信号:out
初始值为 "true",而cnt
状态图
状态图(块Chart
的内容)有 4 种状态:
1.
init
- 算法初始状态,进入状态cnt = 0;
时设置计数器的初始值。increment
- 算法的主状态,计数器值与转换条件(编号为 1、2、3 的转换)进行比较,进入状态cnt = cnt + 1;
时计数器值增加。计数器值增加 "1 "实际上相当于增加 1 毫秒,因为算法计算将在 1 毫秒后进行,这将在 Arduino 草图代码中进一步定义。ON
- 如果计数器的值小于 499 毫秒,则计数器会递增。当满足该条件时,控制 LED 的输出信号将设置为 "true"。OFF
- 如果计数器的值大于或等于 499 毫秒,则计数器递增。满足此条件时,控制 LED 的输出信号将设置为 "假"。
当计数器的值大于或等于 998 ms 时,图表进入状态init
。由于计算初始状态还需要两个 2 毫秒的周期,而初始状态并不直接控制 LED,因此计数器被限制在 998 毫秒内。因此,LED 闪烁周期的总持续时间为 1000 ms。

开发模型时需要考虑的另一个要点是转换的执行顺序。这是从生成的 C 文件的条件构造if(){} elseif(){} ... else{}
中的状态过渡条件的规定顺序。
例如,在此模型中,首先检查周期结束条件[cnt >= 998]
,但如果其顺序为 3,那么成功执行条件[cnt >= 499]
将阻止检查周期结束条件,周期计数器将不会重置。另一方面,在每个计算周期中检查周期结束条件的效率不高,因此应将此转换的阶数定为 3,并将OFF
的转换条件改为[(cnt >= 499) && (cnt < 998)]
模拟结果
让我们加载所述模型:
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);
根据获得的模型数据,绘制计数器数值的变化图:
using Plots
plot(data["Cnt"].time, data["Cnt"].value,
label="Cnt", size=(900,300), lw=2)
xlims!(0.0,3.0)
可以看出,计数器的值在 0 - 1000 之间变化,周期为 1000 毫秒。现在绘制控制 Arduino 内置 LED 的信号out_LED_BUILTIN
的曲线图。
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)
我们得到了一个周期为 1000 毫秒、填充因子为 50%的周期性矩形信号。
在 Arduino 上执行算法
为了将开发的模型转移到目标设备上,让我们生成 C 代码:
engee.generate_code( "$(@__DIR__)/blink_chart.engee",
"$(@__DIR__)/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。
编译草图后,会显示操作成功和输出文件大小的信息:
捕捉 Arduino 板的信号
将代码加载到 Arduino 后,可以观察到调试板上的 LED 指示灯闪烁。为了使示例描述更加清晰,让我们将示波器的测量触点连接到调试板上的 13 号引脚,并绘制示波器图。

从捕捉到的振荡图可以看出,调试板的 13 引脚上形成了一个填充时间为 500 毫秒、周期为 1000 毫秒的周期性矩形信号。
输出
在本示例中,我们使用 Engee****无限自动机库为 Arduino 兼容平台开发了一个数字输出控制模型,并介绍了代码生成过程中正确高效的操作原则。
代码由开发的模型生成,并上传到目标设备。程序在调试板上的执行结果与建模结果完全一致。