www.angelfire.com/dragon/letstry
cwave04 at yahoo dot com

Reading/writing entire bytes

We are programming the microcontroller serieally. This means sending sending and receiving signals bit by bit. For example when the datasheet asks you to send 10100000 to the microcontroller this is what the timing diagram should look like:

Remember the analogy of SCK with a window. The window is closing and opening intermittently. It is open when SCK is 1, closed when 0. The microcontroller gets a glimpse of the MOSI line only when the window is open. Each glimpse counts as a bit.

Clearly, we shall often need to emit a positive pulse down the SCK line. We shall write a little function

void pulseSCK() {
  setBit(SCK,1,"SCK");
  setBit(SCK,0,"SCK");
}

We shall make a convention here: never to touch SCK (after initialising it to 0) outside this function. This will guarantee that SCK is 0 always outside this function.

Next we shall write a function to send a byte down the MOSI line. The function will accept the byte as a string (instead of as an unsigned char). Thus if we want to send the bit pattern 01010000 then we can directly write

sendByte("01010000");

rather than have to convert 01010000 to 0x50 first. This will reduce the chance of mistakes. The function is easy to write:

void sendByte(char what[]) {
  int i, check;
  
  inpByte = 0;
  for(i=0;i<8;i++) {
    setBit(MOSI,what[i],"MOSI");
    pulseSCK();
  }
}

Now we are going to add a little trick that will save lot of debugging effort later. It is an interesting fact (though not clearly documented in the datasheet) that the microcontroller has the habit of echoing back the last byte sent to it. Thus if the PC sends the byte 10101000 to it down the MOSI line, then in the next 8 SCK pulses the microcontroller will send the same bit pattern back via the MISO line. This is a great way to know that the microcontroller is alive and kicking. There are two situations when the microcontroller will not echo via the MISO line. The echoing will not be there during the first 4 bytes. Also the echoing is absent when the microcontroller wants to send some real data to the microcontroller.

To achieve this we shall keep a global variable

unsigned char lastVal;

Whenever we send a byte down the MOSI line we shall keep a backup copy of the value in this variable. This is done in the next function.

void bin2num(char binByte[]) {
  int val;
  int i;

  val = 0;
  for(i=0;i<8;i++) {
    val <<= 1;
    if(binByte[i]=='1') val |= 1;
  }
  lastVal = val;
}

We call this function from inside sendByte as follows.

void sendByte(char what[]) {
  int i, check;
  
  inpByte = 0;
  for(i=0;i<8;i++) {
    setBit(MOSI,what[i],"MOSI");
    pulseSCK();
  }

  check = (lastVal == inpByte);
  bin2num(what);
  if(verbose)
    fprintf(stderr,(check? "In synch\n" : "Out of synch\n");
}

Notice the word "synch" in the diagnostic message. Most communication failures between the microcontroller and the PC occur because of lack of synchronisation (e.g., missing a bit somewhere, and then getting confused with the rest). We shall talk more about synchronisation later in this tutorial.

We shall also need another version of the same function that accepts the input as an unsigned char. We shall do this in two steps. First we shall convert the byte into a string of bits, and then feed the string into the sendByte function. Here is the function to convert a byte into a string:

void num2bin(int num, char binByte[]) {  
  int i;
  int mask;

  for(i=0,mask=1<<7;i<8;i++,mask>>=1) 
    binByte[i] = ((num & mask) ? '1' : '0');
  binByte[8] = 0;
}

The following function first calls the above function and then sends the resulting string down the MOSI line via sendByte.

void sendByte1(int b) {
  char binByte[20];
  num2bin(b,binByte);
  sendByte(binByte);
}

You may very well be irritated by the ineffcient coding: first converting the number to a string and then (inside sendByte) converting it back to a number. There are two minor reasons behind this:
  1. We need to check the feedback of the microcontroller. So we need the lastVal variable. We have declared it as an unsigned char. So if the data byte is specified as a string, we need to convert it to an unsigned char before saving it into lastVal.
  2. Now, if we ever need to tweak the MOSI line communication later (for debugging/experimenting) it will help if the tweaking is done in a single function. In our implementation sendByte is the workhorse function, since sendByte1 also calls it. So tweaking sendByte would be enough, without touching sendByte1.
Reading a byte from the microcontroller does not need any extra work. We simply send 8 dummy bits (say all 0's) and pick up the inpByte which is produced from the bits coming down the MISO line.

unsigned char receive() {
  sendByte("00000000");
  return inpByte;
}


PrevNext
© Arnab Chakraborty (2008)