|
|
|
As a taste of the problems using Java in this environment: to dump the MCU registers up the RxTX I need to
send the 68HC11 an 81(hex). The 68HC11 always echos the command as a 2s complement so I want to check the
echo for 7E(hex). It would be nice if I could define the command as [byte mcuRegDump = 0x81;] and the
check the response against [byte mcuRegDumpAck = 0x7E;] but the Java compiler insists I use
mcuRegDump=-127 and mcuRegDumpAck=126...sigh.
Talker Properties
The talker is a very small program (TalkJF1 is only 169 bytes) but it is able to read and write memory and
all device registers. Using the SWI (Software Interupt Instruction) is is able to stop program execution
change the 68HC11 state and resume execution. This gives the talker complete step-by-step control over
what happens on the chip.
Some very cleaver people built this into the design of the 68HC11 and that was
a major reason for its popularity. It is the basis for JTAG and the SWIM capabilities of the STM chips.
The Motorola "Standard" Talker Uses 5 Commands
$81 - read MCU registers 12 bytes (-$81,stackpointer,CC,B,A,IX,IY,PC)
$C1 - write MCU registers-send 9 bytes: [CC, B, A, IX, IY, PC] to MCU
$01 - read a memory block (1-256 bytes)
$41 - write a memory block (1-256 bytes)
$4A - allow a test program to continue from a breakpoint (SWI)
...all other "commands" just echo the 2s complement of the byte
"Inherent" Commands (high bit set)
$81 [1000 0001] - Transmit MCU registers to PC (12 bytes total)
...returns:
[7E][STKH-STKl,CC,B,A,IXH-IXL,IYH-IYL,PCH-PCL]
$C1 [0011 1110] - Overwrite MCU registers with bytes read from PC
...returns [3E] acknowledge...now send 9 bytes to the 68HC11:
[CC,B,A,IXH-IXL,IYH-IYL,PCH-PCL]
These are the registers saved on the stack during an interrupt so
the MCU is forced to do a fake interrupt which pushes 9 bytes on
stack. The talker then sends to the PC: the 2s complement command, the location
of the stack, and the 9 bytes read off the stack. These should be a "frozen"
picture of the MCU state.
"Memory" Commands (high bit clear)
[command] [count] [addHigh] [addLow] [data bytes]
where [count]...................0000 to 1111 (0=256 bytes)
[addHigh][addLow]...16 bit address
[data bytes]............[count] bytes to write into memory
$01 [0000 0001] [FE] then [count] bytes from memory
$41 [0100 0001] [BE] then send [count] bytes (hangs if not enough sent)
Breakpoint Control of Programs Under Debug Testing
Debugger (IDE) software on the PC inserts a one byte SWI instruction
into the memory location just before a "beakpoint". How the IDE deals
with added bytes in the machine code is often a trade secret. It may
dynamically re-assemble below the SWI, replace/restore bytes displaced by
the SWI, or just re-assemble and reload the entire program on every change.
When the SWI is encountered the 68HC11 transmits a $4A (BRKCODE) to the
host PC indicating a breakpoint has been reached then loops at SWIIDLE
waiting for a reply.
A byte from the PC causes an interrupt...escaping the SWIIDLE loop
and branching to SWISRV1.
If the PC sends a $4A (BRKACK) the 68HC11 falls through
...RTI returns to test program.
If the PC does not send a $4A (or a valid [command]) the 68HC11:
...transmits [addHigh][addLow] of the breakpoint address
...transmits a copy of the MCU registers (9 bytes)
...the [PCL] byte is left in the SCI buffer for the test program to read
If the PC sends a valid [command] (not a $4A) the 68HC11 will process
{$81, $C1, $01, $41} ...then RTI back to the test program
Java and Microcontrollers
Java is a terrible language to use with microcontrollers...it (and its Microsoft C# variant) is however
an extremely popular language for web applications. That means most who read this will be Java programmers.
Realms have their favorite languages. Physics (astrophysics, plasma physics, meteorology) and enginering (civil, structural)
are [large data set] [compute intensive] and rely on weakly typed languages like C and FORTRAN (for..what?). Most
of the worlds financial systems still run COBOL (co..what?) and high frequency trading code is all C++. Games,
operating systems and AI are also C++ realms. If you work in those fields and only know Java you'll get to write the
room booking app or the bulk emailer.
For microcontrollers the code that runs inside the chips is almost always created by an assembler, C or C++.
As a result the tools (written by the same people) that support
the environment are written in C or C++.
C is "weakly typed" which means a block of data can be a string or an array of
bytes or an array of floating point numbers or an IP address at different places in the code. This makes
communications easy because a message may have many data types packed into a single compact stream of bytes. It can
also make for very challanging debuging when a URL location is accidently used as floating point GPS location.
Java can be used with micronctrollers but the code looks weird. There are no good ways to cast one data type into
another so basic strategy is to pass raw data through a print formatter (that doesn't care about type) then parse
it out as a string. When that doesn't work you have to figure out how Java actually stores its objects and trick it
into having the right bytes in the right place. This makes the code hard to read.
For example to get the MCU registers you need to send 81(hex) to the 68HC11. If talker is in a "sane" state it will echo
the 2s complement of the command 7E(hex) then the binary content of 6 registers; some that are single byte
(like the condition codes CC) and some are two byte (like the program counter PCH-PCL). The 81(hex) is an "Inherent"
command because the high bit is set. The high bit is critical to the flow of the talker program during execution.
If Java let you [byte mcuRegDump = 0x81;] it would be clear what is happening. Java has no unsigned
data type so setting the high bit in a byte is not allowed. What you must do is [byte mcuRegDump = -127;] because the
- sign sets the high bit and 128-127=1 set the low bit. That gives you [1000 0001] which is 81(hex).
Lying to the compiler to get it to do what you want is poor programming practice. The example code that follows is
full of such poor parctices.
Talking to the Talker
The standard Java IO libraries are meant to talk to file systems and object orientated display drivers. They have
clumsy with low level device interfaces (although I understand this is getting better). Long ago the
standard libraries were abandoned and for a specialized serial I/O libary. The best replacement
is RXTXComm.
In the zip archive you will find both 32 bit and 64 Windows versions. Create a C:\RXTXcomm and RXTXcomm.jar into it.
Copy the rxtxSerial.dll (and the rxtxParallel.dll) to the \bin path in you Java development installation. My examples
use 6.22 on 64 bit Windows 7 so the dll should be in C:\Program Files\Java\jdk1.6.0_22\bin\rxtxSerial.dll.
If you don't have a Java development environment can get the "Java SE Development Kit 6u22" from
1.6_22 JDK.
The current version of the Java Standard Edition (SE) Development Kit (DK) is 1.8_40 (I actually develop in 6.22, 7.03 and
8.11) and my examples should run fine under Java 8 but they are certain to run under Java 6. You can find all versions of
the SDK in the Oracle arachive.
If you don't use 1.6_22 you'll need to change the .cmd to reflect which JDK you are using.
The Java Samples ZIP contains four programs:
TwoWaySerialComm.java...example from the RxTx Project pages
TalkerJF1.java.................reads all the assets from the 68HC11
Voltmeter.java.................reads A/d samples one at a time from the PC
Voltprog.java...................load a volts.s19 into the 68HC11 for fast A/D
For running TalkerJF1.java and asking for the device registers displays:
(see the TalkerJF1x64.log)
+-------------------------------------------+
|Commands: |
| ^C kill this run |
| F to send eight FFs (attempt to resync) |
| |
| R dump the MCU registers |
| G dump all I/O registers {A,B,C,D,E,G} |
| |
| E dump the 512 byte onchip EEPROM |
| M dump the 1024 byte onchip RAM |
| |
| H display this help message |
+-------------------------------------------+
r
Sending to MCU < 81 >
MCU Registers:
03 F7 40 2C 40 10 00 FF FF FE 12
Stack CC B A [IX ] [IY ] [PC ]
g
Sending to MCU < G >
GIO Ports accessed via 96 bytes starting at 1000(hex):
------------------------------------------------------
+0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F
1000 05 00 4B 00 00 00 00 00 23 00 40 00 00 00 B1 CC
1010 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
1020 00 00 00 F8 00 C0 00 00 04 00 00 30 00 2C 10 00
1030 80 00 00 00 00 1F 4F 4F 20 10 4F 00 05 01 00 FF
1040 4F 4F 4F 4F 4F 4F 4F 4F 4F 4F 4F 4F 4F 4F 4F 4F
1050 4F 4F 4F 4F 4F 4F 4F 4F 4F 4F 4F 4F 00 04 00 07
Selected Ports and Direction Control:
-------------------------------------
Port A (1000): 05(hex) 00000101
..DDRA (1001) direction (in=0, out=1) 00000000
Port B (1004): 00(hex) 00000000 output only
Port C (1006): 00(hex) 00000000
..DDRC (1007) direction (in=0, out=1) 00000000
Port D (1008): 23(hex) 00100011
..DDRD (1009) direction (in=0, out=1) 00000000
Port F (1005): 00(hex) 00000000 output only
Port G (1002): 4B(hex) 01001011
..DDRG (1003) direction (in=0, out=1) 00000000
Port E (100A) may be used for binary input or as analog input for A/D
---------------------------------------------------------------------
Port E (as binary): 40(hex) 01000000 [Do not read E during A/D]
If Port E was used for A/D:
---------------------------
A write to ADCTL starts A/D conversion
ADCTL (1030): 80(hex) 10000000
CCF (bit 7) = 1 (1=Conversion complete)
not used (bit 6) .
SCAN (bit 5) = 0 (0=do 4 samples & stop, 1=continuous)
MULT (bit 4) = 0 (0=use single channel, 1=use 4 channels)
CD (bit 3) = 0 (4 channels...0=use {0123}, 1=use {4567})
CC/CA (bits 2,1,0) = 000 (1 channel...read from this port E pin)
A/D data registers
------------------
ADR0 (1031) data for A/D channels 0 & 4 = FF(hex) 11111111
ADR1 (1032) data for A/D channels 1 & 5 = 00(hex) 00000000
ADR2 (1033) data for A/D channels 2 & 6 = 00(hex) 00000000
ADR3 (1034) data for A/D channels 3 & 7 = 00(hex) 00000000
Voltmeter.java writes a [0000 0000] [do 4 samples and stop, use channels 0..3) to the ADCTL (1030hex)
register to trigger a A/D conversion on channels 1..4 written into registers ADR0..ADR3. It then does
a memory read [01]hex on [1] byte from address [10][31] then converts the 8 bit data byte and reports
it as the actual 0.0V to 5.0V range.
With a 1 volt AC applied to pin 59 (AD0) the program provides a continous report of voltages
(see the Voltmeter.log):
|