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

Controlling the pins independently

Notice that writing a single byte to the parallel port affects all its output pins. There is no direct way to control a single pin individually. However, such a facility would be of great use for us. So let us write some code for that.

We shall be using 3 output pins of the parallel port for 3 different purposes. The 3 pins may be any of the 8 output pins. In out program we shall use the pins D4,D5 and D7. We shall call them MOSI, SCK and RST. These are the names of the microcontroller pins that we shall hook them up with. We shall now learn how to control these 3 pins independently of one another. First we shall identify each pin by its mask:

#define SCK (1<<4)
#define MOSI (1<<5)
#define RST (1<<7)

Then we can easily turn on any given pin (say SCK) without affecting the others. for this we shall use a global variable

unsigned char outByte;

  • To make SCK equal to 1:
    
    outByte |= SCK;
    Out32(PPORT_OUT, outByte);
    
    
  • To make SCK equal to 0:
    
    outByte &= ~SCK;
    Out32(PPORT_OUT, outByte);
    
    
  • To toggle SCK (we shall not need this):
    
    outByte ^= SCK;
    Out32(PPORT_OUT, outByte);
    
    
We shall need to do this repeatedly so it will be a good idea to write a function

void outp() {
  Out32(PPORT_OUT, outByte);
  Sleep(10);
}

We shall soon put extra stuff inside this function.

Drawing the wave form

It will be important (but difficult) to keep track of the pins simultaneously. For example the microcontroller datasheet will often ask you to do something like this:
Make MOSI and SCK both 0, hold them for some time, now make SCK 1 and then after some time 0 again, then make MOSI 1 and hold it. While MOSI is 1, make SCK 1, then 0, then 1 and then 0 again. Now bring MOSI down to 0.
Does it sound mindboggling? Well, there is a visually appealing way to present the same steps and datasheets use it frequently. It is called a timing diagram:

To achieve this timing diagram we can use the following C code snippet:

outByte &= ~SCK;
outByte &= ~MOSI;
outp();
outByte |= SCK;
outp();
outByte &= ~SCK;
outp();
outByte |= MOSI;
outp();
outByte |= SCK;
outp();
outByte &= ~SCK;
outp();
outByte |= SCK;
outp();
outByte &= ~SCK;
outp();
outByte &= ~MOSI;
outp();

Do you see that it is going to difficult to keep track of things? A little mistake would send a wrong pulse, and yet it is difficult to locate the mistake.

Here is a useful device to automatically generate the timing diagram on screen as the pulses are emitted.

To draw the wave forms we shall use the function drawWave given below. Its working is pretty straight forward. Suppose that it gets a sequence 000111000, then it will print:

|
|
|
|___ 
    |
    |
 ___|
|
|

This ASCII art has one line per symbol in the sequence. The line for a symbol is determined by both the current symbol as well as the last symbol:

0 followed by 0 : |

0 followed by 1 : |___ 

1 followed by 1 :     |

1 followed by 0 :  ___|


#define ZO fprintf(stderr,"\t|___ ");
#define OZ fprintf(stderr,"\t ___|")
#define ZZ fprintf(stderr,"\t|    ")
#define OO fprintf(stderr,"\t    |")

void drawWave(int lastByte, 
              int currByte, 
              int mask) {
  fprintf(stderr,"\t");
  if(currByte & mask) {
    if(lastByte & mask) 
      OO;
    else
      ZO;
  }
  else {
    if(lastByte & mask) 
      OZ;
    else
      ZZ;
  }
}

and change the outp() function as follows.

void outp(char *rem) {
  int val;
  Out32(PPORT_OUT, outByte);
  delay();

  if(!verbose) return;


  fprintf(stderr,"!%4s: ",rem);

  drawWave(outByte,MOSI);
  drawWave(outByte,SCK);
  drawWave(outByte,RST);
  if(outByte & SCK)
    fprintf(stderr,(val ? "\t1" : "\t0"));

  fprintf(stderr,"\n");
}

Notice that we have a global variable called verbose. If it is false then no visual output is prodoced. It is very important to have such a control, otherwise you screen will be flooded by too much diagnostic output!

To use this function effciently it will help have the following function:

void setPin(unsigned char mask, unsigned char status, char *pinName) {
  if(status) 
    outByte |= mask;
  else 
    outByte &= ~mask;

  outp(pinName);
}

Now if we can produce the timing diagram given earlier by

outByte = 0;
outp("init");
setBit(MOSI,0,"MOSI");
setBit(SCK,1,"SCK");
setBit(SCK,0,"SCK");
setBit(MOSI,1,"MOSI");
setBit(SCK,1,"SCK");
setBit(SCK,0,"SCK");
setBit(MOSI,1,"MOSI");
setBit(SCK,1,"SCK");
setBit(SCK,0,"SCK");
setBit(MOSI,0,"MOSI");

This would now produce the following timing diagram on screen:

!init: |           |            |
!MOSI: |           |            |
!SCK : |           |___         |
!SCK : |            ___|        |
!MOSI: |___        |            |
!SCK :     |       |___         |
!SCK :     |        ___|        |
!MOSI:     |       |            |
!SCK :     |       |___         |
!SCK :     |        ___|        |
!MOSI:  ___|       |            |

It is much easier to compare this ASCII art with the timing digram than trying to match the code with the steps. This little trick would prove extremely helpful later.

You may be wondering why we have put an exclamation mark at the start of each line. This is because we may later need to print messages other than the timing diagram on the screen. In order to see the timing diagram alone we can then save the entire output in a file and use some filter (like grep or find) to extract only the lines starting with an exclamation mark.

PrevNext
© Arnab Chakraborty (2008)