C program to write to the com port

Garrett Cooper youshi10 at u.washington.edu
Sun Jul 31 15:39:00 GMT 2005


Paul Hamilton wrote:

>Hi,
>
>I am trying to write a C program that will send 3 bytes to the cuaa0 com
>port at 9600 baud, 8n1.  I am trying to control a Northlight 8 Channel Servo
>motor controller:
>http://home.att.net/~northlightsystems/DMX512toRCservo.htm
>
>Most of the code came from this page:
>http://www.easysw.com/~mike/serial/serial.html
>
>Here is what I have so far:-
>-----------------------------------------------------------------------
>
>    #include <sys/time.h>
>    #include <sys/ioctl.h>
>    #include <errno.h>
>    #include <fcntl.h>
>    #include <termios.h> /*Originally it was termio.h*/
>    #include <stdio.h>
>    #include <unistd.h>
>    static char *opt_comport="/dev/cuaa0";
>
>int main(int argc, char **argv)
>{
>    int n;
>    int dcf_dev;
>    int sdata =  0xFF0090;  // sync byte, address, servo value to be sent to
>the Servo Controller
>    struct termios options;
>
>    // ok, lets try opening the com port
>    printf("Opening Com port: %s\n\n", opt_comport);
>    if((dcf_dev = open(opt_comport, O_RDWR | O_NOCTTY | O_NDELAY)) < 0) 
>      {
>         printf("Problems opening %s\n", opt_comport);
>         return (-1);
>      }
>    // set the required com port parrameters
>    options.c_cflag &= ~CSIZE;  /* Mask the character size bits */
>    options.c_cflag |= CS8;     /* Select 8 data bits */
>    options.c_cflag &= ~PARENB; // set no parity
>    options.c_cflag &= ~CSTOPB; // set 1 stop bit
>    options.c_oflag &= ~OPOST;  // Raw output
>
>    tcgetattr(dcf_dev, &options);
>
>    /*
>     * Set the baud rates to 9600...
>     */
>    cfsetispeed(&options, B9600);
>    cfsetospeed(&options, B9600);
>
>    /*
>     * Enable the receiver and set local mode...
>     */
>    options.c_cflag |= (CLOCAL | CREAD);
>
>    /*
>     * Set the new options for the port...
>     */
>    tcsetattr(dcf_dev, TCSANOW, &options);
>
>    // ok, lets transmit our 3 bytes to com port 1
>    n = write(dcf_dev, &sdata, 3);
>    if (n < 0)
>    fputs("write() of 3 bytes failed!\n", stderr);
>    printf("Output status: %d bytes written out\n", n);
>    exit(1);
>};
>
>-----------------------------------------------------------------------
>
>Now I am just a beginner at C code, so I feel pretty good getting this far
>(hey, it compiles :-)  However, a miss is as good as a mile, and so it
>doesn't work :-(   Having said that, I have a serial port LED breakout
>device watching, and I can see a blip on the TX line when I run the compiled
>program.  This is just meant to be test code, i.e.. Get it working before
>cleaning it up etc :-)
>
>I have tried connecting the computers serial port to another one, running:
>'cu -s 9600 -l cuaa0' but I don't see anything.  Having said that, I don't
>see anything if I run the same on the other PC (yes, the TX-RX lines are
>swapped over), so maybe that is a problem with my serial cable between the
>two computers.
>
>The Servo Controller only needs two wires:  signal ground and TX  so not
>much to go wrong there, and as I said above, I do see a blip on the TX LED
>when I run the program.
>
>
>Questions:
>
>1. Am I really sending the data correctly at 9600baud, 8n1?
>
>2. Am I really sending the hex bytes:  FF 00 90  out (or am I sending an
>pointer address)?
>
>3. What am I missing?
>
>
>Thanks.
>
>Cheers,
>
>Paul Hamilton
>  
>
A few things I'm curious about (and thanks for the webpage-it might be 
helpful for me in the future with programming robots and Tern boards for 
coursework):

Is options.c_cflag set to a default value or is it unknown prior to 
runtime? This would cause issues with your &= statement being the first one.
Another thing I noticed from the website is that you actually have to 
send information to the board itself, then you have to send information 
onto the servo, as to what channel on the controller you want to send 
the information to. So, you need to specify somehow via a Hex address 
what individual address on the controller you want to send the data in 
your message, then send the data that way. That requires some research 
of course and it looks like you tried to get that working, but the value 
that you input may differ. Plus, the servo value defaulted to the 1st 
address on the controller (as per the 1st webpage), so what I would do 
is iterate through channels 1 to 8 (actually iterate from 0 to 7 though 
as this is C and not something like Matlab..) to find which controller 
is actually controlling your servo, and then you have a match.
Also, are there any reserved COM addresses that you need to watch out 
for on the controller board (Yes, there are. I answered my own question 
later on...)? The Tern TD-40 board that we use at school has several 
addresses which are reserved for serial communication with the PC 
(output ports), so we have to use a set of addresses at a particular 
offset in order for the program we make to work with certain resources 
on the board. I assume something similar takes place with your controller.
When looking at the information, keep this in mind (referring to the 
documentation on the 1st webpage):

To send a new output level command, 3 bytes are needed.
The first is a “sync” byte with a value of 255. (ok, FF)
The second byte is the servo address. It is a number from 0 to 254. 
(Using 0B as an offset, 0C = 1, 0D = 2, etc up to 14 = 8 in hex)
The third byte is the actual position data, between 0 and 254. (must be 
able to count from the max distance in steps of 1/255-note that 50% = 
128 = floor(255/2))
This sequence is followed for each servo.

For example, as shipped the Serv8 board address is 1. The output 
channels are addressed 1-8. Simple enough, For servos 9 - 15 the decoder 
board address would be set to 9. Channel 3(on the board) would be 11 to 
the controller, the board address plus the channel # - 1.
To change the level of a channel 3 to 50%, three bytes must be sent.
Sync , Address , Posn .
255 , 3 , 128

So it looks like what you need to do is take into consideration that the 
first 8 pins are reserved for output, such that you want an address of 
0xFF0B90 in order to get your expected behavior. And like Giogios 
mentioned, this value most likely can't be expressed as an int. Just 
some food for thought, create this simple program to determine how large 
an integer is:

/*
* A simple program to output the sizes of an ANSI based C unsigned int 
and unsigned long on the specific architecture under test.
*/

#include <stdio.h>

int main() {
printf("An unsigned int is this big in bytes: %d, and an unsigned long 
is this big in bytes: %d.",sizeof(unsigned int),sizeof(unsigned long));
return 0;
}

But yeah, most likely have to use an unsigned long, because an unsigned 
int can go a max of 32 bits on (most) 32-bit archs, which would just! 
allow for all 32 bits of the hex address to be output to the COM port 
without overflow.

A lot of different info was suggested in this, so I hope this helps you 
get started on solving your problem. Take care!
-Garrett


More information about the freebsd-questions mailing list