 |
www.angelfire.com/dragon/letstry
cwave04 at yahoo dot com | 
|
|
|
Synchronisation
Microcontrollers depend on electronics, and like all manmade
gadgets are based on simplified assumptions about nature. Despite
one's best efforts, discrepancies exist between the expected and
the actual behaviour of a microcontroller. Their effects appear
to be random, or at least too complex to control
directly. Sometimes these may appear like manufacturing defects,
and so datasheets always try to downplay these. The datasheets
try to present a microcontroller as a perfect instrument,
and are silent about techniques to handle potential departures
from the ideal, documented behaviour. One has to find these by
trial-and-error.
In my experience there are two major ways microcontrollers can
behave badly during programming. Both are forms of
synchronisation problem.
-
Sometimes bits will randomly go missing during communication.
-
Program enabling is like waking the microcontroller from its
slumber. Now
microcontrollers are rather sleepy creatures, and tend to doze
off again
whenever they get a chance. This is especially true for the less
expensive
ones.
General strategy
The worst feature of synchronisation problem is that it is
random. So each attempt is somewhat like a coin toss: you either
succeed (HEAD) or you mysteriously fail (TAIL).
But if you keep on tossing the same coin again and again, then
the chance of getting at least one HEAD is large. This is
the crucial idea: keep on trying until you get the first
success.
Of course, this strategy won't work for programming bugs, which
are not random. But this works for synchronisation problems,
which are random in nature.
Solution to problem 1: knock, knock, knock...
As we have already warned you, you may not able to wake the
microcontroller up with a single call because of bits going
missing mysteriously. Then we just have to keep
on calling. So we shall make multiple attempts to
wake them up.
It will help to
have a little function that make multiple attempts to
wake them up. does this chore for you. It sends
at most 20 wake up calls, and gives up after that.
int progKick(void) {
int nTry;
for(nTry=0;nTry<20;nTry++) {
if(startProg()) break;
}
if(nTry<20) {
printf("Programming enabled after %d attempt(s).\n",nTry);
return 1;
}
printf("Programming couldn't be enabled even after %d tries!\n",
nTry);
return 0;
}
|
Solution to problem 2: reset whenever you can
Microcontrollers are rather sleepy creatures. They tend to doze
off again whenever they get a chance. This is especially true for
the less expensive ones (AT89S52 will, for example, give more
trouble than ATMEGA8515).
So every now and then you'll have to wake them up again. This
means calling the progKick function repeatedly.
There are a couple of
points that you must bear in mind:
- Every time you wake up the microcontroller it forgets some of things
you had told it earlier. So you must repeat these again. This is true even
if the microcontroller was actually awake when you sent it the "wake up"
signal. So you must be careful about timing your "wake up" calls. It would
help to consider the entire activity of programing as consisting of
critical chunks that must not be interrupted by any "wake up" call. Then
it is always a good idea to issue a "wake up" call between chunks.
- Sometimes it is good to be able to detect if the microcontroller is
awake or asleep. Though the datasheet does not mention any way to do this,
there seems to be one dirty trick that works. Look at the lock bits. These
become 1's whenever the microcontroller falls asleep.
We shall now ilustrate how to use these ideas.
Erasing, writing and reading
To program a microcontroller you have to first erase its memory,
then write your program bytes into it, and finally you should read back
what you've written (to verify that everything is OK). Page 20
of the datahseet gives you the bit sequencees to achieve
these. We present three simple functions that implement these.
Erasing
void chipErase(void) {
statMsg("Chiperase starts"); indent++;
unlock();
statMsg("Byte 1");
sendByte2("10101100");
statMsg("Byte 2");
sendByte2("10011111");
statMsg("Byte 3");
sendByte2("11111111");
statMsg("Byte 4");
sendByte2("11111111");
indent--; statMsg("Chiperase ends");
}
|
If you have carefully read page 20 of the datasheet, then the above
function should not be hard to fathom, except for the call to the unlock
function. The manufacturers have put a little locking
feature in the microcontroller to avoid accidental erasing. The
unlock function opens this lock. Comparing the following code
with page 20 of the datasheet should explain its working.
void unlock(void) {
sendByte("10101100");
sendByte("11100000");
sendByte("11111111");
sendByte("11111111");
}
|
Writing
Of course, before we can write to the microcontroller we must have
something to write! Our final aim will be to write the contents of a HEX
file. But for now we shall content ourselves by writing some random
bytes. These bytes will be stoed in a global array called
mem. This is the right moment to talk about the memory
organisation inside the microcontroller. The information is given in a
cryptically concise form in page 9 of the datasheet. We shall try to
present it in a more detailed way.
The microcontroller program memory is of size 4K. So it can store 4*1024 =
4096 bytes. Do not confuse this memory with the RAM, which is volatile (gets
garbled on power-down). The 4K memory is the FLASH memory (a kind of
EPROM, if that helps). This is where the microcontroller keeps its program
and all permanent data (like fonts, strings etc).
To address a single byte in a 4K = 212 byte space you need an address consisting
of 12 bits. It will help to group these 12 bits into two groups of 4 and
8:
The 4 MS bits comprise what we shall call the page number. The
remaining 8 bits give the address of the byte within the page. Clearly,
there are 24 = 16 pages each of size 28 = 256 bytes.
The AT89S52 microcontroller allows one to write a byte both in byte-mode
(where we use the full 12 bit address for each byte to be written) or in
page-mode (where first we specify the page number and then send the 256
bytes in the page). The page-mode writing is faster. So we shall go for
that.
int sendPage(int whichPage) {
int i,k;
int pageStart, result;
statMsg("sendPage starts"); indent++;
pageStart = whichPage;
pageStart <<= 8;
for(i=0,k=pageStart;i<256;i++,k++) {
sendByte("01000000");
sendByte1(whichPage);
sendByte1(i);
sendByte1(mem[k]);
}
Sleep(100);
indent--; statMsg("sendPage ends");
return 1;
}
|
Reading
The following two functions should be self-explanatory.
int readByte(int whence) {
statMsg("readByte starts"); indent++;
if(verbose & VERBOSE_MSG)
printf("whence = %d\n",whence);
statMsg("Byte 1");
sendByte2("00100000");
statMsg("page MSB 4");
sendByte1(whence>>8);
statMsg("page LSB 8");
sendByte1(whence&0xFF);
statMsg("Dummy");
sendByte("11111111");
indent--; statMsg("readByte ends");
return inpByte;
}
|
void verify(void) {
int pg, byt, bad, k;
for(pg=0,k=0;pg<16;pg++) {
bad = 0;
for(byt=0;byt<256;byt++,k++) {
startProg();
if(mem[k] != readByte(k)) bad =1;
}
printf(bad? "x" : ".");
}
}
|
|