应用NanoJ编制用户流程程序

1.NanoJ编程概要

NanoJ是一项允许您在控制器上运行用户的逻辑程序的技术。 使用的编程语言是C,带有一些扩展。

1.1 NanoJ用户工作流程

工作流程如下:
•然后将该二进制文件上传到控制器。
•修改源代码,通常是vmmcode.cpp。
•保存源代码。 编译器将文件转换(编译)为控制器的格式,即二进制文件。
•最后,程序在控制器上启动。

1.2 NanoJ工作机制

在Sandbox中,通过读写操作结合必要的逻辑判断,循环,运算等操作,实现用户逻辑流程控制。除了内部系统的读写(编写独立于主机的程序)外,NanoJ程序具有多种与控制器通信的可能性(与外部实时交互的程序):

•使用PDO映射读取和写入OD值

•使用系统调用直接读写OD值

•调用其他系统调用(例如,编写调试输出)

通过PDO映射以变量的形式提供用户程序的OD值。 在用户程序收到1 ms的时隙之前,固件会将值从对象字典传送到用户程序的变量。 用户程序收到计算时间后,便可以将这些变量作为常规C变量进行操作。 在时隙结束时,新值将由固件自动复制回相应的OD条目。 为了优化性能,定义了三种类型的映射:输入,输出和输入/输出(输入,输出,输入输出)。

•输入映射只能读取; 它们不会转移回对象字典。
•输出映射只能被写入。
•另一方面,输入/输出映射可以读取和写入。

1.3 可用计算时间

NanoJ程序以1 ms的时钟周期接收计算时间(请参见下图)。 由于计算时间会因固件的中断和系统功能而浪费,因此大约只有大约30%的时间。 用户程序可以使用30%– 50%的计算时间(取决于操作模式和应用程序)。 此时,用户程序必须运行该循环,并且必须完成循环或通过调用yield()函数产生计算时间。 在前一种情况下,用户程序在下一个1 ms周期的开始处重新启动; 后者导致程序在下一个1 ms周期中继续执行,并使用yield()函数后面的命令。

如果NanoJ程序需要的时间比分配的时间长,它将结束并在对象字典中设置错误代码。

小贴士

在开发用户程序时,必须仔细检查运行时行为,尤其是对于耗时的任务。 例如,因此建议使用表格代替使用sin函数计算正弦值。

备注

如果NanoJ程序在很长一段时间后仍未产生计算时间,则由操作系统结束。 在这种情况下,在对象2301h的状态字中输入数字4; 在对象2302h的错误寄存器中,数字为5(超时),请参见2301h NanoJ状态和2302h NanoJ错误代码。

 

1.4 执行NanoJ程序

当执行一个循环时,关于PDO映射,NanoJ程序实质上包括以下三个步骤:

1.从对象字典中读取值,并将其复制到输入和输出区域
2.执行一个用户程序
3.将值从输出和输入区域复制回对象字典
复制过程的配置基于CANopen标准。 另外,可以通过系统调用访问对象字典的值。 这通常比较慢; 因此,映射是优选的。 映射的数量是有限的(在In / Out / InOut中每个都有16个条目)。

小提示

Nanotec建议:映射经常使用和更改的OD条目,并使用系统调用来访问不经常使用的OD条目。

可用系统调用的列表可以在NanoJ程序中的系统调用一章中找到。

小提示

Nanotec建议通过映射或使用带有od_write()的系统调用来访问给定的OD值。 如果同时使用两者,则系统调用无效。

1.5 NanoJ程序结构

用户程序至少包含两个指令:

•预处理程序指令#include“ wrapper.h”

•void user(){}函数

可以将要执行的代码存储在void user()函数中

备注

用户程序的文件名不得超过8个字符,后缀不超过3个字符。 文件名main.cpp是允许的,文件名aLongFileName.cpp是不允许的

备注

在NanoJ程序中,全局变量只能在函数内初始化。 然后如下:
• No new operator
• No constructors
• No initialization of global variables outside of functions

例如:

全局变量将在void user()函数中初始化:

unsigned int i; void user(){

i = 1;

i += 1; }

以下分配不正确:

unsigned int i = 1; void user() {

i += 1; }

1.7 NanoJ程式范例

The example shows the programming of a square wave signal in object 2500h:01h.

// file main.cpp

map S32 outputReg1 as inout 0x2500:1

#include "wrapper.h"

// user program

void user()

{

U16 counter = 0;

while( 1 )

     {

       ++counter;

       if( counter < 100 )

       InOut.outputReg1 = 0;

       else if( counter < 200 )

       InOut.outputReg1 = 1;

       else counter = 0; // yield() 5 times (delay 5ms)

        for(U08 i = 0; i < 5; ++i )

         yield();

         }

}// eof

 1.8 .NanoJ程序中的映射

通过这种方法,NanoJ程序中的变量直接与对象字典中的条目链接。 映射的创建必须位于文件的开头,甚至在#include“ wrapper.h”指令之前。 映射上方允许有注释。

小贴士

我们建议:

•如果需要频繁访问对象字典中的对象,例如控制字6040h或状态字6041h,请使用映射。

•od_write()和od_read()函数更适合一次访问对象,请参阅访问对象字典。

 1.8,1 .NanoJ程序中的映射

完整的映射申明如下:

map <TYPE> <NAME> as <input|output|inout> <INDEX>:<SUBINDEX>

Where:

• <TYPE>

变量数据类型; U32, U16, U08, S32, S16 or S08.

• <NAME>

变量自定义名称.

• <input|output|inout>

变量的读写权限:可以将变量声明为输入,输出或输入输出。 这定义了变量是可读的((input),可写的(output)还是两者(inout)以及必须在程序中通过其寻址的结构。
• <INDEX>:<SUBINDEX>
对象字典中映射的对象的索引和子索引。

1.5 NanoJ program – OD entries

The NanoJ program is controlled and configured in object range 2300h to 2330h (see 2300h NanoJ Control).

Example: To start the TEST1.USR user program, the following sequence can, for example, be used: • Check entry 2302h for error code. • If no error: Start the NanoJ program by writing object 2300h, bit 0 = "1".

Note

It can take up to 200 ms for the NanoJ program to start.

Check entry 2302h for error code and object 2301h, bit 0 = "1".

Note

Due to limitations in the USB implementation, file "VMMCODE.USR" is, following a restart of the controller, set to a size of 16 kB and the creation date set to 13.03.2012.

To stop a running program: write entry 2300h with bit 0 value = "0".

 

 

 2.Mapping in the NanoJ program

 With this method, a variable in the NanoJ program is linked directly with an entry in the object dictionary. The creation of the mapping must be located at the start of the file here, even before the #include "wrapper.h" instruction. A comment is permitted above the mapping.

Tip

Nanotec recommends:

• Use mapping if you need to access an object in the object dictionary frequently, e.g., controlword 6040h or statusword 6041h.

• The od_write() and od_read() functions are better suited for accessing objects a single time, see Accessing the object dictionary.

2.1 Declaration of the mapping

The declaration of the mapping is structured as follows:

map <TYPE> <NAME> as <input|output|inout> <INDEX>:<SUBINDEX>

Where:

• <TYPE>
The data type of the variable; U32, U16, U08, S32, S16 or S08.
• <NAME>
The name of the variable as it is used in the user program.
• <input|output|inout>
The read and write permission of a variable: a variable can be declared as an input, output or inout. This defines whether a variable is readable (input), writable (output) or both (inout) and the structure by means of which it must be addressed in the program.
• <INDEX>:<SUBINDEX>
Index and subindex of the object to be mapped in the object dictionary.

Each declared variable is addressed in the user program via one of the three structures: In, Out or InOut depending on the defined write and read direction.

2.2 Example of mapping

Example of a mapping and the corresponding variable accesses:

map U16 controlWord as output 0x6040:00

map U08 statusWord as input 0x6041:00

map U08 modeOfOperation as inout 0x6060:00
#include "wrapper.h"
void user()
{

[...]
Out.controlWord = 1;
U08 tmpVar = In.statusword; InOut.modeOfOperation = tmpVar;

[...]

}

2.3 Possible error at od_write()

A possible source of errors is a write access with the od_write() function (see System calls in a NanoJ program) of an object in the object dictionary that was simultaneously created as mapping. The
code listed in the following is incorrect:

The line with the od_write(0x6040, 0x00, 5 ); command has no effect. As described in the
introduction, all mappings are copied to the object dictionary at the end of each millisecond.
This results in the following sequence:
1. The od_write function writes the value 5 in object 6040h:00h.
2. At the end of the 1 ms cycle, the mapping is written that also specifies object 6040h:00h, however,
with the value 1.
3. From the perspective of the user, the od_write command thus serves no purpose.

3 System calls in a NanoJ program

With system calls, it is possible to call up functions integrated in the firmware directly from a user
program. Because direct code execution is only possible in the protected area of the sandbox, this
is implemented via so-called Cortex-Supervisor-Calls (Svc Calls). An interrupt is triggered when
the function is called. The firmware thus has the possibility of temporarily allowing code execution
outside of the sandbox. Developers of user programs do not need to worry about this mechanism – for
them, the system calls can be called up like normal C functions. Only the wrapper.h file needs to be
integrated as usual.

3.1 Accessing the object dictionary

void od_write (U32 index, U32 subindex, U32 value)
This function writes the transferred value to the specified location in the object dictionary.
index Index of the object to be written in the object dictionary
subindex Subindex of the object to be written in the object dictionary
value Value to be written
Note

It is highly recommended that the processor time be passed on with yield() after calling a od_write(). The value is immediately written to the OD. For the firmware to be able to trigger actions that are dependent on this, however, it must receive computing time. This, in turn, means that the user program must either be ended or interrupted with yield().

U32 od_read (U32 index, U32 subindex)

This function reads the value at the specified location in the object dictionary and returns it.

Active waiting for a value in the object dictionary should always be associated with a yield().

Example

while (od_read(2400,2) != 0) // wait until 2400:2 is set
{ yield(); }

3.2 Process control

void yield()

This function returns the processor time to the operating system. In the next time slot, the program continues at the location after the call.

void sleep (U32 ms)
This function returns the processor time to the operating system for the specified number of
milliseconds. The user program is then continued at the location after the call.