Thread: bit masking and serial programming

  1. #1
    Registered User
    Join Date
    Jan 2014
    Posts
    62

    bit masking and serial programming

    I'm using the following guide as a reference for reading and writing to a modem via a serial port:

    Serial Programming Guide for POSIX Operating Systems

    So far based on what I read, I have a program that looks like this:

    Code:
    #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 */
    
    
    struct termios options;
    
    int open_port()
    {
        int fd; /* File descriptor for the port */
        fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
    
        if (fd == -1)
        {
            perror("open_port: Unable to open /dev/ttyS0 - ");
        } else {
          fcntl(fd, F_SETFL, 0);
          return (fd);
        }
    }
    
    int configure_port(int fd)
    {
        cfsetispeed(&options, B1200);
        cfsetospeed(&options, B1200);
    
        options.c_cflag |= (CLOCAL | CREAD);
    
        options.c_cflag &= ~CSIZE; /* Mask the character size bits */
        options.c_cflag |= CS8;    /* Select 8 data bits */
    
        tcsetattr(fd, TCSANOW, &options);
    
        return 1;
    }
    
    int main(void)
    {
        int fd = open_port();
        configure_port(fd);
    
        return 0;
    }
    In the section "Setting the Character Size", it says "you must do a little bitmasking to set things up. The character size is specified in bits".

    Now I am familiar with the bitwise operators, but still I cannot make sense of what's going on here. c_cflag is a member of a struct, and we take CSIZE (which printf says its value is 48, which is binary 0011 0000) and use the one's complement ~ to get 1100 1111. And then we use a bitwise AND "&=" with the c_cflag member. But wait a second, c_cflag is a member that holds many options, not just the character size, so why exactly are we assigning it the value 1100 1111?

  2. #2
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    You're not "assigning" it that value. You're "anding" that value with it's current value. CSIZE is a mask with 1's in the position of the bits that encode the characters size. By complementing that value (giving 1100 1111) and anding it against the current value of c_cflag, you reset the bits in those positions to 0. Presumably the next line then sets those bits to the values for your chosen character size.
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  3. #3
    Registered User
    Join Date
    Jan 2014
    Posts
    62
    Quote Originally Posted by oogabooga View Post
    You're not "assigning" it that value. You're "anding" that value with it's current value. CSIZE is a mask with 1's in the position of the bits that encode the characters size. By complementing that value (giving 1100 1111) and anding it against the current value of c_cflag, you reset the bits in those positions to 0. Presumably the next line then sets those bits to the values for your chosen character size.
    If I am ANDing it, then what exactly is c_cflag? The thing is it takes many different kind of options. How does it know which option is which?

  4. #4
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Quote Originally Posted by johnmerlino View Post
    If I am ANDing it, then what exactly is c_cflag? The thing is it takes many different kind of options. How does it know which option is which?
    By reading the documentation:
    Quote Originally Posted by man 3 tcsetattr
    tcflag_t c_cflag; /* control modes */
    ...
    c_cflag flag constants:
    CBAUD
    ...
    As for knowing which option is which, it is positional. c_cflag is of type tcflag_t; the underlying type is implementation defined, but is some sort of unsigned integer (short/int/long). It it probably at least a 16 bit number, maybe 32. Each option only uses some of those bits. For simplicity's sake, lets assume it's a 16 bit short -- you declare options as a global, so it's initialized to all-bits-zero, thus c_cflag is
    Code:
    0000 0000 0000 0000  // c_cflag
    Assume CLOCAL is the least significant bit, and CREAD is the bit next to it.
    Code:
    0000 0000 0000 0001  // CLOCAL
    0000 0000 0000 0010  // CREAD
    You bitwise-OR those together
    Code:
    0000 0000 0000 0011  // CLOCAL | CREAD
    Then you bitwise-OR that with c_cflag, and assign the result to c_cflag (the |= part)
    Code:
        0000 0000 0000 0000  // c_cflag
    |=  0000 0000 0000 0011  // (CLOCAL | CREAD)
        -------------------
        0000 0000 0000 0011  // -> c_cflag
    Next, per the documentation, CSIZE can contain one of 4 values, so it must use at least 2 bits (for this example, we'll assume 00, 01, 10, 11 being the four 2-bit values for CSIZE: CS5, CS6, CS7 and CS8 respectively). Let's also assume CSIZE is uses the two bits next to CREAD
    Code:
        0000 0000 0000 1100  // CSIZE
        1111 1111 1111 0011  // ~CSIZE
        0000 0000 0000 0000  // CS5 -- 00 in the CSIZE bits
        0000 0000 0000 0100  // CS6 -- 01 in the CSIZE bits
        0000 0000 0000 1000  // CS7 -- 10 in the CSIZE bits
        0000 0000 0000 1100  // CS8 -- 11 in the CSIZE bits
    Now, if we & the bitwise inverse of CSIZE, we will clear those two bits that CSIZE uses, from c_cflag
    Code:
        1111 1111 1111 0011  // ~CSIZE
    &=  0000 0000 0000 0011  // c_cflag
        -------------------
        0000 0000 0000 0011  // -> c_cflag
    Now, we bitwise-OR CS8 into c_flag.
    Code:
        0000 0000 0000 1100  // CS8
    |=  0000 0000 0000 0011  // c_cflag
        -------------------
        0000 0000 0000 1111  // -> c_cflag
    That is how the options are combined into a single variable. Note that, if you did c_cflag = CS8 (plain assignment) instead of |=, you would have assigned 0000 0000 0000 1100 to c_cflag, and the two bits you set previously (CLOCAL and CREAD) would have been cleared.

    As for how the function determines which options you set, it basically does the exact opposite of what you do to assemble them:
    Code:
    if (c_cflag & CLOCAL)
        // CLOCAL option was set
    else
      // CLOCAL option was not set
    if ((c_cflag & CSIZE) == CS5)
        // CSIZE was set to CS5
    ...
    If you are still having trouble with bitwise operations, then I suggest getting a book and some online tutorials, and working through several examples by hand, to help you understand it conceptually.

    EDIT:
    Note, the actual position of the individual options within c_cflag, and the values used by CS5 and the like, are irrelevant, that is why they're referred to by names instead of explicitly setting specific bits.
    Last edited by anduril462; 02-03-2014 at 11:50 AM.

  5. #5
    Registered User
    Join Date
    Jan 2014
    Posts
    62
    anduril462, that was very helpful! thanks

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Serial Programming the Win32 api
    By Hexagram1000 in forum Windows Programming
    Replies: 5
    Last Post: 07-18-2008, 08:16 PM
  2. Serial port programming
    By jitender dogra in forum C Programming
    Replies: 4
    Last Post: 07-09-2007, 03:29 AM
  3. Serial Programming
    By gvector1 in forum Networking/Device Communication
    Replies: 3
    Last Post: 07-16-2003, 06:32 PM
  4. Serial programming
    By stovellp in forum C++ Programming
    Replies: 6
    Last Post: 01-20-2003, 12:29 PM
  5. Need help on Serial Port programming!!!
    By Unregistered in forum C Programming
    Replies: 4
    Last Post: 06-04-2002, 05:02 PM