應用NanoJ編制用戶流程程序
1.NanoJ編程概要
NanoJ是一項允許您在控制器上運行用戶的邏輯程序的技術。 使用的編程語言是C,帶有一些擴展。
1.1 NanoJ用戶工作流程
工作流程如下:
?然后將該二進制文件上傳到控制器。
?修改源代碼,通常是vmmcode.cpp。
?保存源代碼。 編譯器將文件轉換(編譯)為控制器的格式,即二進制文件。
?最后,程序在控制器上啟動。
1.2?NanoJ工作機制
在Sandbox中,通過讀寫操作結合必要的邏輯判斷,循環(huán),運算等操作,實現用戶邏輯流程控制。除了內部系統(tǒng)的讀寫(編寫獨立于主機的程序)外,NanoJ程序具有多種與控制器通信的可能性(與外部實時交互的程序):
?使用PDO映射讀取和寫入OD值
?使用系統(tǒng)調用直接讀寫OD值
?調用其他系統(tǒng)調用(例如,編寫調試輸出)
通過PDO映射以變量的形式提供用戶程序的OD值。 在用戶程序收到1 ms的時隙之前,固件會將值從對象字典傳送到用戶程序的變量。 用戶程序收到計算時間后,便可以將這些變量作為常規(guī)C變量進行操作。 在時隙結束時,新值將由固件自動復制回相應的OD條目。 為了優(yōu)化性能,定義了三種類型的映射:輸入,輸出和輸入/輸出(輸入,輸出,輸入輸出)。
?輸入映射只能讀取; 它們不會轉移回對象字典。
?輸出映射只能被寫入。
?另一方面,輸入/輸出映射可以讀取和寫入。
1.3 可用計算時間
NanoJ程序以1 ms的時鐘周期接收計算時間(請參見下圖)。 由于計算時間會因固件的中斷和系統(tǒng)功能而浪費,因此大約只有大約30%的時間。 用戶程序可以使用30%– 50%的計算時間(取決于操作模式和應用程序)。 此時,用戶程序必須運行該循環(huán),并且必須完成循環(huán)或通過調用yield()函數產生計算時間。 在前一種情況下,用戶程序在下一個1 ms周期的開始處重新啟動; 后者導致程序在下一個1 ms周期中繼續(xù)執(zhí)行,并使用yield()函數后面的命令。
如果NanoJ程序需要的時間比分配的時間長,它將結束并在對象字典中設置錯誤代碼。
小貼士
在開發(fā)用戶程序時,必須仔細檢查運行時行為,尤其是對于耗時的任務。 例如,因此建議使用表格代替使用sin函數計算正弦值。
備注
如果NanoJ程序在很長一段時間后仍未產生計算時間,則由操作系統(tǒng)結束。 在這種情況下,在對象2301h的狀態(tài)字中輸入數字4; 在對象2302h的錯誤寄存器中,數字為5(超時),請參見2301h NanoJ狀態(tài)和2302h NanoJ錯誤代碼。
?
1.4 執(zhí)行NanoJ程序
當執(zhí)行一個循環(huán)時,關于PDO映射,NanoJ程序實質上包括以下三個步驟:
2.執(zhí)行一個用戶程序
3.將值從輸出和輸入區(qū)域復制回對象字典
復制過程的配置基于CANopen標準。 另外,可以通過系統(tǒng)調用訪問對象字典的值。 這通常比較慢; 因此,映射是優(yōu)選的。 映射的數量是有限的(在In / Out / InOut中每個都有16個條目)。
小提示
Nanotec建議:映射經常使用和更改的OD條目,并使用系統(tǒng)調用來訪問不經常使用的OD條目。
可用系統(tǒng)調用的列表可以在NanoJ程序中的系統(tǒng)調用一章中找到。
小提示
Nanotec建議通過映射或使用帶有od_write()的系統(tǒng)調用來訪問給定的OD值。 如果同時使用兩者,則系統(tǒng)調用無效。
1.5 NanoJ程序結構
用戶程序至少包含兩個指令:
?預處理程序指令#include“ wrapper.h”
?void user(){}函數
可以將要執(zhí)行的代碼存儲在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程序中的變量直接與對象字典中的條目鏈接。 映射的創(chuàng)建必須位于文件的開頭,甚至在#include“ wrapper.h”指令之前。 映射上方允許有注釋。
小貼士
我們建議:
?如果需要頻繁訪問對象字典中的對象,例如控制字6040h或狀態(tài)字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.
?