Channels ▼

Al Williams

Dr. Dobb's Bloggers

Port of Call

February 10, 2012

You can go back to blocking mode with a call to fcntl. I decided to leave it nonblocking, but I left the code in to block and do timeouts on blocking reads since the timeout settings are in a data structure I had to change anyway:


//  fcntl(fd,F_SETFL,0);    // <<<<<--------- <<<<< Go back to blocking mode
  tcgetattr(fd,&oldtio); /* save old settings */
  memset(&tio,0,sizeof(tio));
  // Set up for 8N1, no processing */
  tio.c_cflag=BAUDRATE|CS8|CLOCAL|CREAD|CRTSCTS;
  tio.c_iflag=IGNPAR;
  tio.c_oflag=0;
  tio.c_lflag=0;
  for (i=0;i<NCCS;i++) tio.c_cc[i]=0;
  tio.c_cc[VMIN]=1;
  tio.c_cc[VTIME]=10;  // 1s timeout
  tcflush(fd,TCIFLUSH);
  tcsetattr(fd,TCSANOW,&tio); // make settings take  

Don't forget the fctl call to return to blocking mode is commented out. The c_flag member sets the baud rate (which is set by a #define not shown here), the number of bits (8), and the hardware handshaking (CRTSCTS).

The read function is straightforward, other than handling the non-blocking behavior:


unsigned int gp3read(void)
{
  char c;
  unsigned int rv;
  int ct;
  do {
    ct=read(fd,&c,1);
    if (ct==-1) QApplication::processEvents();
  } while (ct!=1);
// since c is char should be unnecessary
  rv=((unsigned int)c) & 0xFF;
  DEBUG("read",rv);
  return rv;
}

If there's no data available, the routine calls the Qt event loop so that the user interface doesn't lock up. Unfortunately, I didn't add any time out code. If the board just dies and never sends any data, the program's user interface won't lock up, but nothing will work. In this particular case, if the board is dead the program isn't really useful anyway so I decided that was sufficient.

The rest of the serial port code isn't anything special. I wrote another C++ class to model the actual board. It uses an unusual protocol that allows the board to synchronize in the event that it resets in the middle of a data stream or if the PC resets in the middle of sending a command.

Command packets to the board have either one or two bytes. The one-byte packets and the first byte of a two-byte packet always have their most significant bit set to zero. As you might guess, the second byte of a packet always has the most significant bit set to one.

The problem, of course, is that you lose one bit in each byte. For the first byte, it doesn't matter because it is primarily a command byte and there are only a few commands. For the second byte, the chip assumes you've shifted the byte over one place to the right. The low bit of the second byte appears in the sixth bit of the first byte. For example, if you want to send (in hex) 05 03 to the board, you'd have to transform the two bytes using the above rule into 45 81. This is easier to see in binary:


05 03       0000 0101  0000 0011
45 81       0100 0101  1000 0001

The gp9.cpp file defines an abstraction for the board that hides the low-level interface and the protocol. Here are a few excerpts:


bool gp9::open()
{
    portopen=gp3openport(port.toAscii())!=-1;
    if (!portopen) error=true; else error=false;
    return portopen;
}

bool gp9::close()
{
    gp3closeport();
    error=portopen=false;
    return true;
}

void gp9::write(int byte)
{
    if (!portopen)
        if (!open()) return;
    gp3write(byte);
}
void gp9::write(int b1, int b2)
{
    if (b2&1) b1|=0x40;
    b2>>=1;
    b2|=0x80;
    write(b1 &0xFF);
    write(b2 &0xFF);
}

void gp9::setoe(int mask)
{
    write(0xD,mask);
}

int gp9::readpwm(int chan)
{
    write(0x20+chan);
    return read();
}

There's more, of course. You can download the whole thing online. I took the abstraction class and the serial port code and built a simple test program, and that's the project you'll find online. You can see a screen shot below

[Click image to view at full size]

Next time I'll show you inside the analogous Windows code and talk about what had to change in the Qt parts of the code (very little, actually). Keep in mind that although this is serial port code, the actual hardware was, in fact, USB. If you want to completely break the serial habit, you can do that too, and that will be an upcoming topic.

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 


Video