5

I'm attempting to read and write data from and to an FPGA board. The board itself came with a driver that create a terminal device called ttyUSB0 whenever the board is plugged in. On the FPGA, an asynchronous receiver and transmitter were implemented, and they seem to work.

However, there seems to be an issue on C side of things. I've been using some test vectors to test if the FPGA is outputting the proper information. I've noticed a few things things:

  1. The device sometimes does not open correctly
  2. The terminal attributes sometimes fail to be retrieved or set.
  3. The read is sometimes non-blocking and doesn't retrieve the proper value.

Below is how I set up terminal and file descriptor options. Much of it was taken from here: http://slackware.osuosl.org/slackware-3.3/docs/mini/Serial-Port-Programming

Any advice or comments as to why the program may be failing would be greatly helpful.

#include <stdio.h>   // Standard input/output definitions
#include <string.h>  // String function definitions
#include <unistd.h>  // UNIX standard function definitions
#include <fcntl.h>   // File control definitions
#include <errno.h>   // Error number definitions
#include <termios.h> // POSIX terminal control definitions

int open_port(void){

    int fd;    // File descriptor for the port
    fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);

    if (fd == -1){
        fprintf(stderr, "open_port: Unable to open /dev/ttyUSB0 %s\n",strerror(errno));
        exit(EXIT_FAILURE);
    }

    return (fd);
}

int main(void){

    int fd = 0;              // File descriptor
    struct termios options;  // Terminal options

    fd = open_port();    // Open tty device for RD and WR

    fcntl(fd, F_SETFL);            // Configure port reading
    tcgetattr(fd, &options);       // Get the current options for the port
    cfsetispeed(&options, B230400);    // Set the baud rates to 230400
    cfsetospeed(&options, B230400);

    options.c_cflag |= (CLOCAL | CREAD);    // Enable the receiver and set local mode
    options.c_cflag &= ~PARENB;             // No parity bit
    options.c_cflag &= ~CSTOPB;             // 1 stop bit
    options.c_cflag &= ~CSIZE;              // Mask data size
    options.c_cflag |=  CS8;                // Select 8 data bits
    options.c_cflag &= ~CRTSCTS;            // Disable hardware flow control  

    // Enable data to be processed as raw input
    options.c_lflag &= ~(ICANON | ECHO | ISIG);

    // Set the new attributes
    tcsetattr(fd, TCSANOW, &options);

    ////////////////////////////////////
    // Simple read and write code here//
    ////////////////////////////////////

    // Close file descriptor & exit
    close(fd)
    return EXIT_SUCCESS
}  

UPDATE I've modified my code based on the first answer. This is what I have now:

#include <errno.h>      // Error number definitions
#include <stdint.h>     // C99 fixed data types
#include <stdio.h>      // Standard input/output definitions
#include <stdlib.h>     // C standard library
#include <string.h>     // String function definitions
#include <unistd.h>     // UNIX standard function definitions
#include <fcntl.h>      // File control definitions
#include <termios.h>    // POSIX terminal control definitions

// Open usb-serial port for reading & writing
int open_port(void){

    int fd;    // File descriptor for the port
    fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);

    if (fd == -1){
        fprintf(stderr, "open_port: Unable to open /dev/ttyUSB0 %s\n",strerror(errno));
        exit(EXIT_FAILURE);
    }

    return fd;
}

int main(void){

    int              fd = 0;     // File descriptor
    struct termios   options;    // Terminal options
    int              rc;         // Return value

    fd = open_port();            // Open tty device for RD and WR

    // Get the current options for the port
    if((rc = tcgetattr(fd, &options)) < 0){
        fprintf(stderr, "failed to get attr: %d, %s\n", fd, strerror(errno));
        exit(EXIT_FAILURE);
    }

    // Set the baud rates to 230400
    cfsetispeed(&options, B230400);

    // Set the baud rates to 230400
    cfsetospeed(&options, B230400);

    cfmakeraw(&options);
    options.c_cflag |= (CLOCAL | CREAD);   // Enable the receiver and set local mode
    options.c_cflag &= ~CSTOPB;            // 1 stop bit
    options.c_cflag &= ~CRTSCTS;           // Disable hardware flow control
    options.c_cc[VMIN]  = 1;
    options.c_cc[VTIME] = 2;

    // Set the new attributes
    if((rc = tcsetattr(fd, TCSANOW, &options)) < 0){
        fprintf(stderr, "failed to set attr: %d, %s\n", fd, strerror(errno));
        exit(EXIT_FAILURE);
    }

    ////////////////////////////////
        // Simple Read/Write Code Here//
        ////////////////////////////////

    // Close file descriptor & exit
    close(fd);
    return EXIT_SUCCESS;
} 

Just to clarify, the receiver and transmitter use 8 data bits, 1 stop bit, and no parity bit.

sj755
  • 3,944
  • 14
  • 59
  • 79

1 Answers1

5

I prefer Serial Programming Guide for POSIX Operating Systems.

You should delete the fcntl(mainfd, F_SETFL) statement, since it's not required and incorrectly implemented (F_GETFL not done prior and missing third argument).

Try using cfmakeraw to setup non-canonical mode, since your initialization code is incomplete:

options->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
        | INLCR | IGNCR | ICRNL | IXON);
options->c_oflag &= ~OPOST; 

For non-canonical mode, you also need to define

options.c_cc[VMIN]  = 1;
options.c_cc[VTIME] = 2;

1 and 2 are just suggested values.

Add testing of return status after all system calls.

rc = tcgetattr(mainfd, &options);
if (rc < 0) {
    printf("failed to get attr: %d, %s\n", mainfd, strerror(errno));
    exit (-3);
}

Try testing with slower baudrates (e.g. 115200 or even 9600).

sawdust
  • 16,103
  • 3
  • 40
  • 50
  • I've made an update and posted my current code. For some reason the program is unable to read anything from the board, it just waits on the read function. Any thoughts? – sj755 Apr 09 '13 at 02:31
  • 1
    You don't need `c_cflag &= ~PARENB` since `cfmakeraw()` will handle that. You probably do need `c_cflag &= ~CSTOPB` and `c_cflag &= ~CRTSCTS` which got removed! Either of these could kill reading. – sawdust Apr 09 '13 at 03:22
  • I've added the 1 stop bit flag and disable hardware flow control, but it program still waits on the read function. The original code tends to cause the program to crash, but on occasion it does read from the board correctly, so the board is definitely transmitting something. Any other thoughts? – sj755 Apr 09 '13 at 04:32
  • I just did some debugging on the hardware, it seems the board produces the correct result internally. I'm hoping that the transmitter controller I designed works correctly, and the PC does at times receive the correct result. At this point I'm 90% sure the issue lies solely with the C code. – sj755 Apr 09 '13 at 04:56
  • 1
    If those edits were done correctly, then I don't see a problem. Maybe you could apply those changes to the "update" in the Q? The read code could be an issue. You could isolate the issue if you have a COM port or a USB-to-RS232 adapter: make a loopback tying TxD to RxD (i.e. short pins 2 & 3 of the DB9). Then hack your code to send some data before reading. You could also compare your code to [this tested code](http://stackoverflow.com/questions/12437593/how-to-read-a-binary-data-over-serial-terminal-in-c-program/12457195#12457195), although the port is opened for non-blocking I/O. – sawdust Apr 09 '13 at 05:57
  • I've updated the question to include the edits. Tying the TxD to the RxD pin would take some time, because the connection has to be made in hardware description, and I've have to implement a new module. – sj755 Apr 09 '13 at 16:02
  • Hey, I did some more debugging. The program no longer stops receiving data, however it exhibits some strange behavior. – sj755 Apr 09 '13 at 17:46
  • So I've pin-pointed an issue with the hardware. It seems that when the FPGA is first turned on, it writes something to serial port. There doesn't seem to be anything I can do about that. Everything else is working however, I just need to find a way ignore the junk values first being sent out. Anyway, thanks for the help. – sj755 Apr 09 '13 at 22:14