Question

I what to send some commands to a LPC 1786 microcontroller over a serial port. To accomplish this task I open the serial port using this code.

struct termios tio;
int tty_fd;

memset(&tio,0,sizeof(tio));
tio.c_iflag = 0;
tio.c_oflag = 0;
tio.c_cflag = CS8|CREAD|CLOCAL;           // 8n1, see termios.h for more information
tio.c_lflag = 0;
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 5;

tty_fd = open(device, O_RDWR | O_NONBLOCK);
cfsetospeed(&tio, B9600);            // 115200 baud
cfsetispeed(&tio, B9600);            // 115200 baud
tcflush(tty_fd, TCIFLUSH);

This is for an embedded linux system using (iMX53). When I compile the code on my PC and run it. I get this:

sent:    ?
recieve: Sinchronized<CR><LF>
sent:    Sinchronized<CR><LF>
recieve: Synchronized<CR>OK<CR><LF>
sent:    16000<CR><LF>
recieve: 16000<CR>OK<CR><LF>

witch is fine. When I cross compile it and upload it to the system I get this.

Sent:  ?
HEX:   0x53 0x79 0x6E 0x63 0x68 0x72 0x6F 0x6E 0x69 0x7A 0x65 0x64 0xA 0xA 0x53 0x79 0x6E 0x63 0x68 0x72 0x6F 0x6E 0x69 0x7A 0x65 0xA
ASCII: SynchronizedSynchronizedOKOKnchronized1K024K024chronized1

Sent:  ?
HEX:   0xA 0xA 0xA 0xA 0xA 0xA 0xA 0x30 0xA 0xA 0x31 0xA 0xA 0xA 0xA 0xA 0xA 0x34 0xA 0xA 0x31 0xA 0xA 0xA 0xA 0xA 0xA 0xA 0xA 0xA 0A
ASCII: 014141hronized1111111ronized1

witch kind of ruins my day. For writing and reading I'm using write & read functions. I bet this is to do with the way I am opening the serial port. But what wrong. I have tried different settings but with little success. In the best case as a response I get "?????1???". And most of the time there is nothing to be read.

Was it helpful?

Solution

The fundamental problem is that your code indicates that you have not (properly) initialized the tty port. Essentially the initialization does not call tcsetattr() to install the updated configuration that you tried to specify.

The proper way to initialize the tty port is to open the port, obtain the current attributes, save the current attributes for restoration on program exit, modify the attributes, and install the attributes. And always test the return codes from system calls.

    tty_fd = open(device, O_RDWR | O_NONBLOCK);
    if (tty_fd< 0) {
        syslog(LOG_DEBUG, "failed to open: %d, %s", tty_fd, strerror(errno));
        exit (-1);
    }
    rc = tcgetattr(tty_fd, &tio);
    if (rc < 0) {
        syslog(LOG_DEBUG, "failed to get attr: %d, %s", rc, strerror(errno));
        exit (-2);
    }
    savetio = tio;    /* preserve original settings for restoration */

    spd = B9600;
    cfsetospeed(&tio, (speed_t)spd);
    cfsetispeed(&tio, (speed_t)spd);

    cfmakeraw(&tio);

    tio.c_cc[VMIN] = 1;
    tio.c_cc[VTIME] = 5;

    tio.c_cflag &= ~CSTOPB;
    tio.c_cflag &= ~CRTSCTS;    /* no HW flow control? */
    tio.c_cflag |= CLOCAL | CREAD;
    rc = tcsetattr(tty_fd, TCSANOW, &tio);
    if (rc < 0) {
        syslog(LOG_DEBUG, "failed to set attr: %d, %s", rc, strerror(errno));
        exit (-3);
    }

Of course there may also be cabling and hardware issues still to be resolved, but since the tty port has not been initialized to the operational state that you want or need, nothing is likely to function as you expect.

Here's a guide for programming the serial port.

OTHER TIPS

There's a lot of good debugging comments for this question, but here's a summarized list of items to check for serial link: (Adding some of my own & slanted towards bring-up of developmental systems)

These two are sort of initial design level checks. Sometimes you end up revisiting them:

  • Check the datasheets: Do the voltage levels match? e.g "True" RS232 vs TTL, 12v vs 5v vs 3v
  • Buzz out your cable or circuit connections: grounding, does rx go to tx if differential do TX+/TX- etc matchup? (especially if it's a custom or hand-built cable)

These are more general:

  • Check that the baud rate matches
  • Check the data, stop bits and flow control settings match on both ends
  • Does a lower baud rate work?
  • Possibly try alternate cables?
  • Possibly try a different PC (had an odd ground loop with laptop AC supply causing problems once)
  • Make one end a "known-good" quantity if possible.

These are rarer checks:

  • Check any input clock settings feeding the baud rate of the serial peripheral.
  • Break out the scope and look to see if the signal is getting corrupted (how "square" are your level changes and do they match what you think the voltage levels of your hardware are)

In addition to the problems mentioned in other answer, one likely cause is a bad pre-scaler clock fed to the MCU UART. UART requires a clock accuracy of +- 3%. If your pre-scaler isn't that accurate, you will exactly the kind of issues described in the question: the data looks fine most of the time, and then suddenly you get garbage.

When trouble-shooting this, you need to check for overrun, framing errors etc on your receiver side. Check the signal and measure the baudrate with an oscilloscope. You cannot develop embedded applications without an oscilloscope, it is just as essential a tool as the debugger.

I'm a hardware guy at heart, so when things like this happen, I tend to start from the bottom:

  • Get a scope or logic analyser out and look at the bits transmitted. Check the baud rate. Check the characters. A scope that interprets the bits for you is a huge help here :)

  • If they are OK, check the bytes are getting into the embedded processor OK. Check the interrupt service routine. Set a bit on entry and clear it before exit. Check that the characters coming in are not overrunning the ISR.

Work up from there - is there some low level driver taking the data from the ISR?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top