FLProg-独立集成到自定义控制器的程序中


很长时间以来,FLProg项目尚未在Habré中涉及。 这是由于没有时间写文章,甚至是我自己的个人问题。 但是该项目并没有死,它仍然存在并发展。 平均每月发布一次该程序的下一个版本。 自上次发布以来,该方案的渲染已完全重做(由于此,在渲染过程中消除了条纹),引入了该项目的交叉引用系统以及一个错误项目分析系统。 程序代码本身已移植到编程语言的最新版本。 因此,从积极的一面(从我的角度来看,当然,用户会支持我)方面,程序界面已更改。 增加了许多有趣的块。 实现方案缩放和块搜索。

但是,该项目的主要问题仍然是该程序的开发人员是一个人(这就是我),而且中国人和我们的制造商最近制造的不同控制器板,传感器,传感器,扩展卡的数量一直在增长。 用户想尝试它们,或在他们的项目中使用它们。 对于外围设备,我一次或多或少地通过创建自定义块编辑器解决了这个问题。 正如他们所说的那样,这一步骤“射击”了,现在,几乎在大多数现有外围设备板上,Internet上或该程序的论坛上 ,您都可以找到相应的用户块。 好吧,或者在同一个论坛中(或在Vkontakte的一个小组中 ),请当地专家撰写一份。 通常您可以以一种或另一种方式达成共识。

现在该解决第二个问题了-这些是控制器板。

最近,发布了该程序的Beta版本,编号为6.0。 在此版本中,实现了用于控制器用户描述的编辑器。 与自定义块一样,描述可以分组到库中,另存为单独的描述,也可以将整个库另存为文件并共享。

在这篇文章中,我将开始一系列至少两篇文章,我们将考虑如何使用此编辑器。 在第一篇文章中,在shiotiny的允许下我将尝试描述他的ShIoTiny董事会。 在第二篇文章中,我们将尝试虚拟提出一个真正的工业控制器的模拟物,并对其进行描述。

因此,让我们开始吧。

打开FLProg程序。 在“工具”菜单中,选择“控制器自定义描述编辑器”。



说明编辑器窗口打开。

定制描述可以在库中以任何嵌套深度进行组合。 因此,我们首先创建一个描述库。



设置库的名称。



之后,我们将在库中创建控制器的描述。



给他起个名字:



创建控制器描述时,我们立即进入常规参数分支。



在这里,我们可以更改名称,设置CPU的类型,设置EEPROM的大小,更改描述的版本号。

由于ShloTiny开发板基于ESP8266控制器,因此我们选择它。 选择控制器时,将自动粘贴相应的EEPROM体积。 如有必要,可以更改此值。 新描述的默认版本号设置为1。

将来,当更改现有说明时,建议朝增加的方向进行更改。 在项目中使用此描述时,程序将比较项目和库中描述的版本。 如果库中描述的版本号大于项目中的描述的版本号,则用于更新程序中描述的项目将显示在程序的“项目”菜单中。 如果反之亦然,则将出现一个条目来更新库中的描述。 好吧,如果在库中根本找不到项目中使用的控制器的描述,则用于将项目中的描述输入到库中的项目将出现在同一菜单中。

同样在此线程中,您可以编写控制器的文本说明,以帮助其选择。

要应用在更改到另一个控制器参数分支之前所做的更改,请单击“应用”按钮。 如果不这样做,更改将不会保存。

现在考虑ShloTiny板的电路图。



该评估板具有三个数字输入,三个数字输出,一个模拟输入和一个传感器输入。 董事会共有八项结论。

在编辑器中,转到“控制器结论”分支并设置结论数,然后不要忘记单击“应用”按钮。



该程序将生成所需数量的结论。 我们传递给他们中的第一个。



在此分支上,您可以指定一个备用输出名称,该名称将显示在程序中。 如果不存在,程序将显示PinN,其中N是引脚列表中的引脚号。 我建议您在备用名称字段上的板上写上题字。 这将使您更容易理解得出的结论。 对于上述电路板,我们将根据电路图在其中放置Input1的值。 因此,在此线程中,您可以编写输出的单独描述,例如,以指示其应用程序的功能。 如有必要,还可以为输出指定别名,该别名将在程序中输出名称后的方括号中显示。

进行更改后,请不要忘记单击“应用”按钮。 按照相同的原则,让我们称呼其余结论。



在主要输出参数的同一分支中,将其执行的功能添加到其中。



可以将以下功能分配给每个引脚。



  • I2C是I2C总线引脚之一(SDA或SCL)的功能。
  • SPI是SPI总线引脚之一(MISO,MOSI,SCK,SS)的功能。
  • UART是UART接口引脚之一(RX或TX)的功能。
  • 模拟输入功能
  • 模拟输出功能(尚未在FLProg中使用,将来会积压)。
  • 数字输入/输出功能。

分配引脚1数字输入/输出功能。 当为输出分配新功能(尚未配置)时,“错误”分支将出现在控制器树中,并且错误函数分支的整个路径将变为红色。 在“错误”分支上,您可以看到在控制器说明中找到的错误列表。



在这种情况下,“数字输入/输出”功能中的输入1不会将此输入的编号指定为数字。 我们切换到该功能的分支(将来,我计划在单击错误时直接转换到所需的分支)。



根据电路板的电路图,输出“ Input1”连接到控制器GPIO2引脚。 我们在“数字输入编号”字段中输入此值。 编译项目时,此值将插入代码中。 由于未提供使用此输入作为输出的功能,因此我们取消选中“可以用作输出”复选框。 与往常一样,单击“应用”按钮。 以相同的方式,配置其他两个数字输入。 然后转到模拟量输入“ ADC1”的输出,并向其中添加功能“ Analog Input”。



在此功能的设置中,必须指定此输出的编号作为模拟输入。 根据方案,它是0(在ESP8266中是1)。



然后转到输出设置“ 1WIRE”。 在这里,我们向其添加了数字输入/输出功能,但是我们禁止同时使用其输入和输出。 结果,它将仅在传感器块中可供选择,而在创建输入和输出时不可用。



输出“ K1”-“ K3”的设置与输入“ Input1”-“ Input3”的设置相似,仅在输出禁止的情况下才允许将它们用作输入。





转到“图像”分支。 在其中,我们可以添加加载到控制器描述中的图像,例如,带有控制器的外观,引脚排列等。

请注意,图像直接加载到控制器描述中,并存储在使用它创建的项目中。 因此,请勿上传大图像。 这将导致项目规模的增加,并会影响项目的开放速度。

使用“添加图像”按钮添加图像。



选择并加载图像后(仅支持PNG格式),将为树中的每个图像创建一个单独的分支。 在此分支中,您可以为下载的图像指定名称,该名称将显示在控制器信息中。



如有必要,您可以上传多张图像并按所需顺序设置它们。





我们转到“标准块”分支。



该分支显示了可以根据程序的当前设置在程序的标准块库中表示的块。 那是什么意思 该程序的某些构造块仅适用于某些类型的CPU。 它们的可用性取决于所选的处理器。 同样,仅当将相应功能添加到控制器引脚时,设计为与SPI,I2C,UART配合使用的模块才会出现在此列表中。 仅当在常规控制器参数的分支上指定的EEPROM大小大于零或将I2C功能添加到控制器引脚时,才会出现设计用于EEPROM的模块。 默认情况下,禁止使用库中的所有块(以橙色标记)。 通过突出显示必要的分支并单击“允许”按钮,或从上下文菜单中,可以允许使用单独的块或整个块文件夹。



当您允许程序中显示一个块或一个块的文件夹时,它们会涂成黑色。

禁止以相同方式阻止块或单独的文件夹。



在不允许显示所有块的文件夹中,它们的名称前后带有三个星号。

我们禁止使用电动机,实时时钟,显示器,IR控制单元,传感器(DS18B20和DHT22传感器除外,因为板级开发人员到目前为止仅宣布支持),扩展芯片和压电扬声器。



我们将对LAD语言执行相同的操作



进行更改后,请不要忘记应用它们。

控制器说明的其余分支将在下一篇文章中讨论,而当前设置足以在FLProg程序中使用此特定板。

现在,我只简单说一句“特殊块”分支用于将自定义块加载到描述中,这将使用此控制器显示在程序的标准块库中。 “特殊代码”分支用于编写将始终插入到已编译代码中的代码,“特殊库”分支用于将库加载到控制器的描述中,该描述将上载到\库ArduinoIDE文件夹中,程序可与该文件夹一起使用。

完成所有更改后,保存控制器描述库。



现在,让我们尝试使用我们创建的控制器描述。
在FLProg程序中,我们为控制器创建一个新项目。



打开控制器选择窗口。



并选择我们创建的描述。



选择控制器时,您可以查看其图像,并在必要时将必要的内容保存到文件中。





您还可以看到其他参数



选择控制器后,确认其选择:



结果,将打开主项目工作窗口,该窗口根据所选控制器进行配置。



这篇文章的目的不包括使用FLProg程序进行培训的任务。 在我以前的帖子项目网站项目论坛中对此进行了很好的描述。 此外,YouTube上已经有很多频道发布了有关使用该程序的视频教程。 例如,一个非常好的频道“ Arduino的钥匙”刚刚进行了一系列课程,并很好地介绍了材料。

因此,例如,我们仅创建一个简单的项目并尝试对其进行编译。
首先,添加控制器输入。



只有那些允许我们以此身份工作的结论才可供选择,以作为输入。



然后创建一个数字输出。 此处仅提供允许的结论。



我们组装电路,以便在按下Input1(输入1)上的按钮时,继电器K1(Relay1)打开。
我们还从DS18B20传感器中拉出用于读取信息的单元,并将其放在单独的电路板上。



转到块设置。 创建新的OneWire总线时,我们看到所有数字输入/输出均可供选择。 这是6.0版中的一个缺陷。 但由于此版本仍具有Beta状态,因此可以原谅。 在6.1版中,可以禁止将引脚用作传感器,传感器或扩展芯片。 另外,在下一篇文章中,我将告诉您另一种解决此问题的方法。 现在,选择1WIRE引脚。



我们将传感器配置为通过阵列设置地址。



我们在第一个电路板的前面插入一块新的电路板,然后拉出其上的“ OneWire总线扫描”块。



我们将其配置在同一OneWire总线和同一阵列上,在该阵列上配置了DS18B20传感器的读取单元。



我们使用触发器的R块(选择上升沿)为控制器启动时的总线扫描的一次启动组装了一个方案。



在带有DS18B20传感器数据读取单元的板上,我们组装了具有滞后作用的继电器开/关电路。 为此,我们使用两个比较器和一个SR触发器。 由于采用了这种方案,继电器2将在温度下降到10度以下时打开,并在温度上升到20度以上时关闭。



我们开始编译方案。



结果,结果代码将在Arduino IDE中打开。 作为一块板,我们选择NodeMCU,其上安装的芯片与ShloTiny相同。 检查代码表明它正确无误,可以下载。



编译代码
#include <OneWire.h> extern "C" { #include "user_interface.h"; } OneWire _ow13(13); byte _FLPArray133289689[9]; unsigned long _d18x2x1Tti = 0UL; float _d18x2x1O = 0.00; bool _trgr1 = 0; bool _bounseInputD2S = 0; bool _bounseInputD2O = 0; unsigned long _bounseInputD2P = 0UL; bool _trgrt1 = 0; bool _trgrt1I = 0; bool _sowb1_needScan = 0; bool _sowb1_ost = 0; bool _sowb1_Out_1 = 0; void setup() { pinMode(2, INPUT); pinMode(12, OUTPUT); pinMode(14, OUTPUT); _bounseInputD2O = digitalRead(2); } void loop() { bool _bounceInputTmpD2 = (digitalRead (2)); if (_bounseInputD2S) { if (millis() >= (_bounseInputD2P + 40)) { _bounseInputD2O = _bounceInputTmpD2; _bounseInputD2S = 0; } } else { if (_bounceInputTmpD2 != _bounseInputD2O ) { _bounseInputD2S = 1; _bounseInputD2P = millis(); } } //:1 if (1) { if (_trgrt1I) { _trgrt1 = 0; } else { _trgrt1 = 1; _trgrt1I = 1; } } else { _trgrt1 = 0; _trgrt1I = 0; }; if (_sowb1_needScan) { if ( _oneWireSeach (_FLPArray133289689, _ow13)) { _sowb1_Out_1 = 1; } _ow13.reset_search(); _sowb1_needScan = 0; } if (_trgrt1) { if (! _sowb1_ost) { _sowb1_ost = 1; _sowb1_needScan = 1; _sowb1_Out_1 = 0; } } else { _sowb1_ost = 0; } //:2 digitalWrite(12, !(_bounseInputD2O)); //:3 if (_isTimer(_d18x2x1Tti, 1000)) { _d18x2x1Tti = millis(); _d18x2x1O = _readDS18_ow13(_FLPArray133289689, _FLPArray133289689[8]); } if (((_d18x2x1O)) > (20)) _trgr1 = 0; if (((_d18x2x1O)) < (10)) _trgr1 = 1; digitalWrite(14, _trgr1); } bool _isTimer(unsigned long startTime, unsigned long period ) { unsigned long currentTime; currentTime = millis(); if (currentTime >= startTime) { return (currentTime >= (startTime + period)); } else { return (currentTime >= (4294967295 - startTime + period)); } } float _convertDS18x2xData(byte type_s, byte data[12]) { int16_t raw = (data[1] << 8) | data[0]; if (type_s) { raw = raw << 3; if (data[7] == 0x10) { raw = (raw & 0xFFF0) + 12 - data[6]; } } else { byte cfg = (data[4] & 0x60); if (cfg == 0x00) raw = raw & ~7; else if (cfg == 0x20) raw = raw & ~3; else if (cfg == 0x40) raw = raw & ~1; } return (float)raw / 16.0; } float _readDS18_ow13(byte addr[8], byte type_s) { byte data[12]; byte i; _ow13.reset(); _ow13.select(addr); _ow13.write(0xBE); for ( i = 0; i < 9; i++) { data[i] = _ow13.read(); } _ow13.reset(); _ow13.select(addr); _ow13.write(0x44, 1); return _convertDS18x2xData(type_s, data); } bool _oneWireSeach (byte array[], OneWire ow ) { byte temp[8]; byte i; if ( !ow.search(temp)) { return false; } if (OneWire::crc8(temp, 7) != temp[7]) { return false; } switch (temp[0]) { case 0x10: array[8] = 1; break; case 0x28: array[8] = 0; break; case 0x22: array[8] = 0; break; default: return false; } for ( i = 0; i < 8; i++) { array[i] = temp[i]; } return true; } 


FLProg程序的ShloTiny控制器说明

我们将完成本课,在下一篇文章中,我们将尝试描述更严肃的,更接近“真实”工业控制器的事物。

Source: https://habr.com/ru/post/zh-CN467007/


All Articles