The boot ROM expects a connection at 9600 baud, 8 data bits, 1 stop bit, no parity, with software (XON/XOFF) flow control. If you start a terminal application, you can probe the device to see if communications are working. Here's the basic flow:
- You reset the device and assert ISP_Enabled. (Without any user code on the IC, it'll default to the boot ROM w/o having to assert ISP_En.)
- The host (PC) sends '?' repeatedly until the CPU responds with "Synchronized<CR><LF>".
- The host responds "Synchronized<CR><LF>". The CPU should respond "OK<CR><LF>".
- The host then sends the actual system clock frequency, in kHz, as a decimal number: "12000<CR><LF>". The CPU should again respond "OK<CR><LF>".
- You are then free to start throwing commands at it. For example you can turn echo off, or change the baud rate and stop bits to your preference, query the part ID, unique ID, and boot ROM version, or start reading / writing memory.
Now, the docs say you must terminate lines with CRLF, and any spurious CRs and LFs will be ignored. It also says the CPU will use CRLF to terminate its lines, however, it seems like I only get CR from the device. That
could be something the kernel is doing, but you might expect to deal with CR and/or LF as a matter of course.
To start poking at flash, you need to first unlock flash access. This requires a code (presumably to prevent line noise from inadvertently sending an unlock command) that you have to get from your part's data sheet. Flash sectors must also be prepared for writing (basically like turning off a per-sector read-only flag). Then, you can erase them, copy from RAM to flash, etc. All flash access is based on sectors, starting at 0. Flash sectors are not all uniform in size. It's common to see e.g., 4KB sectors for the first few, then 32KB sectors for the rest. Check the data sheet.
RAM access is by address rather than sector, and must be word-aligned, or divisible by 4. There's no way to write directly to flash, so you must pre-stage writes in RAM first. You then copy from RAM to flash.
There are commands to check flash sectors for blankness (to avoid gratuitous erase cycles), and compare memory (e.g., RAM and flash to confirm valid contents after a write).
All data transfers are UUencoded, resulting in a conversion from 3x 8-bit data bytes to 4x 6-bit chars. If you have less than three bytes remaining, pad them with 0x00. The resulting chars are shifted into the 0x21-0x60 character space to avoid conflicting with non-printable control characters (like ESC or XON/XOFF). Do this by taking the value and adding 0x20,
except if the char's value is 0x00 -- then you add 0x60.
Data is sent in lines of up to 45 data bytes (60 chars) with the length of the line (number of 8-bit data bytes) converted to an 8-bit integer, and shifted up by 0x20 (again, to be a printable character.) This "length" byte is the first byte in each line, immediately followed by the 60 UUencoded characters, and then CRLF.
You can send up to 20 lines at a time, then you must send a checksum. To generate a checksum, simply add the values of each incoming data byte as it's encoded. The resulting decimal value (byte[0] + byte[1] + ... + byte[44], for each line, for 20 lines) is sent as an ASCII number (e.g., "25140") followed by CRLF. You should then get "OK<CRLF>" back from the MCU, or "RESEND<CRLF>" if the checksums don't match.
When you reach the end of the data, your last group will potentially be less than 20 lines, and less than 45 bytes on the last line. Send a final checksum and watch for "OK<CRLF>". You tell the boot ROM in advance how many bytes you're going to send, so there's no ambiguity.
Receiving data works the same way, but in reverse.
I'm attaching the code I'm working on. It's incomplete, and I'll probably change parts of it along the way, so don't take it too seriously yet. However, it does work so far as establishing communications, and fetching IDs and such. I'm working on the memory access parts now.
Quick-start:
# gcc -o serial uuencode.c intelhex.c unix.c unix-cmds.c
# ./serial -p /dev/ttyUSB0 -c 12000 --verboseFor help:
# ./serial --helpEDIT:
REV 10: Fixed some issues and cleaned up main() a little. Added revision to version info. (./serial --version)
REV 11: Fixed some bugs, added line encode/decode to UUencode library, added Intel Hex library, added tests.