<%@ Page Language="C#" %> <%@ Register src="/ASPNetUserControl/GoogleAd.ascx" tagname="GoogleAd" tagprefix="uc1" %> 进阶话题:文件系统、LCP、内存、NXJ 工具
进阶话题:文件系统、LCP、内存、NXJ 工具
进阶话题:文件系统、LCP、内存、NXJ 工具
感谢网友 lifanxi 的翻译

理解leJOS文件系统

leJOS NXJ在闪存上实现了它的文件系统。在闪存上以256字节的页为单位来读取或写入数据。 闪存的前两个页上保存了文件列表(目录),其它的页上则可以用来保存用户的文件。 文件以连续的字节来表示 - 也就是说,文件可以占用连续的多个页。这样就保证了文件可以以内存区域的地址来直接访问。

Flash

Flash类用于从闪存中读写256字节的页。 用户程序中不应当使用这个类。

File

File类包含一系列用于操作文件系统静态方法和一些用于操作特定的文件的实例方法。

静态方法:

  • void defrag()

    从文件系统中移除未被使用的页。

  • void format()

    删除所有的文件并重新格式化闪存。

  • int freeMemory()

    返回文件系统的总省余空间。

  • File[] listfiles()

    以数组的形式返回文件系统中的所有的文件对象。

如果需要创建一个新文件,需要调用构造函数先构造一个File类的实例。

  • File(String name)

然后

  • create newFile();

    这个方法可能会抛出IOException异常,所以必须在try/catch块中调用它。

如果需要读写文件数据,需要使用流。流对象的构造函数同样可能抛出IOException异常, 所以也必须在try/catch块中调用它。

文件流只能读写单个的字节,所以你通常需要使用数据流过滤器。

FileOutputStream

这个类有以下构造函数:

  • FileOutputStream(File f)

    创建一个新的OutputStream实例用于对文件进行写入操作,写入会从文件开头的位置开始。

  • FileOutputStream(File f, boolean append)

    创建一个新的OutputStream实例用于对文件进行写入操作。

  • void write(int b)

    向文件中写入1个字节

  • void flush()

    刷新输出流,强制所有缓存数据写入闪存

  • void close()

    把缓存的数据写入闪存并更新内存中的文件参数。请确保在程序退出前调用这个函数。

代码示例:


import lejos.nxt.*;

public class FileWriteTest {
  public static void main(String[] args) { 
    FileOutputStream out = null; // 在try/catch块外声明对象
    File data = new File("log.dat");

    try {
      data.createNewFile();
      out = new FileOutputStream(data);
    } catch(IOException e) {}

    DataOutputStream dataOut = new DataOutputStream(out);

    float x = 1f;
    int length = 8;

    try { // 写入数据
      for(int i = 0 ; i<length; i++ ) {
        dataOut.writeFloat(x);
        LCD.drawString(" "+x,0,i);
        x = x*-2.2f; 
      }
      out.close(); // 刷新缓存写入文件
    } catch (IOException e) {
      LCD.drawString("write error", 0, 3);
    }
    Sound.beep();
    Button.waitForPress();
  }
}
				

运行完这个程序后,你可以在File菜单中找到你新创建的文件。

FileInputStream

构造函数:

  • FileInputStream( File afile)

方法:

  • int read()

    读出并返回一个字节,返回值的范围是0到255。

  • int available()

    返回该文件剩余可以继续读出的字节数。

这里是一个文件读取的示例:



import java.io.*;
import lejos.nxt.*;

public class FileReadTest {
  public static void main(String[] args) {
    File data = new File("log.dat");
    int i = 0;

    try {
      InputStream is = new FileInputStream(data);
      DataInputStream din = new DataInputStream(is);

      while (is.available() > 3) { // 至少还应该有4个以字节的数据可以被读取
        LCD.drawInt(is.available(), 4, 10, 0);
        float x = din.readFloat();

        LCD.drawString("" + x, 0, i++);
      }
      din.close();
    } catch (IOException ioe) {
      LCD.drawString("read exception", 0, 0);
    }
    Button.waitForPress();
  }
}
				

返回顶部

理解LCP

LEGO定义了一种协议,称为 LEGO MINDSTORMS NXT通信协议 (LEGO MINDSTORMS NXT Communications Protocol, LCP)。这个协议用于向LEGO标准固件发送指令。 有关这个协议的规范可以在 http://mindstorms.lego.com/Overview/NXTreme.aspx中的蓝牙开发套件(Bluetooth Development Kit)中找到。指令分为直接指令和系统指令。 直接指令在一篇独立的文件中有描述:LEGO MINDSTORMS NXT : Direct Commands.

直接指令是设计用于让用户程序和工具来控制机器人的, 而系统指令则是用于让工具软件来上传下载文件或执行其它各种管理任务的。

leJOS NXJ 仿真了很多直接指令和系统指令,所以很多可以在LEGO标准固件上使用的工具也可以在leJOS上直接使用。

很多leJOS NXJ的工具,比如: nxj, nxjuload, nxjbrowse 和 nxjmonitor都使用了LCP。 leJOS NXJ 对LCP进行了一些小的扩展,以便让 相关的工具可以更好的工作。

LCP在lejos.nxt.comm.LCP类中实现。对于leJOS的传感器和马达, 它们在leJOS上的工作方式与在标准固件上稍有不同, 因此leJOS上的LCP语意与在LEGO标准固件上也并不总是完全一致的。

启动菜单中的 – StartUpTextjava – 功能使用LCP来支持leJOS NXJ的工具和其它的第三方工具。 这也就意味着当这个功能运行的时候,可以通过蓝牙或USB来执行LCP指令。

返回顶部

理解leJOS NXJ的内存使用

NXT具备256K的闪存和64K的内存。

闪存可以像内存一样的读取(比内存稍慢一点),但只能以256字节的页为单位来写入数据,这是由硬件规范所决定的。 在往闪存中写入数据时,不能同时读取数据。

leJOS NXJ固件是用C和ARM的汇编指令写成的。它包含了初始化的代码、Java虚拟机和所有的硬件设备驱动程序。 leJOS固件不依赖并完全可以替代LEGO标准固件。闪存的前32K被分配用于leJOS NXJ固件。 大部分的可执行代码都是从内存上执行的,但有一小部分的代码(比如:向闪存中写入数据页)是被复制到内存中执行。 只读的数据会被保留在闪存中而需要被读写的数据会被复制到内存中。固件具有一个固定大小的堆栈和中断栈。

leJOS NXJ Java 虚拟机一次运行一个Java程序。这个程序可以是用户程序也可以是leJOS的启动菜单。 一个Java程序中可以调用另一个程序。当第二个程序被调用起来后,第一个程序就会被从内存中移除,第二个程序得以执行。 这也就是启动菜单调用用户程序的原理。

启动菜单占用从内存地址32K(也就是说从固件的结尾)开始的48K内存。 这48K内存的最后一个字记录了整个启动菜单程序的大小。

Java程序从闪存中运行。静态的只读数据被保留在闪存中。 静态读写数据被复制到内存中。对象会在堆中被创建,堆是从内存的最高地址向低地址方向生长的。 Java运行栈从内存的最低地址开始向上生长。在堆即将被用满的时候, 垃圾回收机制会销毁未被引用的对象来释放内存。

返回顶部

理解leJOS NXJ的工具

nxjflash

如果要把固件写入NXT,NXT必须被设置在固件更新模式。如果你使用LEGO标准固件 (或者你正在使用一个较新的leJOS固件),nxjflash程序会自动保证这一点。 如果它没有正常的自动工作,你需要手工把NXT设置到固件更新模式, 这可以通过长按重启按钮4秒以上来完成。这样做会让NXT运行一段很小的名为SAM-BA的启动辅助代码。 这段代码是NXT所使用的ARM芯片厂商Atmel所写的。SAM-BA中包含了USB驱动, 可以接收通过USB连接传递的指令。这些指令允许把数据上传到内存中并执行相应的代码。 早期的leJOS没有固件的版本,那时就是通过这样的机制来让leJOS得以在内存中执行。 最新版的nxjflah会自动把NXT置于固件更新模式。

在Windows或MAC OS X上,可以使用标准的LEGO USB驱动。这个驱动被包含在 LEGO的随机软件中,因此这些软件必须被安装。在Windows中,当NXT处于固件更新模式, 用USB电缆把NXT与PC机相连并打开NXT(按橙色的按钮),这时就可以在控制面板>系统> 硬件>设备管理中看到在 “Lego Devices”中有一项名为 “LEGO MINDSTORMS NXT Firmware Update Mode”的USB设备。

nxjflash使用libusb库来驱动USB接口。在Linux中,libusb提供了USB驱动。 在Windows中,你需要运行libusb-win32驱动。这样就可以让nxjflash来操作 LEGO MINDSTORMS NXT Firmware Update Mode的USB驱动。

nxjflash通过David Aderson的libnxt库来调用libusb。这样做可以支持SAM-BA指令。 在刷新一个新的固件的时候, 它把固件的镜象一次上传到一个256字节的页中并执行一段常驻内存的程序来把这个页写入闪存。 通过这样的方法,就可以把leJOS NXJ固件的lejos_nxt_rom.bin写入闪存了。 这个固件会占用闪存中前32K的空间。

nxjflash也会上传leJOS NXJ的启动菜单镜象StartUpText.bin。 这个菜单跟别的leJOS NXJ的程序一样,也是用Java写成的。 它实现了leJOS NXJ的菜单系统并支持执行通过USB或蓝牙传输的LCP指令。 StartUpText.bin驻留在闪存32K开始的48K空间中。当固件和菜单程序被上传以后, nxjflash会发送一个SAM-BA指令让NXT跳传到0字节地址执行代码, 这样leJOS NXJ固件就运行起来了。

返回顶部

nxjupload

nxjupload用于通过USB或蓝牙上传程序或其它文件。 它的命令行接口可以用于在命令行窗口、脚本或类似于Eclipse这样的集成环境中做为命令来调用。

nxjupload通过向NXT发送LCP系统指令来上传文件。 这些指令分别是 OPEN_WRITE, WRITE 和 CLOSE。

nxjupload的命令行使用如下的形式:

  • nxjupload [options] <binary-file>

默认情况下nxjupload会通过USB寻找NXT设备。如果没找到,它会尝试用蓝牙。 它通过查询蓝牙设备来寻找NXT设备。如果找到了一个或多个NXT设备, 它会尝试去连接每一个设备并把文件上传到第一个连接成功的设备上。 这也就意味着如果你有多个NXT设备,nxjupload会把文件上传到那个当前处于开机状态的设备上。

这个默认行为可以通过下面的参数来改变:

  • -b or –bluetooth: 只尝试蓝牙

  • -u or –usb: 只尝试USB

  • -n or --name: 上传到指定名称的NXT。如果查询蓝牙设备,只查找指定名称的设备。

  • -d or –address <address>: 文件上传到指定的蓝牙地址。不尝试USB,不进行蓝牙设备查询。

  • -r or –run: 通过发送 STARTPROGRAM LCP指令,在上传完成后运行上传的程序。

nxjupload使用Apache Commons CLI库来处理命令行。

返回顶部

nxjlink

nxjlink用于调用链接器(js.tinyvm.TinyVM类)。

命令格式如下:

  • nxjlink [options] class –o <binary file>

-cp或--classpath选项用来指定链接器寻找类的位置。要注意链接器使用的类路径与用于执行 js.tinyVM.TinyVM的所使用的路径并不相关。

链接器首先会在类路径中寻找指定的类,然后会寻找这个类所引用的所有其它的类来形成一个“闭包”。 链接器的类路径中应该包适classes.jar和所有用户程序中的类。 命令行上所指定的类必须包含main方法。nxjlink程序使用Jakarta apache的 Byte Code Engineering Library (BCEL)来处理类文件。

链接会忽略没有被引用到的方法,除非指定了–a 或 –all参数。所用的算法很简单, 所以并不能保证会忽略掉所有没有被引用到的方法。

链接器会产生leJOS NXJ的二进制可执行代码并输出到–o参数所指定的文件。

nxjlink需要知道它所生成的二进制代码的所应该采用的字节序。对于NXT来说, 字节序是通过 “–writeorder LE” 来指定的Little-Endian字节序。

如果设置了–v或–verbose参数,就会在标准输出上输出二进制代码中所有的类和方法的列表。 对于调试来说,这个功能非常有用。

nxjlink使用Apache Commons CLI库来处理命令行。

nxj

nxj命令用于链接和上传程序文件。它的命令行参数与nxjlink相同。

nxjc

这个命令调用编译器。它接受与标准Java编译器相同的命令行参数。

返回顶部