Date: Sun, 31 Jul 2005 08:38:14 -0700 From: Garrett Cooper <youshi10@u.washington.edu> To: Paul Hamilton <paulh@bdug.org.au>, freebsd-questions@freebsd.org Subject: Re: C program to write to the com port Message-ID: <42ECF066.5090808@u.washington.edu> In-Reply-To: <01cb01c595df$435ed060$6600a8c0@w2k2> References: <01cb01c595df$435ed060$6600a8c0@w2k2>
next in thread | previous in thread | raw e-mail | index | archive | help
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
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?42ECF066.5090808>