This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM335 - UART - BeagleBone Loopback Test

Other Parts Discussed in Thread: DM3730
I’m trying to do a loopback test on the BeagleBone serial ports.  I currently have the TX and RX of UART1 looped back.  I first tested with my Debian SD card by running picocom on port /dev/ttyO1, I was expecting characters typed to be echoed back.  I didn’t see any action however.
 
I did some searching on the web and came across this article:

I then rebooted the BeagleBone using the original Angstrom image and ran

echo 20 > /sys/kernel/debug/omap_mux/uart1_rxd

 and

echo 0 > /sys/kernel/debug/omap_mux/uart1_txd

  to configure the pins as described.  I then ran minicom on /dev/ttyO1 but still did not see any loopbacked characters.

 Could you advise on why this is not working?  Where in the system are the serial ports initialized?

  • Hi Michael,

    Here are some of questions I have to ask to solve your query

    1. Are you using loop back UART as a console? If yes, then you will not able to see any logs on minicom as Rx and Tx of UART are connected to each other.
    I have tested internal loopback AM335X and followed the below procedure:
    1. Made changes in UART driver to enable internal loop back. For AM335X I have made following changes:
    • File :drivers/tty/serial/omap-serial.c. 
    • Function : serial_omap_startup.
    • Add this line "up->port.mctrl |= TIOCM_LOOP;" in serial_omap_startup function before writing modem control register.  
           2. Build Kernel and Run it using minicom.
           3. Please check attached file which I used in order to test loopback.In brief, this file is used to send characters and receive it on same port and then
               used to capture send and receive characters in two different files ( rdFile and wrFile ). Compile this file using proper toolchain and put this test application in rootfs
               and run it on terminal which ends up giving two files which you can compare it to check loopback is working properly.
    Compilation steps of  application program:
    1. gcc -o uart-loopback  uart-loopback_st_orig.c
    2. Output File generated : uart-loopback
    Run "uart-loopback" on mincom:
    1. Run uart-loopback /dev/ttyO2 [give a port on which you want to test UART loopback]
    2. Two files get generated rdFile and wrFile 
    3. Compare these two files to check loopback working
    Please try to do these modifications in your code and use this application to check loopback an convey your results.
    /*
     * UART Loop back test for L138 and OMAP3 EVM
     *
     * Based on  uart-loopback.c -
     *     userspace uart loopback test <http://elinux.org/File:Uart-loopback.c>
     *
     * Modified by <sinoj@mistralsolutions.com> 
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
     * DAMAGE.
     *
     * 
     */
    
    /* Compilation */
    /* arm-none-linux-gnueabi-gcc uart-loopback_st.c -o ltest */
    
    /* Execution */
    /* ./ltest </dev/ttyS1> <115200> */
    
    /* Flow control disabled, one thread, (write CBUFSIZE bytes of data 
     * and read back it and compare )  WRLOOP times  */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <getopt.h>
    #include <termios.h>
    #include <sys/ioctl.h>
    
    #define CBUFSIZE 256 
    #define WRLOOP 64 
    
    /* Gvariables */
    
    fd_set         readfs;    
    struct timeval timeout;
    int            fd;
    char           *tty;
    char           *btr = NULL;
    int            opt, count=127, indt;
    struct termios term;
    int            baud;
    char           wrbuf[CBUFSIZE*WRLOOP] = {0};
    char           rdbuf[CBUFSIZE*WRLOOP] = {0};
    pthread_t      td1;
    int            rdSize;
    
    void print_usage(char *prg)
    {
        fprintf(stderr, "\nUsage: %s [options] tty buad\n\n", prg);
        fprintf(stderr, "Example..\n");
        fprintf(stderr, "./ltest /dev/ttyS1 115200\n");
        fprintf(stderr, "\n");
        exit(1);
    }
    
    static int uart_speed(int s)
    {
        switch (s)
        {
            case 9600:
                return B9600;
            case 19200:
                return B19200;
            case 38400:
                return B38400;
            case 57600:
                return B57600;
            case 115200:
                return B115200;
            case 230400:
                return B230400;
            case 460800:
                return B460800;
            case 500000:
                return B500000;
            case 576000:
                return B576000;
            case 921600:
                return B921600;
            case 1000000:
                return B1000000;
            case 1152000:
                return B1152000;
            case 1500000:
                return B1500000;
            case 2000000:
                return B2000000;
            case 2500000:
                return B2500000;
            case 3000000:
                return B3000000;
            case 3500000:
                return B3500000;
            case 4000000:
                return B4000000;
            default:
                return B38400;
        }
    }
    
    /* read task */
    //void *task(void)
    void task (int offset)
    {
        int idx2 = 0;
        int idx3 = 0;
    
        int ret = 0;
        int startingpoint = 0;
        
    //    printf ("Task\n");
    
        FD_SET(fd, &readfs);  
    
        /* set timeout value within input loop */
        timeout.tv_usec = 0;  /* milliseconds */
        timeout.tv_sec = 10;  /* seconds */
    
        idx3 = offset*CBUFSIZE;
    
        startingpoint = idx3;
    
        do
        {
            ret = select(fd+1, &readfs, NULL, NULL, &timeout);
            if (ret == 0)
            {
                printf("read timeout error!\n");
                exit(1);
            }
            else
            {
                ret = read(fd, &rdbuf[(idx3)], CBUFSIZE);
            }
    
            if (ret == -1)
            {
                printf("read error!\n");
                return ;
            }
            else if (ret == 0)
            {
                printf("read 0 Buf !\n");
            }
            else 
            {
                for (idx2 = idx3; idx2 < (idx3 + ret); idx2++)
                {
    
                    if (wrbuf[idx2] != rdbuf[idx2])
                    {
                        printf("read data error: wrote 0x%x read 0x%x idx2 %d ret %d \n", \
                                wrbuf[idx2], rdbuf[idx2], idx2, ret);
                    }
                }
                idx3 += ret;
            }
    
        } while (idx3 < (startingpoint + CBUFSIZE));
    
    } /* end of task */
    
    
    int main(int argc, char **argv)
    {
        int idx1 = 0;
        int ret = 0;
    
        FILE *wrFile;
        FILE *rdFile;
    
    
    #if 0
        while ((opt = getopt(argc, argv, "bc:?")) != -1) 
        {
            switch (opt)
            {
                case 'b':
                   btr = optarg;
                   printf("baud=%s\n", btr);
                   break;
    
                case 'c':
                   count = atoi(optarg);
                   break;
    
                case '?':
                default:
                   print_usage(argv[0]);
                   break;
            }
        }
        
                                                                    
        if (argc - optind != 2)
            print_usage(argv[0]);
    #endif
    
        tty = argv[optind];
    
    //    baud = atoi (argv[optind+1]);
    
        baud = 115200;
       	
        printf("uart port used: %s\n",tty);
    	
        if ((fd = open (tty, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0)
        {
            perror(tty);
    	exit(1);
        }
    
        tcflush(fd, TCIFLUSH);
    
        if (tcgetattr(fd, &term) < 0)
        {
            perror("can't get port settings");
            return -1;
        }
    
        cfmakeraw(&term);
    
        term.c_cflag |= CLOCAL;
    
        term.c_cflag &= ~CRTSCTS;
    
        cfsetospeed(&term, uart_speed(baud));
        cfsetispeed(&term, uart_speed(baud));
    
        if (tcsetattr(fd, TCSANOW, &term) < 0)
        {
            perror("can't set port settings");
            return -1;
        } 
    #if 0
        if ( pthread_create(&td1, NULL, task, NULL) != 0 )
        {
           printf ("pthread_create() error\n");
        }
    #endif
        for (idx1 = 0; idx1 < CBUFSIZE*WRLOOP; idx1++)
        {
            wrbuf[idx1] = idx1 + 0x10;
            rdbuf[idx1] = 0xff;
        }
    
        /* write task */
    
        for (idx1 = 0; idx1 < (WRLOOP); idx1++)
        {
    
            ret = write(fd, &wrbuf[(idx1*CBUFSIZE)], CBUFSIZE);
            if (ret != CBUFSIZE)
            {
                printf("write error!i ret= %d\n", ret);
                exit(1);
            }
    
            task(idx1);
    
        }
    
    //    sleep (3);
    
        wrFile = fopen ("wrFile", "wb+");
    
        if (wrFile == NULL)
        {
             printf ("wrFile open error!\n");
        }
        else
        {
             if (fwrite (&wrbuf[0], (CBUFSIZE*WRLOOP), 1, wrFile)  < 1)
             {
                  printf ("wrFile write error!\n");
             }
        }  
    
        rdFile = fopen ("rdFile", "wb+");
    
        if (rdFile == NULL)
        {
             printf ("rdFile open error!\n");
        }
        else
        {
             if (fwrite (&rdbuf[0], (CBUFSIZE*WRLOOP), 1, rdFile)  < 1)
             {
                  printf ("rdFile write error!\n");
             }
        } 
    
     
    #if 0  
        /* Dump the user space buffers */
    
        printf ("write buf\n");
        for (idx1 = 0; idx1 < CBUFSIZE; idx1++)
        {
            printf ("0x%2x ", wrbuf[idx1]);
            if (idx1 % 32 == 0)
            {
                printf ("\n");
                for (indt = idx1/32; indt > 0; indt--) 
                {
                     printf ("  "); 
                }     
            }
        }
    
        printf ("\nread buf\n");
    
        for (idx1 = 0; idx1 < CBUFSIZE; idx1++)
        {
            printf ("0x%2x ", rdbuf[idx1]);
            if (idx1 % 32 == 0)
            {
                printf ("\n");
                for (indt = idx1/32; indt > 0; indt--) 
                {
                     printf ("  ");
                }     
            }
    
        }
    #endif
        printf("\nUART loop back test completed! \n");
    
        close(fd);
    
        return 0;
    }
    
    Thanks 
    Manish B.
               
  • Hi Manish,

    This is Sean, I am the originator of the message.  I just got the loopback working by further tweaking of the pin settings under Linux.

    I heard that uarts 3 to 5 are not enabled in the Linux kernel image.  What steps are required to get them to work?

    Thank you,

    Sean

  • Hi Sean

    Please check HW modules are added for UART which you want to test.

    For this you can check file "arch/arm/mach-omap2/omap_hwmod_33xx_data.c"  and 

    check HW modules are added for UART which you want to test in "am33xx_hwmods" array .

    I think all UARTs are already added in "am33xx_hwmods" array. Just to confirm.

    As UART3 to UART5 are connected on daughter board, you have to first check on which profile you are running board

    and in board file "arch/arm/mach-omap2/board-am335xevm.c", you have to add proper pin muxing for UART which you want to test.

    See sample modification which I have done for UART3:

    1. Board is general purpose EVM.
    2. Profile 5 is selected on EVM through switches.
    3. Added entry for UART3 in gen_purp_evm_dev_cfg array as below:

                 static struct pinmux_config uart3_pin_mux[] = {

                                {"spi0_cs1.uart3_rxd", OMAP_MUX_MODE1 | AM33XX_SLEWCTRL_SLOW |

                                                                                              AM33XX_PIN_INPUT_PULLUP},

                                {"ecap0_in_pwm0_out.uart3_txd", OMAP_MUX_MODE1 | AM33XX_PULL_UP |

                                                              AM33XX_PULL_DISA | AM33XX_SLEWCTRL_SLOW},

                                {NULL, 0},

                };

                static struct evm_dev_cfg gen_purp_evm_dev_cfg[] = {

                               {uart3_init, DEV_ON_DGHTR_BRD, PROFILE_5},  /* As UART3 active in Profile 5 */

                }

                static void uart3_init(int evm_id, int profile)
                {
                              setup_pin_mux(uart3_pin_mux);
                              return;
                } 

          3. This way you can set pin muxing for UART3.

          4. Or other way, you can simple use echo as you used before to set pin muxing of particular line after booting board.

    Note : Before booting board, you just give proper profile setting through switches ( for e.g for UART3  select profile 5). 

    Now, your pins are configured and accessible for UART3.

    Let me know, If you want any further details.

    Thanks 

    Manish Badarkhe

  • Hi Manish,

    How do I enable ttyO5?  I have it pin muxed correctly, and tried a TTL level loopback on /dev/ttyO5 but am not receiving or transmitting any characters.  I notice that uart5 seems to be defined in omap_hwmod_33xx_data.c, but why is the .sdma_reqs field equal to uart1_edma_reqs, and no separate uart6_edma_reqs structure defined?

    In general how do I get hardware loopback on ttyO5 to work?

  • Sean,

    I forked this to another post since it seems to be more about enabling TTY05 then the loop back functionality.

    [AM335x] Board Porting - Enable TTY05 in Linux Kernel
  • Hello Manish,

    I plan to use the same code for testing loopback on my DM3730 board. However, I plan to set up an external loopback for the testing i.e. putting up a loopback switch followed by code execution. I would NOT be changing the uart driver.

    Can the same code work in the case of external loopback?

    BR,

    Harshit

  • Hi Manish B.

    After adding the line up->port.mctrl |= TIOCM_LOOP, can I still successfully login via /dev/ttyO0 and then test /dev/ttyO2 loopback?