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