Jump to content
43oh

Problem with bit wise operation


Recommended Posts

Hi, I am just getting started with the MSP430, turning leds on and off and such, and I seem to have a few problems with bitwise operators.

 

I dont understand why do I have to write:

 

P1OUT |= BIT0;

 

to turn LED 0 on when I initialize the program, but I cant write:

 

P1OUT = BIT0;

 

Shouldn't it be the same?

 

Also I get a lot of problems when transferring the program from CCS to the MSP430 and I wanted to know if this is normal. It seems like a lot of the times I transfer the program to the Launchpad it doesnt work, and I have to try to transfer it several times (without changing anything in the source code) until it "catches" and the program works. Sometimes turning the launchpad on and off seems to make the program that I just transferred, but wasnt working, to start working. I dont know if this is normal or I have a defective unit.

 

By the way this is the code I am using, it alternates the leds on and off by pressing the button.

 



int main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
P1DIR |= BIT0+BIT6; // Set P1.0 and P1.6 to output direction

//--------------------------------------------------------------------------------------------------------------

P1OUT = 0x00; // IF I DELETE THIS LINE THE PROGRAM WORKS, IF I LEAVE IT IT DOESNT

//--------------------------------------------------------------------------------------------------------------

P1IE |= BIT3; // P1.3 interrupt enabled
P1IES |= BIT3; // P1.3 Hi/lo edge
P1REN |= BIT3; // Enable Pull Up on SW2 (P1.3)
P1IFG &= ~BIT3; // P1.3 IFG cleared
//BIT3 on Port 1 can be used as Switch2

_BIS_SR(LPM4_bits + GIE); // Enter LPM4 w/interrupt

}

// Port 1 interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{

long int i = 0;
P1IE = 0x0; // P1.3 interrupt disabled


P1OUT ^= BIT0+BIT6; // P1.0 + P1.6 = toggle



for(i=0; i< 30000; i++); // Wait for about half a second
P1IFG &= ~BIT3; // P1.3 IFG cleared
P1IE |= BIT3; // P1.3 interrupt enabled
}



 

Thanks in advance.

Link to post
Share on other sites

hey, im not sure but maybe the direct assignment P1OUT = BIT0; doesn't work because BIT0 is defined as 16 bits. have you tried, for example, removing the two leading zeroes from the define like this

#define BIT0                   (0x01)

and see if it works.

just an idea.

 

also you might want to enable interrupts after setting everything, like this:

P1IES |= BIT3; // P1.3 Hi/lo edge
P1REN |= BIT3;             // Enable Pull Up on SW2 (P1.3)
P1IFG &= ~BIT3; // P1.3 IFG cleared

P1IE |= BIT3; // P1.3 interrupt enabled

cheers

Link to post
Share on other sites

You might have a defective unit. Try a different usb cable. Are you using a hub?

 

As for P1out = bit0; you CAN do that. But that's only if you don't care about the rest of p1out. Using |= is a "good practice" habit.

 

If p1out is currently 0b01100110, and you use p1out = bit0, p1out becomes 0b00000001. Everything that was set gets overridden. P1out |= bit0; is the same as p1out = pi1out | bit0; so 0b01100110 | 0b00000001 = 0b01100111. Bit0 is set, without overwriting bits 1-7.

 

BUT initializing p1out to 0x00 should be fine, if that's what you want. There is no reason it shouldn't work. Do you get any errors in CSS? Compile errors?

 

ACTUALLY, looks like you have are not enabling the Pull UP on p1.3. P1REN enables the pull resistor, but p1out determines if that resistor is a pull UP (1) or pull DOWN (0). p1out = bit3; should work.

Link to post
Share on other sites

Time for a little tutorial in binary!

 

There are three primitive binary operations:

  • invert (negate/not): take one input and give back the opposite (a 0 becomes a 1, a 1 becomes a 0)
  • and: take two inputs and give back a 1 if both inputs are 1 as well (give back 0 if any or all inputs are not 1)
  • or: take two inputs and give back a 1 if any or all inputs are 1 (give back 0 only if no inputs are 1)

Visually it looks like this, here A and B are inputs and U is the output

 NOT       AND       OR
A | U   A B | U   A B | U
--+--   ----+--   ----+--
0 | 1   0 0 | 0   0 0 | 0
1 | 0   0 1 | 0   0 1 | 1
        1 0 | 0   1 0 | 1
        1 1 | 1   1 1 | 1

In the language C, the bitwise notations for these three operations are ~ (tilde, not), & (ampersand, and), | (pipe, or)

 

So changing a value in bit X to it's inverted value is written as

X = ~X;

 

Now, C does not have a bit variable type, (almost) only integer number variables. So a little about bitwise operations on numbers. For the sake of simplicity I use 4-bit numbers, in reality most variables are either 8-bit or 16-bit (or 32/64-bit on PCs).

Say I have a variable X and I want to set the least significant bit (the bit most to the right when written), how do I do that? I do an OR operation with the value which holds one 1, which is is the least significant bit position, this value is 1

X = X | 1;

X: 0 1 0 0
1: 0 0 0 1
   ------- OR
   0 1 0 1

Observe that a bit in the result is one if any or all bits directly above it are 1 as well.

X: 0 1 0 1
1: 0 0 0 1
   ------- OR
   0 1 0 1

Note that the value of this bit in the variable is not important for the result; the least significant bit is always 1 in the result, because one of the inputs was always 1.

 

Now if we want to clear that bit? We do an AND with the inverse of 1. Say what? Let's draw it

 1: 0 0 0 1
    ------- NOT
~1: 1 1 1 0

 X: 0 1 0 1
~1: 1 1 1 0
    ------- AND
    0 1 0 0

So by ANDing with the inverse of the desired clear bit, you can actually set it to 0. In C we'll write

X = X & ~1;

 

Note that in both these cases ONLY the least significant bit changes, any bits that may be set or cleared in other bit positions are unaffected. This is why you want to use the OR and AND operations, not simple assignments.

In C there are shorthands for operations on something itself:

X = X & ~1; equals X &= ~1;

X = X | 1; equals X |= 1;

 

So if I write P1OUT |= BIT0; I will only be affecting the bits that are set in BIT0, which is only the least siginificant bit. BIT1 only has the second least siginificant bit set, BIT2 the third least siginificant bit, etcetera.

 

Advanced bit subjects

If you want to alter multiple bits, you can first OR them together:

BIT0: 0 0 0 0  0 0 0 1
BIT2: 0 0 0 0  0 1 0 0
      ---------------- OR
      0 0 0 0  0 1 0 1 BIT0 | BIT2

Then you can use this to set or clear both bits simultaneously:

X = X | BIT0;

X = X | BIT2;

becomes

X = X | BIT0 | BIT2;

in short

X |= BIT0 | BIT2;

 

Now if you want to clear both bits in X, you need to use the negated values

X = X & ~BIT0;

X = X & ~BIT2;

becomes

X = X & ~BIT0 & ~BIT2;

becomes

X &= ~BIT0 & ~BIT2;

visually

 BIT0: 0 0 0 0  0 0 0 1
       ---------------- NOT
~BIT0: 1 1 1 1  1 1 1 0

 BIT2: 0 0 0 0  0 1 0 0
       ---------------- NOT
~BIT2: 1 1 1 1  1 0 1 1

~BIT0: 1 1 1 1  1 1 1 0
~BIT2: 1 1 1 1  1 0 1 1
       --------------- AND
       1 1 1 1  1 0 1 0 ~BIT0 & ~BIT2

    X: 0 1 0 1  0 1 0 0
       1 1 1 1  1 0 1 0
       ---------------- AND
       0 1 0 1  0 0 0 0

Note that any bit that used to be 0 stays 0 and any 1 stays 1, except for the bits that are 0 in ~BIT0 & ~BIT2

 

We could get the value 1111 1010 in another way as well: take the inverse of 0000 0101. As you know by now, 0000 0101 is attained by ORing BIT0 and BIT2

 BIT0: 0 0 0 0  0 0 0 1
 BIT2: 0 0 0 0  0 1 0 0
       ---------------- OR
       0 0 0 0  0 1 0 1 BIT0 | BIT2

       0 0 0 0  0 1 0 1 BIT0 | BIT2
       --------------- NOT
       1 1 1 1  1 0 1 0 ~(BIT0 | BIT2)

    X: 0 1 0 1  0 1 0 0
       1 1 1 1  1 0 1 0
       ---------------- AND
       0 1 0 1  0 0 0 0

Observe that the result of ~(BIT0 | BIT2) is identical to ~BIT0 & ~BIT2. This property is called Demorgan's law:

  • By inverting both inputs and the output of an AND operation, I get an OR operation.
  • By inverting both inputs and the output of an OR operation, I get an AND operation.

Since ~BIT0 inverted is ~~BIT0, which is equal to BIT0, this property holds. So I take NOT NOT BIT0 (ie: BIT0) and NOT NOT BIT2 (ie: BIT2) and replace the AND operation with an OR operation. Then I NOT the result once more and get the same result.

 

So

X = X & ~BIT0 & ~BIT2;

becomes

X &= ~BIT0 & ~BIT2;

which is equal to

X &= ~(~~BIT0 | ~~BIT2);

is equal to

X &= ~(BIT0 | BIT2);

 

As a last reminder: BIT0, BIT1, etc. are part of the MSP430 library (msp430.h), not part of the C language itself. So you cannot use these definitions in a PC application unless you create them first.

 

Exclusive or and asymmetric bitwise operations

Apart from the three basic operation there is one other operation that might be useful on occasion; the exclusive or. Exclusive or (or XOR in short) is written as ^ (circumflex, xor) and gives back 1 if exactly one input is 1 (give back 0 if no or both inputs are 1)

   XOR
A B | U
----+--
0 0 | 0
0 1 | 1
1 0 | 1
1 1 | 0

XOR can be used to toggle bits. If I have a variable X and want to toggle the least siginificant bit, I XOR X with 1.

X = X ^ 1;

or

X ^= 1;

X: 0 1 0 1  0 1 0 1
1: 0 0 0 0  0 0 0 1
   ---------------- XOR
   0 1 0 1  0 1 0 0

X: 0 1 0 1  0 1 0 0
1: 0 0 0 0  0 0 0 1
   ---------------- XOR
   0 1 0 1  0 1 0 1

As you can see, it works both ways. Be careful when using XOR, as you explicitly have no control over the resulting value; you only make sure it has changed, so keep track of the value it may have had before.

 

To make our list of operators complete, let's sum up all 16 possible output combinations and see when they're useful and when not.

   NUL      AND     AND ~      A     ~ AND       B       XOR      OR    ~( OR )  ~( XOR )    ~B       OR ~     ~A      ~ OR   ~( AND )    ONE
A B | U  A B | U  A B | U  A B | U  A B | U  A B | U  A B | U  A B | U  A B | U  A B | U  A B | U  A B | U  A B | U  A B | U  A B | U  A B | U
----+--  ----+--  ----+--  ----+--  ----+--  ----+--  ----+--  ----+--  ----+--  ----+--  ----+--  ----+--  ----+--  ----+--  ----+--  ----+--
0 0 | 0  0 0 | 0  0 0 | 0  0 0 | 0  0 0 | 0  0 0 | 0  0 0 | 0  0 0 | 0  0 0 | 1  0 0 | 1  0 0 | 1  0 0 | 1  0 0 | 1  0 0 | 1  0 0 | 1  0 0 | 1
0 1 | 0  0 1 | 0  0 1 | 0  0 1 | 0  0 1 | 1  0 1 | 1  0 1 | 1  0 1 | 1  0 1 | 0  0 1 | 0  0 1 | 0  0 1 | 0  0 1 | 1  0 1 | 1  0 1 | 1  0 1 | 1
1 0 | 0  1 0 | 0  1 0 | 1  1 0 | 1  1 0 | 0  1 0 | 0  1 0 | 1  1 0 | 1  1 0 | 0  1 0 | 0  1 0 | 1  1 0 | 1  1 0 | 0  1 0 | 0  1 0 | 1  1 0 | 1
1 1 | 0  1 1 | 1  1 1 | 0  1 1 | 1  1 1 | 0  1 1 | 1  1 1 | 0  1 1 | 1  1 1 | 0  1 1 | 1  1 1 | 0  1 1 | 1  1 1 | 0  1 1 | 1  1 1 | 0  1 1 | 1

As you may see, some operations aren't useful, like the NUL and ONE operator, just use hard values in these cases. The A, B, ~A and ~B operator ignore one parameter, so leave the ignored parameter out.

Then we find the three btiwise operators AND, OR and XOR and their twins ~( AND), ~( OR ) and ~( XOR ). When I write ~( AND ), read it like X = ~(A & B );

There are four operators left, which I named ~ AND, ~ OR, AND ~ and OR ~. These operators take one of their two parameters inverted, they are asymmetrical; you cannot flip the two parameters and get the same result. As an example the ~ AND operator is written like X = ~A & B;

Edited by roadrunner84
Link to post
Share on other sites

ACTUALLY, looks like you have are not enabling the Pull UP on p1.3. P1REN enables the pull resistor, but p1out determines if that resistor is a pull UP (1) or pull DOWN (0). p1out = bit3; should work.

 

You are a bloody genius. That was exactly the problem.

 

I tried writing:

 

P1OUT = 0x00 + BIT3;

 

And the problem is completely fixed. I know that initializing ports this way is bad practice, but I just wanted to know why. Now I know!

 

I do have a little question about the reason why the error came up in the first place.

 

First, I just though that P1OUT was a registry that determined what the state of each pin would be, should the pin be set to OUTPUT. I though that if the pin was set to INPUT, then the value that that pin had in the P1OUT registry would be asolutely meaningless. But reading what you wrote it turns out it also determines the setting of the pull up/down resistor. Is there a reason for this?

 

Also, in the code, after defining P1OUT I wrote:

 

P1REN |= BIT3;

 

Shouldnt this had overriden what I did with P1OUT? (Obviously it didnt because it didnt work, but I am curious as to why)

 

Finally, if I pushed the switch with the pull up/down resistor improperly set up, shouldn't I have shorted the pin and destroyed the MCU?

 

Sorry for the question tsunami!

 

Also roadrunner84, thank you a lot for the detailed tutorial. It was a very good/clear read.

Link to post
Share on other sites

First, I just though that P1OUT was a registry that determined what the state of each pin would be, should the pin be set to OUTPUT. I though that if the pin was set to INPUT, then the value that that pin had in the P1OUT registry would be asolutely meaningless. But reading what you wrote it turns out it also determines the setting of the pull up/down resistor. Is there a reason for this?

 

Also, in the code, after defining P1OUT I wrote:

 

P1REN |= BIT3;

 

Shouldnt this had overriden what I did with P1OUT? (Obviously it didnt because it didnt work, but I am curious as to why)

 

Finally, if I pushed the switch with the pull up/down resistor improperly set up, shouldn't I have shorted the pin and destroyed the MCU?

It's set up that way because it's easier for them to reuse a register, than to have an extra register that's only used if p1ren is set. And because the msp430s have both pull-ups like most mcus, but also have pull-downs, unlike AVRs or PICs. So they need a register to define which level the pin is at, 0 or 1. Since P1Out determines that anyway, they reused the same register.

 

Setting P1Ren doesn't affect p1out for the same reason, since there is pull ups AND pull downs, they use whatever is in p1out to determine which resistor is used. If setting p1ren overrided p1out, it could cause issues.

 

As for destroying the pin or mcu, no, because with the r1.5 launchpad, the p1.3 button has no external pull up. Without the external pull up, if you set no internal pull resistor, the line would be either floating (when the button is not pressed) or directly grounded (when the button is pressed). With a pull-down enabled and a button that pulls to ground, the pin would always be at ground, so there is no short circuit. The only way to possibly damage the mcu (Without adding external components) would be to have p1.3 set to output high, then pressing the p1.3 button. Direct short of vcc to ground.

 

As a note, the r1.4 launchpad does have the external pull-up 47k (and cap) on the p1.3 line. Had you enabled the internal pull-down, which is roughly 50k, the two resistors would have created a voltage divider and the voltage at the pin would have been roughly half VCC (~1.85v), which might not be enough to trigger the voltage level change at that pin.

Link to post
Share on other sites

There are five properties of a digital I/O pin:

  • PxIN: the input value
  • PxOUT: the output value
  • PxDIR: the pin direction (input/output)
  • PxREN: the resistor enable
  • PxSEL(2): the special function

First and foremost, the behaviour of the pin can be different for every pin when it is in second/third/fourth function mode, so the rest of the story only holds when PxSEL is 0 and if it exists PxSEL2 is also 0.

Now then, PxIN holds the read back value, it always does, no matter the other three properties. However, when the pin is set as output PxIN will hold the output value, not the actual level of the pin.

PxDIR | PxIN
------+-----
  0   | Pin level
  1   | PxOUT level

PxREN enables the resistor, this means that the pin is connected via a resistor to PxOUT. As a consequence the voltage on the pin can be overridden by external driving high or low. Contrary to intuition (at least mine), PxREN does not change behaviour depending on PxDIR.

PxOUT determines the output drive level, this value is ignored if PxDIR is 0 and PxREN is 0.

PxDIR is the direction, 1 is output, 0 is input. When PxREN is low, this changes the pin from diriving high or low to the pin being a floating (high impedance, HiZ) input pin. However, if PxREN is enabled, PxDIR does not change the pin from driving to floating. If PxDIR is 1 however, PxIN will hold the value of PxOUT, not the actual pin level!

PxOUT PxDIR PxREN external | PxIN external
---------------------------+--------------
  0     0     0      0     |   0     0
  0     0     0      1     |   1     1
  0     0     0     HiZ    |   ?    HiZ    PxIN logic may become unstable; undesired power dissipation
  0     0     1      0     |   0     0
  0     0     1      1     |   1     1
  0     0     1     HiZ    |   0     0     Pull-down enabled
  0     1     0      0     |   0     0     Driving an output pin; not recommended; may damage circuit
  0     1     0      1     |   0   short   High current influx; may damage circuit
  0     1     0     HiZ    |   0     0
  0     1     1      0     |   0     0
  0     1     1      1     |   0     1     Pin voltage ignored; PxIN equals PxOUT
  0     1     1     HiZ    |   0     1     Pull-up enabled. Pin voltage ignored; PxIN equals PxOUT
  1     0     0      0     |   0     0
  1     0     0      1     |   1     1
  1     0     0     HiZ    |   ?    HiZ    PxIN logic may become unstable; undesired power dissipation
  1     0     1      0     |   0     0
  1     0     1      1     |   1     1
  1     0     1     HiZ    |   1     1     Pull-up enabled
  1     1     0      0     |   1   short   High current outflux; may damage circuit
  1     1     0      1     |   1     1     Driving an output pin; not recommended; may damage circuit
  1     1     0     HiZ    |   1     1
  1     1     1      0     |   1     0     Pin voltage ignored; PxIN equals PxOUT
  1     1     1      1     |   1     1
  1     1     1     HiZ    |   1     1     Pull-up enabled
Link to post
Share on other sites

Thanks cde I understood everything perfectly.

 

Setting P1Ren doesn't affect p1out for the same reason, since there is pull ups AND pull downs, they use whatever is in p1out to determine which resistor is used. If setting p1ren overrided p1out, it could cause issues.

 

So when the pin is set to OUTPUT, is the resistor enabled or disabled? Because the way I see it, if the resistor is enabled, having it set to PULL UP and having the output set to LOW would consume a small current that would be a bit unnecessary. It wouldn't change the function of the pin, but it could be a bit wasteful. Here is the drawing I am basing my analisis from, maybe it is wrong:

 

Configuracion+para+los+botones.gif

 

On the drawing on the right, if Vout was set to LOW there would be a current through the pull up resistor that would serve no purpose.

 

 

 

However, when the pin is set as output PxIN will hold the output value, not the actual level of the pin.

 

Wouldn't the value at PxOUT always be the actual level of the pin?

 

Also, when the pin is set to output, why would I read the values from PxIN, when I could just read them from PxOUT?

 

Thanks for the responses by the way, I feel like I learnt more in this thread than on most of the tutorials that I've read.

Link to post
Share on other sites

Pull up / down would be if the pin is an input.

 

You're using a pin to monitor something, like a switch connecting to ground. So, you tie the pin high, with a high-resistance.

When the switch closes and connects the pin to ground, there is less resistance through the switch than the resistor holding the pin high so the pin gets pulled low.

There's a "falling" edge transition for you to deal with.

 

Conversely, the switch connects to Vcc, a switch connecting to the supply voltage. So you would tie the pin low, with a high-resistance.

When the switch closes and connects to Vcc, there is less resistance through the switch than the resistor holding the pin low so the pin gets pulled high.

There's a "rising" edge transition for you to deal with.

Link to post
Share on other sites

Thanks cde I understood everything perfectly.

 

 

So when the pin is set to OUTPUT, is the resistor enabled or disabled? Because the way I see it, if the resistor is enabled, having it set to PULL UP and having the output set to LOW would consume a small current that would be a bit unnecessary. It wouldn't change the function of the pin, but it could be a bit wasteful. Here is the drawing I am basing my analisis from, maybe it is wrong:

 

Configuracion+para+los+botones.gif

 

On the drawing on the right, if Vout was set to LOW there would be a current through the pull up resistor that would serve no purpose.

 

 

 

Wouldn't the value at PxOUT always be the actual level of the pin?

 

Also, when the pin is set to output, why would I read the values from PxIN, when I could just read them from PxOUT?

 

Thanks for the responses by the way, I feel like I learnt more in this thread than on most of the tutorials that I've read.

If PxRen is set (pull resistors enabled) and PxDir is set (output mode), PxOut would affect both the direct output AND the resistor. At no point would they have different values. You Would essentially have a shorted out resistor, so it wouldn't affect the line, it would act like it is not there. Unless there is voltage sag on the direct output, then there would a very slight amount of current draw.

Having pxren set and setting the pin to output are mutually exclusive. Setting a pin to output does not disable the resistor, and the output level (0 or 1) still determines which pull resistor is used.

 

As for PxOut vs PxIn, well, think of that as a light in your house. The Pin is the light, PxOut is the light switch, and PxIn is your eyes. You can turn On the light switch, and the light should light up. But it doesn't. If you look at the light switch, its set to on. But your eyes tell you the light is off.

Link to post
Share on other sites
Wouldn't the value at PxOUT always be the actual level of the pin?

Also, when the pin is set to output, why would I read the values from PxIN, when I could just read them from PxOUT?

Thanks for the responses by the way, I feel like I learnt more in this thread than on most of the tutorials that I've read.

No, PxOUT is only the actual level of the pin when the pin is set as output in the PxDIR register and either not driven externally or the resistor is disabled in PxREN.

The actual level of the pin is in PxIN.

The odd thing is that PxOUT can toggle the resistor from pull-up to pull-down when PxREN is enabled, no matter PxDIR. In this case PxDIR does influence whether you'd read the value you WANT to set on the pin (using pull-up/down) or the value that IS on the pin.

RLD1CAK.png

As you can see in this image, PxDIR is partially overridden by PxREN, while the schmitt trigger from the pin (right side) to PxIN is disabled when PxDIR is high. The resistor is connected to a switch that is set by PxREN, whose voltage is either Vcc or Vss, depending on PxOUT.

In this picture there is also PxSEL, but assume it to be 0 for normal pin operation.

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...