Embedded C: Logs Access

Suppose we want to write at say 0xc000 , we can define a macro in C as:

 #define LCDCW1_ADDR 0xc000 #define READ_LCDCW1() (*(volatile uint32_t *)LCDCW1_ADDR) #define WRITE_LCDCW1(val) ((*(volatile uint32_t *)LCDCW1_ADDR) = (val)) 

My question is that when using any microcontroller, consider the example MSP430, the address of the P1OUT register is 0x0021.

But when we use P1OUT = 0xFFFF; // sets the value of P1OUT to 0xFFFF.

My question is how he writes to this address, for example. in this case 0x0021. IDE - IAR. I found in the header msp430g2553.h below the definition:

 #define P1OUT_ (0x0021u) /* Port 1 Output */ DEFC( P1OUT , P1OUT_) 

I assume that it defines the address, but where other macros write or read.

Can someone explain the stream that P1OUT writes at this particular address? Also let me know what u means in 0x0021u?

thanks


So far I have found the following data:

at msp430g2553.h

 #ifdef __IAR_SYSTEMS_ICC__ #include "in430.h" #pragma language=extended #define DEFC(name, address) __no_init volatile unsigned char name @ address; #define DEFW(name, address) __no_init volatile unsigned short name @ address; #define DEFXC volatile unsigned char #define DEFXW volatile unsigned short #endif /* __IAR_SYSTEMS_ICC__ */ #ifdef __IAR_SYSTEMS_ASM__ #define DEFC(name, address) sfrb name = address; #define DEFW(name, address) sfrw name = address; #endif /* __IAR_SYSTEMS_ASM__*/ #define P1OUT_ (0x0021u) /* Port 1 Output */ DEFC( P1OUT , P1OUT_) 

io430g2553.h says

 __no_init volatile union { unsigned char P1OUT; /* Port 1 Output */ struct { unsigned char P0 : 1; /* */ unsigned char P1 : 1; /* */ unsigned char P2 : 1; /* */ unsigned char P3 : 1; /* */ unsigned char P4 : 1; /* */ unsigned char P5 : 1; /* */ unsigned char P6 : 1; /* */ unsigned char P7 : 1; /* */ }P1OUT_bit; } @0x0021; 

Can someone explain what the above definition does? Details I found in the MSP430 IAR C / C ++ Compiler:

 Example of using __write and __read The code in the following examples use memory-mapped I/O to write to an LCD display: __no_init volatile unsigned char LCD_IO @ address; size_t __write(int Handle, const unsigned char * Buf, size_t Bufsize) { size_t nChars = 0; /* Check for stdout and stderr (only necessary if file descriptors are enabled.) */ if (Handle != 1 && Handle != 2) { return -1; } for (/*Empty */; Bufsize > 0; --Bufsize) { LCD_IO = * Buf++; ++nChars; } return nChars; } The code in the following example uses memory-mapped I/O to read from a keyboard: __no_init volatile unsigned char KB_IO @ 0xD2; size_t __read(int Handle, unsigned char *Buf, size_t BufSize) { size_t nChars = 0; /* Check for stdin (only necessary if FILE descriptors are enabled) */ if (Handle != 0) { return -1; } for (/*Empty*/; BufSize > 0; --BufSize) { unsigned char c = KB_IO; if (c == 0) break; *Buf++ = c; ++nChars; } return nChars; } 

Somebody knows?

+4
source share
3 answers

This is “how the compiler generates code from what I wrote,” and only the compiler authors can really answer this for you.

Obviously, there are several non-standard C components in the code above __no_init, using @, etc. In my reading, this tells the compiler that "this is an HW port that provides an unsigned char, and the address is 0xd2." The compiler will issue the correct instructions for reading and writing such a port - this is what works depending on the compiler, the processor for which the compiler generates code, etc.

The P10out structure defines the bit fields that are part of the C standard. Google is your friend here.

+4
source

The direction operator (unary * ) returns an l-value equivalent to the value at the pointer address.

 #define LCDCW1_ADDR 0xc000 void f() { uint32_t a = *(volatile uint32_t *)LCDCW1_ADDR; //reading from LCDCW1_ADDR *(volatile uint32_t *)LCDCW1_ADDR = 0xffff; //writing to LCDCW1_ADDR /*...*/ } 

Basically, the compiler is smart enough to see that the expression a = *addr; means "read the value from addr address and put it in a . At the same time *addr = 0xffff will be interpreted as" put 0xffff in addr address "

In your case, you can use the READ_LCDCW1() macro READ_LCDCW1() both the left and right sides of the assignment operator. There is no need for a separate macro WRITE_LCDCW1(val) . We can rewrite the previous code as:

 #define LCDCW1_ADDR 0xc000 #define LCDCW1 (*(volatile uint32_t *)LCDCW1_ADDR) void g() { uint32_t a = LCDCW1; //reading from LCDCW1_ADDR LCDCW1 = 0xffff; //writing to LCDCW1_ADDR /*...*/ } 

P1OUT macro from the IAR is most likely defined in the same way as LCDCW1 above (if you follow the DEFC() definition, you will eventually find something similar).

+1
source

My question is that when using any microcontroller, the example MSP430

You are not using a microcontroller, you are using an MSP430. It has an IO memory with memory mapping (which is very nice to use for us programmers). The display of memory varies by device. Answers to any questions related to the address can be found in the user guide for your specific device. TI makes a very good user guide. Find it for your specific device and read it carefully.

My question is how he writes to this address, for example. in this case 0x0021. IDE - IAR.

Compiler glue code. Your compiler’s provider will provide you with the necessary headers, macros, and functions to write to your device’s addresses. Use the code of the compiler provider if you can’t absolutely prove that it doesn’t work for your business (with IAR I would assume that 99.9% of this works, you get what you pay for. Perhaps there are implementation errors with a completely new device but probably not if you cannot prove it).

Also let me know what u means in 0x0021u?

From what you posted, this is the base address for port 1. It looks like you have 8 contacts on port 1 that you can control.

 #pragma language=extended 

From now on, you should assume that there are all kinds of “magic” (aka non-standard C) things that will happen. You can conclude what, in your opinion, the compiler does (and, for the most part, it is quite clear), however, this implementation is defined, which means that only the IAR compiler supports what comes next. Look at the compiler documents for specific commands and values. In particular, the characters __no_init and @ are not standard. __no_init will not initialize the variable when starting C (i.e. before starting main ()). @ Looks like an absolute address instruction to be provided to the linker (I may be wrong here).

 __no_init volatile union { unsigned char P1OUT; /* Port 1 Output */ struct { unsigned char P0 : 1; /* */ unsigned char P1 : 1; /* */ unsigned char P2 : 1; /* */ unsigned char P3 : 1; /* */ unsigned char P4 : 1; /* */ unsigned char P5 : 1; /* */ unsigned char P6 : 1; /* */ unsigned char P7 : 1; /* */ }P1OUT_bit; } @0x0021; 

This determines how to get specific byte bits for port 1. This allows you to manipulate IO pins. Some would say that OMG bitcodes are portable, implementation defined! Yes, they are right, but IAR is a developer, so in this case, just trust them to do the right thing.

Finally, you probably just want to use IAR macros as defined. You paid them a lot of money (unless you are using the free kickstart version). You can focus on writing your application and not manipulate bits this way. IAR does a good job of standardizing its names, so you can also use the same code (or very similar) for related parts. If you switch to another compiler, all of this will exit the window, and you will need to do this in a new compiler way. Good and bad points to this approach, there is probably no “right” answer.

0
source

All Articles