This page contains a collection of tips for using Parallax's new Javelin Stamp. Most of the tips here are in the form of code snippets that represent common activities.
The Timer VP and the timeout function can be used to create periodic actions.
public static void main() { Timer t = new Timer(); // Create a timer VP. t.mark(); // Take a snapshot of the current time. while ( true ) { /* Do other activity here. */ /* * Check if our time has expired. The * time is measured in milliseconds. */ if ( t.timeout(2000) ) { /* Do the periodic action. */ // Reset the timeout so it fires again. t.mark(); } } }
If you look in the source code for the Javelin libraries you will notice quite a few methods which are marked "Internal method. Do not use". What are these methods for and why can't you use them?
These methods are usually Java native methods. That means that they are implemented in the Javelin Stamp's firmware. Usually the functionality of these native methods is wrapped in a set of Java methods which are more powerful, easier to use, or perform safety checks.
For example, in the CPU.java file is the message() native method. This method is used to send messages from the Javelin Stamp to the terminal window in the IDE. All of the message printing is done by the System.out.print... methods which internally use the CPU.message() method.
Should I use internal methods in my Java programs? Probably not. They might change in future versions of the Javelin Stamp whereas, the library classes are less likely to change. In addition, some internal methods might have undocumented restrictions on how they should be used and so you might be introducing subtle bugs into your programs.
When sending data over a serial link a common problem is keeping the two endpoints in sync. One way to keep the communication synced up is to send data in small packets. The packet boundaries can be used to reset syncrohization when it is lost. The following code is an example Javelin class which can do packetized serial communication.
import stamp.core.*; /** * Example routines for doing serial communication. * * This code is based on the ideas in SLIP and PPP which use special * characters to mark the start and end of packets and byte stuffing * in case the data contains these special symbols. * * For example, the data: * 0x01 0x02 0x03 0x7F 0x7E 0x04 * will generate: * 0x7F 0x01 0x02 0x03 0x7E 0x80 0x7E 0x81 0x04 0x7F * * * @version 1.0 6-25-02 * @author Chris Waters */ public class SerialProtocol { final static int ESCAPE_CHAR = 0x7E; final static int FRAME_CHAR = 0x7F; public byte packet[]; public int length; boolean inPacket; boolean escaped; /** * Create a serial protocol encoder/decoder. * * @param maxPacketLength the maximum size of packet that this * code should handle. */ public SerialProtocol(int maxPacketLength) { packet = new byte[maxPacketLength]; length = 0; inPacket = false; escaped = false; } /** * Accumulate a packet by accepting bytes. It is up to the caller * to extract the bytes from the Uart. When a packet is received * the caller should immediately read it out of thepacket
* array. * * @return returns true when a complete packet has been received. */ public boolean receiveByte(byte b) { if ( b == FRAME_CHAR ) { // Toggle the packet state. inPacket = !inPacket; // If we just finished a packet then let the caller know. return !inPacket; } else if ( !inPacket ) { // If we are not in a packet then just ignore the input. return false; } else if ( b == ESCAPE_CHAR ) { escaped = true; return false; } if ( escaped ) { b = (byte)~b; escaped = false; } if ( length < packet.length ) { packet[length++] = b; } else { // If the packet is too long then just discard it and reset the // state machine. escaped = false; inPacket = false; length = 0; } return false; } /** * Send a byte-stuffed packet over the Uart. * * @param data the data packet to send. * @param dataLength the number of byte fromdata
that * should be transmitted. * @param txUart the Uart to send the bytes over. */ public void sendPacket(byte data[], int dataLength, Uart txUart) { // Send the start of packet marker. txUart.sendByte(FRAME_CHAR); for ( int i = 0; i < dataLength; i++ ) { byte b = data[i]; // Do byte stuffing if necessary. if ( b == ESCAPE_CHAR || b == FRAME_CHAR ) { txUart.sendByte(ESCAPE_CHAR); b = (byte)~b; } // Send the byte. txUart.sendByte(b); } // Send the end of packet marker. txUart.sendByte(FRAME_CHAR); } }
Java respects strict scoping rules. You can only refer to a variable when it is "in scope", this means: within the block that the variable is declared in.
The debugger is also aware of these scoping rules an will only display the value for a variable when that variable is in scope, or if it is in scope in a function in the call stack.
If you step your program past the point where x and y are assigned values the debugger will show you what these values are.
Debug messages sent from the Javelin conform to the following pattern:
0x05 0x03 <messagedata> 0x7E
<messagedata> is the sequence of bytes being sent by the CPU.message() native method. It is byte-stuffed. This means that if <messagedata> contains a byte <b> that has the value 0x7F or 0x7E then that byte is replaced with 0x7F <b>^0x20. i.e. 0x7F followed by the orginal byte xored with 0x20.
Terminal characters are sent to the Javelin using the following algorithm:
After the Javelin is reset it send 0x0F followed a pause followed by 0x50 (the version of the Javelin firmware).
Running the following simple program:
public class HelloWorld { public static void main() { System.out.println("Hello World"); } }
will generate the following on Sout:
0F 50 05 03 48 65 6C 6C 6F 20 57 6F 72 6C 64 7E 05 03 0D 0A 7E 05 05 7E
Note that the sequence 0x05 0x05 0x7E indicates that the Java program has ended.
The Javelin linker is what takes the .class files generated by the compiler and turns them into the binary image that is downloaded to the Javelin Stamp. The linker is class based. This means that if you use any method within a class, the entire class will be included. However if you use the same class more than once that class will only be included once. For example, if you call CPU.writePin() from Foo.java and from Bar.java, only one copy of the CPU class will be downloaded to the Stamp.
There is a tradeoff between putting more methods into a smaller number of classes, or having many classes with fewer methods. With fewer methods per class there are probably fewer unused methods being linked, but each class has some additional overhead. Generally however, fewer methods per class will result in smaller code (and is probably more likely to give your code better structure anyway).
The compiler and linker are clever enough to only compile and link code that is used. So for example, if your program doesn't refer to any LCD classes at all then non of the LCD code will be compiled or linked. Therefore there is no problem with have as many .java files as you like in a directory or in your lib directory tree.
Do you have any more tips or tricks that should be published here? Let me know: chris@waters.co.nz.