Trying to understand registers on MSP430G2553

Discussion in 'Embedded Systems and Microcontrollers' started by StrongPenguin, Nov 7, 2018 at 3:46 PM.

  1. StrongPenguin

    Thread Starter Member

    Jun 9, 2018
    72
    2
    Code (Text):
    1.  
    2. #include <msp430.h>
    3.  
    4. void main(void) {
    5.     WDTCTL = WDTPW | WDTHOLD;       // Stop watchdog timer
    6.  
    7.     P1DIR |= (BIT0+BIT6);           // P1.0 (Red LED), P1.1 (Green LED)
    8.  
    9.     while(1)
    10.     {
    11.         volatile unsigned long i;
    12.  
    13.         P1OUT &= ~BIT6;             //Green LED -> OFF
    14.         P1OUT |= BIT0;              //Red LED -> ON
    15.  
    16.         for(i = 0; i<10000; i++);   //delay
    17.  
    18.         P1OUT &= ~BIT0;             //Red LED -> OFF
    19.         P1OUT |= BIT6;              //Green LED -> ON
    20.  
    21.         for(i = 0; i<10000; i++);   //delay
    22.     }
    23. }
    24.  
    I am trying to wrap my head around the register setups/operations, but it's not going so well, I must say. So I have a bunch of questions.

    The watch dog, in @MrChips (hope it's ok that I'm calling you here) tutorial, the line to stop it is WDTCTL = WDTPW + WDTHOLD;, but here it's an OR operation. EDIT: Upon writing this post, I realized that they are the same, but I will let it stay.

    I understand that you can say WDTCTL = WDTPW | WDTHOLD, because you have two variables to OR together. But I don't understand P1OUT &= ~BIT0;, nor do I understand the other register operations. What is the value of P1OUT? What is it being & with? I understand ~BIT0, that just means flip bit 0, right? How does P1OUT |= BIT0 turn on the led? 0 + 0 = 1?

    What confuses me even more, is that if I remove the & from the OFF part, it still works as intended :confused:

    Do you guys have any exercises for getting comfortable with these operations? I'm pretty much through my beginner C book (K&R book in the mailbag), and the bitwise operations have only been mentioned, not dealt with.
     
  2. MrChips

    Moderator

    Oct 2, 2009
    17,040
    5,268
    What you are seeing are some special operators in C.

    Here is an example.
    We can add a value A to a variable X using:
    X = X + A

    You can write this in C as:
    X += A

    which can be read as "add A and assign to X".

    The same rule applies to all other binary (i.e. two operands) operators.
    For example, to form X = X OR A

    we can write X = X | A

    or

    X |= A

    So what is the result of

    P1OUT &= ~BIT6

    This is effectively saying:

    P1OUT = P1OUT (AND) (NOT BIT6)

    (You can work out for yourself what happens if we eliminate the P1OUT on the right hand side of the expression.

    You can read about C operators here.
    https://www.tutorialspoint.com/cprogramming/c_operators.htm
     
    StrongPenguin likes this.
  3. KeepItSimpleStupid

    Distinguished Member

    Mar 4, 2014
    2,868
    503
    To SET a bit; psuedocode

    A=A or (bit value)

    A=A or 128; Sets bit 7 if labeled 0-7 and little endian

    ===

    To CLEAR a bit

    A = A and NOT (bit value)

    A=A and not (128)

    Some of this stuff gets wierd when playing with the sign bit.
    Something called "sign" extension is used when you say have a 12 bit two complement number and your operating in a 16 bit world.
     
  4. StrongPenguin

    Thread Starter Member

    Jun 9, 2018
    72
    2
    @MrChips Ahhh...didn't think of that! Makes a whole lot more sense now.

    In your blog, you address the bits directly, like this.
    Code (Text):
    1.  
    2.        P1OUT_bit.P0 = 1;  // turn on LED
    3.        P1OUT_bit.P0 = 0;  // turn off LED
    4.  
    But when I try this, I just get a compile error. Do I have to add something in my .h file if I wan't this "feature" (I'm still learning the terminology..)? Or I can just OR the bits like P1OUT = BIT0 | BIT2 | BIT4 ?
     
    Last edited: Nov 7, 2018 at 5:25 PM
  5. StrongPenguin

    Thread Starter Member

    Jun 9, 2018
    72
    2
  6. MrChips

    Moderator

    Oct 2, 2009
    17,040
    5,268
    To elaborate:

    what is the difference between

    X = X OR 00010000

    and

    X = 00010000

    What is the difference between
    X = X AND 11101111

    and

    X = 11101111

    where the numbers shown represent binary numbers.
     
  7. MrChips

    Moderator

    Oct 2, 2009
    17,040
    5,268
    What IDE (compiler) are you using?
     
  8. StrongPenguin

    Thread Starter Member

    Jun 9, 2018
    72
    2
    @MrChips I'm not sure I understand the question, but X = X OR 00010000 performs an OR operation on each bit from both variables. If X contains all 0, then the result is 00010000. Same procedure goes for the AND.

    I use Code Composer Studio v8.
     
  9. MrChips

    Moderator

    Oct 2, 2009
    17,040
    5,268
    But what happens if X is not all 0?
     
  10. MrChips

    Moderator

    Oct 2, 2009
    17,040
    5,268
    Code Composer Studio redirects msp430.h to msp430g2553.h
    You can open this file and look at the contents. I does not create unions to bits.

    Here is an example of how to create a union for P1OUT_bit
    Code (Text):
    1. __no_init volatile union
    2. {
    3.   unsigned char P1OUT;   /* Port 1 Output */
    4.  
    5.   struct
    6.   {
    7.     unsigned char P0              : 1; /*  */
    8.     unsigned char P1              : 1; /*  */
    9.     unsigned char P2              : 1; /*  */
    10.     unsigned char P3              : 1; /*  */
    11.     unsigned char P4              : 1; /*  */
    12.     unsigned char P5              : 1; /*  */
    13.     unsigned char P6              : 1; /*  */
    14.     unsigned char P7              : 1; /*  */
    15.   }P1OUT_bit;
    16. } @0x0021;
    You can add this to your main.c or to the header file.
     
  11. StrongPenguin

    Thread Starter Member

    Jun 9, 2018
    72
    2
    I don't think this answer will suffice, but X is not all 0, then whatever X is will get added to 00010000. I haven't had the oppertunity to play around with bit operators yet, since I can't find a 220 ohm resistor for the leds, but soon I will. You are more than welcome to reveal the answer, If I was wrong :)

    Regarding the union code. You start with __no_init because you don't wan't the union to set the registers upon startup or reset, right? I can't seem to find this in the CCS compiler manual, but this is what I could Google.
     
  12. geekoftheweek

    Active Member

    Oct 6, 2013
    173
    22
    Actually it means flip all bits. Forgive me for using bit 6 for an example... I just realized what I did, but I'm not going to rewrite it out.

    Your bit6 would actually be represented as 01000000 in binary.
    The ~bit6 in binary would be 10111111

    Say you have:
    X = 10101010
    bit6 = 01000000

    X | bit6 = 11101010 --> (10101010 | 01000000)
    X & ~bit6 = 10101010 --> (10101010 & 10111111)
    X & bit6 = 00000000 --> (10101010 & 01000000)

    If you change X to 01010101:
    X | bit6 = 01010101 --> (01010101 | 01000000)
    X & ~bit6 = 00010101 --> (01010101 & 10111111)
    X & bit6 = 01000000 --> (01010101 & 01000000)


    Because you are assigning 10111111 (~bit6) or 11111110 (~bit0) to the register. The led will work as intended, but it will also alter the other bits of the register which I'm sure you don't want.

    Also the reason WDTCTL = WDTPW + WDTHOLD works out the same as WDTCTL = WDTPW | WDTHOLD is most likely how the bits are arranged. For example if you:
    1 + 1 = 2 (00000001 + 00000001 = 00000010)
    1 | 1 = 1 (00000001 | 00000001 = 00000001)

    BUT
    2 + 1 = 3 (00000010 + 00000001 = 00000011)
    2 | 1 = 3 (00000010 | 00000001 = 00000011)

    You may already understand from what I gathered, but just thought I'd write it out anyways.

    To confuse matters worse I may have the bits backwards since I've never worked with MSP430G2553. Most computers, microcontrollers, and circuits represent numbers with bit 0 on the right, but there are various older Sun, IBM, and others that reversed the bits and bit 0 was the left most bit.

    The way I had to learn this stuff was to start writing things out as 8 (or 16 or 32) ones and zeros and go from there. It will in the long run help with learning how hexidecimal numbers work and you'll soon see simple math tricks you can use here and there. Instead of dividing a number by 2 or any multiple of 2 you can simply shift the bits to the right however many you need to go, but you'll lose any remainder which in cases won't matter anyways. The same with multiplying by 2 or multiples of 2... just shift the bits left however many you need to. In some instances division takes numerous instruction cycles due to there's no dedicated hardware and has to be done in software. Multiplication hardware seems to be fairly common.

    Hope that make a little sense...
     
    Last edited: Nov 10, 2018 at 5:34 PM
    StrongPenguin likes this.
  13. StrongPenguin

    Thread Starter Member

    Jun 9, 2018
    72
    2
    @geekoftheweek Thanks for that post, very well explained.

    You say "Because you are assigning 10111111 (~bit6) or 11111110 (~bit0) to the register. The led will work as intended, but it will also alter the other bits of the register which I'm sure you don't want"

    Can I then assume, that all registers are always 1, unless specified otherwise?
     
  14. MrChips

    Moderator

    Oct 2, 2009
    17,040
    5,268
    In a nutshell

    PORT = PORT OR BIT6
    is setting bit-6 of PORT while leaving all other bits unchanged

    PORT = PORT AND ~BIT6
    is clearing bit-6 of PORT while leaving all other bits unchanged

    where BIT6 is defined as 01000000
     
    StrongPenguin likes this.
  15. Mark Hughes

    Member

    Jun 14, 2016
    279
    24
    @StrongPenguin
    TI provides code that allows you to control the MSP430 at the assembly level, register level (which you are using), and the library level. If you're not comfortable with controlling things at the register level -- look into the library level. Then you're using functions to turn pins on or off instead of bitmasking (which gets me into trouble all the time).
    In CCS (or IAR), ensure that you've got the correct board specified, and have the correct lnk file added to your project (lnk_msp430g2553.cmd).
    1st thing -- you need to add this line to your code: at line 6: "PM5CTL0 &= ~LOCKLPM5;" The default state of the pins is a high-impedance mode -- until you get out of that high-impedance mode, those pins are going to do absolutely nothing, and the rest of your code is useless.
    I don't see that in your code -- which means the pins are stuck in high-impedance state...so nothing is happening.

    Here's a quick overview:
    PORTxDIR sets the port direction -- 0 is input, 1 is output. If you're only using GPIO 1.6 as an LED output (vs. a switch input), and you don't care about any other pins, you'd type PORT1DIR = BIT6;
    Somewhere in that .cmd file, or another header file BIT6 is defined as 0x0040, or 0B01000000;

    PORTxOUT sets the output -- 0 is logic low, 1 is logic high. If you want to set only GPIO1.6 at logic high to turn on a LED, you'd type PORT1OUT = BIT6; This does not ignore the other pins -- it actively modifies the other pins.
    PORTxSELx can enable other functions for the pin if available -- SPI, I²C, etc.. You don't need this in your code.

    Add the line of code I posted above right after you come out of reset -- then let us know if you still have issues.
     
    Last edited: Nov 10, 2018 at 10:16 PM
    StrongPenguin and geekoftheweek like this.
  16. geekoftheweek

    Active Member

    Oct 6, 2013
    173
    22
    I may have confused terminology...
    I tend to call the physical connections the "port" and the actual memory location you are working with a "register". What you are doing is manipulating a byte in memory that corresponds to the physical pins of the micro controller. Even though you are wording it as individual bits the compiler will treat is as an eight bit number and assign the rest of the bits when compiling to perform the change in memory needed for the individual port pin if it works as I think it would. I normally use assembly for my microcontroller projects so my C usage is strictly PC related and I may be missing possible optimizations the compiler can do.

    It seems that by @MrChips response you will want to think of it as the whole eight bits also. If you are a glutton for punishment look up "The Art of Assembly Language" online and skip the first couple pages of your search result. Usually you can find it somewhere on a university web site for their students or anyone who stumbles across it. There are several versions out there and some sites are better put together than others so it may take a few more tries to find a good one. Even though it was written for early PC processors the concepts are similar enough since it is more or less a motherboard crammed into a single chip.
    Once your brain is numb check out MSP 430 Family Instruction Set. If you then felt the need you could write inline assembly (instructions) that would work solely with the single bit you want to manipulate instead of all eight of them.

    @Mark Hughes weighed in while I was poking around online and covered some interesting stuff also.
     
    StrongPenguin likes this.
  17. StrongPenguin

    Thread Starter Member

    Jun 9, 2018
    72
    2
    Thanks for all the good answers. Very helpful.

    @MrChips Thanks for clearing this up. I have now connected LEDs to all ports, just to play around with some boolean arithmetic and general practice.

    @Mark Hughes Glad to hear I'm not the only one being fooled my registers. By library level, you mean using the BITx in the main header file? Also, the "PM5CTL0 &= ~LOCKLPM5" line won't compile, I cant find it in the header file.

    @geekoftheweek Yes, I think some things are starting to make more and more sense now. But there also seems to be many abstractions, like you say port vs. register. I'm also having a hard time making sens of that question. What part of the memory am I playing with? Flash? RAM, ROM?
     
  18. geekoftheweek

    Active Member

    Oct 6, 2013
    173
    22
    You are working with RAM in this case. If you haven't downloaded the datasheet yet do so and starting on page 61 it lists what memory addresses correspond to what peripheral or port. Table 6-32 breaks down the individual addresses for PORT1 and PORT2. When you say P1OUT |= BIT0 what you are doing is oring bit 0 to the memory address 0202h or the 514th byte of RAM which then turns bit 0 on.

    I'm kind of glad I clicked on this question in the first place. What little I've poked around the datasheet this looks like something that I may want to play around with some day.

    Happy tinkering...
     
    Last edited: Nov 11, 2018 at 6:00 PM
  19. geekoftheweek

    Active Member

    Oct 6, 2013
    173
    22
    Usually a library is a set of functions to make life easier. Instead of:
    P1OUT |= BIT0
    you'll find something like
    set_port_bit(PORT1, BIT0)

    They're also used to be able to share code for things like LCD displays, external communications, and other outside peripherals you can add. Kind of like not having to reinvent the wheel to get things up and running.

    Not knowing the specifics I can just offer a general idea...
     
  20. Mark Hughes

    Member

    Jun 14, 2016
    279
    24
    @Mark Hughes Glad to hear I'm not the only one being fooled my registers. By library level, you mean using the BITx in the main header file? Also, the "PM5CTL0 &= ~LOCKLPM5" line won't compile, I cant find it in the header file.

    @geekoftheweek Yes, I think some things are starting to make more and more sense now. But there also seems to be many abstractions, like you say port vs. register. I'm also having a hard time making sense of that question. What part of the memory am I playing with? Flash? RAM, ROM?[/QUOTE]

    @StrongPenguin Can you show me your current source code and the error message(s)? Are you sure you put a semicolon at the end of the line? I'm 99% confident that until we get that problem solved, nothing else will happen.

    By library level -- I mean that there are functions inside gpio.c that you can use to affect your LEDs(e.g. GPIO_setAsOutputPin(port, pins), setOutputHighOnPin(port, pins); ) You are loading them when you include msp430.h (e.g. #include <msp430.h>.)

    Also -- We have to solve the "PM5CTL0 &= ~LOCKLPM5;" problem. I'm 99.5% certain nothing will happen until we solve that problem.

    Please post your source code and the error codes. Thanks!
    Mark
     
  21. MrChips

    Moderator

    Oct 2, 2009
    17,040
    5,268
    Sure you can use library functions and make your code portable.

    However, I cannot imagine code more simple than:

    RED_LED = OFF;
    GREEN_LED = ON;
     
Loading...