AnyMath 文档
Notebook

Arduino代码生成(基于有限自动机的闪烁LED)

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

简介

AnyMath 有限自动机库是开发控制算法(包括针对目标设备的算法)的实用且高效的工具。 将算法表示为包含多个确定状态及其之间条件转换的图,使其更加直观,也更便于调试和升级。这样既提高了控制程序的开发速度,又降低了出错概率。

本示例旨在展示如何利用有限自动机库,为Arduino兼容平台创建闪烁LED的控制算法。
该示例实质上是演示示例arduino_blink 的变体,其中包含对模型blink_chart 的描述及其工作原理演示。在此模型中,LED的状态(开/关)以及切换延迟值由模块chart 确定。

硬件支持

为验证算法运行情况,可使用任何与Arduino兼容的平台。 engee 模型之所以具有多平台兼容性,是因为生成的 C 代码以可链接的 C 文件形式导入,而硬件配置则在第三方软件中进行。

在本示例中,我们使用 Amperka 公司的 Iskra Neo 调试板。它通过 USB - USB-micro 数据线连接到电脑。 编译用户程序并将其上传至控制器时,需使用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.init - 算法的初始状态,在进入cnt = 0; 状态时设置计数器的初始值。
2.increment - 算法的主状态,在此处将计数器的值与转换条件(编号为1、2、3的转换)进行比较,并在进入状态cnt = cnt + 1; 时将计数器的值加1。 计数器值增加“1”实际上相当于增加1毫秒,因为算法的计算将在1毫秒后进行,这一点将在后续的Arduino草图代码中定义。
3.ON ——当计数器值小于499毫秒时,计数器会增加的状态。满足此条件时,控制LED的输出信号将设为“true”。
4.OFF - 当计数器值大于或等于499毫秒时,计数器会递增的状态。满足此条件时,控制LED的输出信号将设为“false”。

当计数器值达到大于或等于998毫秒时,状态图将转入init 状态。 计数器被限制在998毫秒,因为计算“init”状态(该状态不直接控制LED)还需要两个周期,每个周期持续2毫秒。 因此,LED闪烁周期的总持续时间为1000毫秒。

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 毫秒。 现在,我们绘制信号out_LED_BUILTIN 的波形图,该信号用于控制Arduino的内置LED。

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 目录,并通过 Arduino IDE 将程序sketch_blink_chart_custom.ino 上传至目标设备。如前所述,本例中使用的是 Amperka 公司的 Iskra Neo。


编译程序后,系统会显示操作成功的信息以及输出文件的大小:

Compilation.JPG

从 Arduino 开发板采集信号

将代码上传至 Arduino 后,可在调试板上观察到 LED 闪烁。为了便于说明,本示例中我们将示波器的测量探头连接到调试板的第 13 引脚,并采集波形。

FirstOscillogramma.jpg

从捕获的示波波形可以看出,调试板的第13引脚上形成了一个周期性方波信号,其占空时间为500毫秒,周期为1000毫秒。

结论

在本示例中,利用AnyMath有限自动机库为Arduino兼容平台开发了数字引脚控制模型,并阐述了代码生成过程中确保正确且高效运行的原则。


基于开发的模型生成了代码并将其加载到目标设备上。在调试板上运行程序的结果与模拟结果完全一致。

示例中使用的块