Shop OBEX P1 Docs P2 Docs Learn Events
Time to learn about bitwise and shift — Parallax Forums

Time to learn about bitwise and shift

TommyBoyTommyBoy Posts: 9
edited 2013-12-07 07:54 in Learn with BlocklyProp
Hello Everyone -

I'm revisiting electronics and programming after being away from it for 20 years, and I've been following the C Tutorials. I got to the Sony IRC lesson and decided to branch off because my remote wasn't a Sony, but something else. I found that it uses NEC protocol. I've researched the device and I've developed fully functioning source code to:
1) Detect pulses on the IR detector,
2) Store the length of the pulses in an array,
3) Decode the pulse lengths into '1's and '0's,
4) Convert the LONG bits into an INT, and
5) Perform some function when I press a key.
-
Everything works as it should, but I'm storing and converting a LONG when I really need to store a BIT.

One area where my re-education is lacking is in the use of Bitwise and shift operators. I have a sense that I'll be needing a lot of these. My little program might be a good place to demonstrate their use.

Can someone please demonstrate a more efficient way to get the bits into an INT format using Bitwise and shift operators? Heavy emphasis on the explanations please.

Thanks.

Comments

  • edited 2013-12-04 06:39
    You have a series of incoming pulse measurements. Depending on the size of these pulse measurements, you want to either store a 1 or a 0 in a variable. By repeatedly shifting the 1/0 results the variable already has to the left and then copying the latest result to the rightmost binary digit in the variable, you can create a collection of 1/0 results in a single variable. The variable's 1/0 pattern will be a number that your code can compare against some list of numbers to decide, in the case of your TV remote, which button was pressed.

    Here is an example with numbers in a loop. Each time through the loop, it shifts the the binary values in the bits variable to the left by 1, makes a comparison, and stores the 1/0 result of that comparison in bits variable's the rightmost binary digit. As written, the code displays this result in the SimpleIDE Terminal:

    bits(dec) = 3510
    bits(binary) = 110110110110

    You can change the (inVal % 3 < 2) condition to get a different set of 1/0 result bits.
    #include "simpletools.h"              // Include simpletools library
    
    int inVal;                            // Input value
    int bits;                             // Accumulates 1/0 results
    
    int main()                            // Main function
    {
      bits = 0;                           // Clear result bits variable
      for(inVal = 0; inVal < 12; inVal++) // Create varying inVals in a loop
      {
        // bits = (bits shifted left by 1) OR (the 1/0 result of a comparison)
        bits = (bits << 1) | (inVal % 3 < 2);
      }
      // Display bits result as decimal and binary value
      print(" bits(dec) = %d\n bits(binary) = %b\n", bits, bits);  
    }
    

    How it Works

    Before the loop starts, make sure to clear the variable that will store the result bits by setting it to zero. In this example, the for loop counts inVal from 0 to 11. Each time through the loop, the right side of the expression bits = (bits << 1) | (inVal % 3) does three things:

    1) Shift all the binary values stored by bits left by one slot with (bits << 1). So, if bits stored binary 101, this operation will return 1010.

    2) Get the result if a comparison. In this case, (inVal % 3 < 2) checks to find out if the remainder of inval / 3 is less than 2. So, when the remainder of inVal / 3 is 0 or 1, the the comparison returns 1. If the result of inVal / 3 is 2, well, that's not less than 2, so the comparison returns 0. Since we are dealing with int values in the Propeller, the 0 result will actually be a 32-bit variable with all zeros. The 1 result is really 000...0001, where the ... is taking the place of 25 more zeros.

    3) Bitwise OR the shifted bits result from step 1 with the 1/0 result from step 2. The rule for OR is that 1 OR any value (could be 1, could be 0) is 1. If the result from step 2 was 0, the OR result would be.

    ...1010
    ...0000 OR
    ...1010

    If the result from step 2 was 1, the result would be:

    ...1010
    ...0001 OR
    ...1011

    Note that that all the 0s in the second number OR with values in the top number leaves the values unchanged in the result. But when we made the rightmost digit in the second number a 1, the 0 in the top number OR the 1 in the second number caused a 1 in the rightmost digit of the result.

    In your case, step 2 would be a comparison of your pulse_in measurement to find out if the remote transmitted a pulse that lasted the length of time for transmitting a 1 or a 0.
  • TommyBoyTommyBoy Posts: 9
    edited 2013-12-04 17:58
    Thanks Andy,

    That's exactly what I needed.

    I reworked your code so I could see what was going on, step-by-step.
    Bitwise.c

    I've also revised my code to include the 'Left Shift' and 'Or' functions. It works as expected.
    NEC Prot.c

    'Left Shift' makes the first pulse the MSB. Is there a way to make it the LSB? I know 'Right Shift' doesn't do it.
  • Hal AlbachHal Albach Posts: 747
    edited 2013-12-05 13:07
    So that bit 0 of the first command field actually winds up in bit 0 position and not at position 7 I made a very slight modification to your NEC Prot.c program. I made the for loop start at position 23 and count down to 16.
    In the line of code the comments identify as Step 3, change the for(i = 16; i < 24; i++) to for(i = 23; i > 15; i--) // Step 3. . . and your codes will come out as actually represented in the transmission. Pressing 1 will actually output a 1.
    BTW, in your original statement you had for(i = 16; i < 25; i++) , looking at 9 bits instead of 8.
    A simple print("%d\n", KeyBit); before the Switch statement will help you reidentify all the other buttons on your remote.

    'Left Shift' makes the first pulse the MSB. Is there a way to make it the LSB? I know 'Right Shift' doesn't do it" Yes it will, just change both statements in step 4 to shift right and in the second statement change the 1 at the end ( after the OR symbol '|' to either 128 or 0x80 so that you are placing a 1 bit into the MSB position after the byte was shifted right once.
    Just use one method or the other, not both.

    Hal
  • TommyBoyTommyBoy Posts: 9
    edited 2013-12-05 16:43
    Hello Hal,

    Thanks for the reply.
    In the line of code the comments identify as Step 3, change the for(i = 16; i < 24; i++) to for(i = 23; i > 15; i--) // Step 3. . . and your codes will come out as actually represented in the transmission. Pressing 1 will actually output a 1.
    Darn, I knew that at one time and I'd forgotten!
    BTW, in your original statement you had for(i = 16; i < 25; i++) , looking at 9 bits instead of 8.
    Good catch. Thanks for the pickup.
    change both statements in step 4 to shift right and in the second statement change the 1 at the end ( after the OR symbol '|' to either 128 or 0x80 so that you are placing a 1 bit into the MSB position after the byte was shifted right once.
    This is exactly what I was looking for.

    NEC Prot.c
    Here's the updated code with both suggestions incorporated. (After testing I commented out one.)

    Thanks again Hal. You've given me the info I was looking for.
  • Hal AlbachHal Albach Posts: 747
    edited 2013-12-05 16:58
    You're welcome! Thanks to you and Andy I now have a working NEC protocol program. Just finished programming the case statements for an orphan Toshiba Remote that uses NEC.
  • TommyBoyTommyBoy Posts: 9
    edited 2013-12-05 18:29
    If I know how, we could make it a library file. :)
  • Hal AlbachHal Albach Posts: 747
    edited 2013-12-05 19:26
    I've been thinking about making this code run in its own cog. A single global variable can be used so that when the cog receives a valid IR code it simply places it in the global var. The main program can, at various times, check the variable for non-zero and act upon it. While the variable is non-zero the IR program could ignore further IR codes or que them. When main copies the global var, it then zeroes the global var which tells the IR program to place the next code into it....and so on and on. This way the main program is not stalled waiting for the IR operation to complete.
  • TommyBoyTommyBoy Posts: 9
    edited 2013-12-07 07:54
    I think that would be the way to go. I see the remote as a way to communicate simple commands with a 'bot but not as a navigational controller. I'm thinking along the lines of 'light on/off', pan camera left/right up/down, open/close manipulator arm, etc. Things that are done occasionally, not constantly.
Sign In or Register to comment.