C language Embedded System Programming Practice 3: Memory operations

Source: Internet
Author: User
Data Pointer

In embedded system programming, it is often required to read and write content in a specific memory unit, and the compilation has a corresponding mov command, other programming languages except C/C ++ do not have the ability to directly access absolute addresses. In the actual debugging of the embedded system, the read/write capability of the absolute address unit content is mostly achieved by using the C language pointer. Direct Memory operations with pointers occur in the following situations:

(1) an I/O chip is located in the CPU storage space rather than the I/O space, and the registers correspond to a specific address;

(2) two CPUs communicate with each other through dual-port RAM. The CPU needs to write content in a specific module (called mail box) of the dual-port RAM to interrupt the other's CPU;

(3) read the Chinese characters and English letters burned by a specific unit in ROM or flash.

For example:

Unsigned char * P = (unsigned char *) 0xf000ff00;
* P = 11;

The above program indicates that the absolute address 0xf0000 + 0xff00 (80186 uses the 16-bit segment address and 16-bit offset address) is written to 11.

When using an absolute address pointer, note that the result of the auto-increment and auto-increment operations of the pointer depends on the data category pointed to by the pointer. In the above example, the result after P ++ is P = 0xf000ff01. If P points to int, that is:

Int * P = (int *) 0xf000ff00;

The result of P ++ (or ++ P) is equivalent to: P = P + sizeof (INT), while P-(or-P) the result is P = p-sizeof (INT ).

Similarly, if you execute:

Long int * P = (long int *) 0xf000ff00;

Then the result of P ++ (or ++ P) is equivalent to: P = P + sizeof (long INT), while P-(or-P) the result is P = p-sizeof (long INT ).

Remember: the CPU is encoded in bytes, And the C language pointer uses the data type length to auto increment and auto increment. Understanding this is very important for directly operating the memory with pointers.

  Function pointer

First, you must understand the following three questions:

(1) in C language, the number of letters directly corresponds to the address of the instruction code generated by the function in the memory. Therefore, the function name can be directly assigned to the pointer pointing to the function;

(2) calling a function is actually equivalent to "transfer command + parameter transfer processing + regression position into stack ", essentially, the core operation is to assign the first address of the target code generated by the function to the PC register of the CPU;

(3) because the essence of function calling is to jump to the code of an address unit for execution, you can "call" a function entity that does not exist at all, dizzy? Please refer to the following link:

Take out any university textbook "Microcomputer principles" that you can obtain. The book says that after 186 CPU is started, jump to the absolute address 0xffff0 (corresponding to the C language pointer 0xf000fff0, 0xf000 for the segment address, 0xfff0 is an intra-segment offset). See the following code:

Typedef void (* lpfunction) ();/* defines a type without parameters or returned data */
/* Function pointer type */
Lpfunction lpreset = (lpfunction) 0xf000fff0;/* defines a function pointer pointing */
/* The position where the first command is executed after the CPU is started */
Lpreset ();/* call a function */

In the above program, we didn't see any function entity at all, but we executed such a function call: lpreset (), which actually played the role of "soft restart, jump to the position of the first command to be executed after the CPU is started.

Remember: The function does not have it, only the instruction set ears; you can call a function without the function body, essentially just starting to execute the instruction with another address!

  Array vs. Dynamic Application

In embedded systems, dynamic memory applications have stricter requirements than general system programming because the memory space of embedded systems is often very limited, accidental Memory leakage will quickly cause system crashes.

Therefore, make sure that your malloc and free appear in pairs. If you write such a program:

Char * function (void)
{
Char * P;
P = (char *) malloc (...);
If (P = NULL)
...;
... /* A series of operations on p */
Return P;
}

Call function () somewhere and use the dynamically applied memory in the function to free it, as shown below:

Char * q = function ();
...
Free (Q );

The above code is obviously unreasonable because it violates the principle of pairing malloc and free, that is, "who applies, who releases it. If this principle is not met, the coupling degree of the Code increases, because you need to know the internal details when calling the function!

The correct method is to apply for memory at the call and pass in the function, as follows:

Char * P = malloc (...);
If (P = NULL)
...;
Function (P );
...
Free (P );
P = NULL;

Function functions receive the following parameter P:

Void function (char * P)
{
... /* A series of operations on p */
}

Basically, the dynamic memory request method can be replaced with a large array. For beginners of programming, I recommend you use arrays as much as possible! The embedded system can receive flaws with a broad mind, but cannot make "Haina" errors. After all, Guo Jing, who has worked hard in the most stupid way, is better than Yang Kang, who is smart and smart but fails to follow the path of the counter-revolution.

Principles:

(1) Use arrays as much as possible, and arrays cannot be accessed across borders (if the truth goes beyond a step, it is a fallacy, And the array goes beyond the boundary to fulfill a chaotic embedded system );

(2) If dynamic application is used, you must determine whether the application is successful after application, and malloc and free should appear in pairs!
That is:

Int * P = (int *) 0xf000ff00;

The result of P ++ (or ++ P) is equivalent to: P = P + sizeof (INT), while P-(or-P) the result is P = p-sizeof (INT ).

Similarly, if you execute:

Long int * P = (long int *) 0xf000ff00;

Then the result of P ++ (or ++ P) is equivalent to: P = P + sizeof (long INT), while P-(or-P) the result is P = p-sizeof (long INT ).

Remember: the CPU is encoded in bytes, And the C language pointer uses the data type length to auto increment and auto increment. Understanding this is very important for directly operating the memory with pointers.

  Function pointer

First, you must understand the following three questions:

(1) in C language, the number of letters directly corresponds to the address of the instruction code generated by the function in the memory. Therefore, the function name can be directly assigned to the pointer pointing to the function;

(2) calling a function is actually equivalent to "transfer command + parameter transfer processing + regression position into stack ", essentially, the core operation is to assign the first address of the target code generated by the function to the PC register of the CPU;

(3) because the essence of function calling is to jump to the code of an address unit for execution, you can "call" a function entity that does not exist at all, dizzy? Please refer to the following link:

Take out any university textbook "Microcomputer principles" that you can obtain. The book says that after 186 CPU is started, jump to the absolute address 0xffff0 (corresponding to the C language pointer 0xf000fff0, 0xf000 for the segment address, 0xfff0 is an intra-segment offset). See the following code:

Typedef void (* lpfunction) ();/* defines a type without parameters or returned data */
/* Function pointer type */
Lpfunction lpreset = (lpfunction) 0xf000fff0;/* defines a function pointer pointing */
/* The position where the first command is executed after the CPU is started */
Lpreset ();/* call a function */

In the above program, we didn't see any function entity at all, but we executed such a function call: lpreset (), which actually played the role of "soft restart, jump to the position of the first command to be executed after the CPU is started.

Remember: The function does not have it, only the instruction set ears; you can call a function without the function body, essentially just starting to execute the instruction with another address!

  Array vs. Dynamic Application

In embedded systems, dynamic memory applications have stricter requirements than general system programming because the memory space of embedded systems is often very limited, accidental Memory leakage will quickly cause system crashes.

Therefore, make sure that your malloc and free appear in pairs. If you write such a program:

Char * function (void)
{
Char * P;
P = (char *) malloc (...);
If (P = NULL)
...;
... /* A series of operations on p */
Return P;
}

Call function () somewhere and use the dynamically applied memory in the function to free it, as shown below:

Char * q = function ();
...
Free (Q );

The above code is obviously unreasonable because it violates the principle of pairing malloc and free, that is, "who applies, who releases it. If this principle is not met, the coupling degree of the Code increases, because you need to know the internal details when calling the function!

The correct method is to apply for memory at the call and pass in the function, as follows:

Char * P = malloc (...);
If (P = NULL)
...;
Function (P );
...
Free (P );
P = NULL;

Function functions receive the following parameter P:

Void function (char * P)
{
... /* A series of operations on p */
}

Basically, the dynamic memory request method can be replaced with a large array. For beginners of programming, I recommend you use arrays as much as possible! The embedded system can receive flaws with a broad mind, but cannot make "Haina" errors. After all, Guo Jing, who has worked hard in the most stupid way, is better than Yang Kang, who is smart and smart but fails to follow the path of the counter-revolution.

Principles:

(1) Use arrays as much as possible, and arrays cannot be accessed across borders (if the truth goes beyond a step, it is a fallacy, And the array goes beyond the boundary to fulfill a chaotic embedded system );

(2) If dynamic application is used, you must determine whether the application is successful after application, and malloc and free should appear in pairs!

Keyword const

Const means "read-only ". The difference between the functions of the following code is very important, and it is also a long sigh. If you still don't know the differences between them and have been exploring the program for many years, it can only be said that this is a sorrow:

Const int;
Int const;
Const int *;
Int * const;
Int const * a const;

(1) The keyword const is used to send useful information to people who read your code. For example, adding the const keyword before a function parameter means that this parameter is not modified in the function body and is a "input parameter ". When there are multiple parameters, the function caller can clearly identify which are input parameters and which are possible output parameters based on whether the const keyword exists before the parameter.

(2) the rational use of the keyword const can enable the compiler to naturally protect those parameters that do not want to be changed, so as to prevent them from being accidentally modified by code, so as to reduce the appearance of bugs.

Const contains more meanings in C ++, while in C, it only means: "common variables that can only be read ", it can be referred to as "unchangeable variables" (This statement seems to be an easy-to-use, but most accurately expresses the essence of const in C ), the constants required in the compilation phase can only be defined in the # define macro! Therefore, the following program in C language is invalid:

Const int size = 10;
Char A [size];/* invalid: variables cannot be used during compilation */

  Keyword volatile

The C language compiler optimizes the code written by users, for example, the following code:

Int A, B, C;
A = inword (0x100);/* read the content of port 0 x of the I/O space and save it to variable */
B =;
A = inword (0x100);/* read the content of port 0 x of the I/O space again and save it to variable */
C =;

It is likely to be optimized by the compiler:

Int A, B, C;
A = inword (0x100);/* read the content of port 0 x of the I/O space and save it to variable */
B =;
C =;

However, such optimization results may cause errors. If the content of port 0 X in the I/O space is written to a new value by other programs after the first read operation, in fact, the content read from the 2nd read operations is different from that for the first time. The values of B and C should be different. Adding the volatile keyword before the definition of variable A can prevent similar optimization by the compiler. The correct method is:

Volatile int;

The volatile variable may be used in the following situations:

(1) Hardware registers of parallel devices (for example, Status Registers, codes in this example );

(2) Non-automatic variables (global variables) that will be accessed in an interrupt service subroutine );

(3) variables shared by several tasks in multi-threaded applications.

  Inconsistent processing of CPU character length and memory Bit Width

As mentioned in the background, this article selects a storage chip that is inconsistent with the CPU font length to solve the inconsistency between the CPU character length and memory bit width. The length of 80186 characters is 16, while that of NVRAM is 8. In this case, we need to provide an interface for reading and writing bytes and words for NVRAM, as shown below:

Typedef unsigned char byte;
Typedef unsigned int word;
/* Function: Read bytes in NVRAM
* Parameter: woffset. The read location is offset relative to the NVRAM base address.
* Return value: the read byte value.
*/
Extern byte readbytenvram (word woffset)
{
Lpbyte lpaddr = (byte *) (NVRAM + woffset * 2);/* why is the offset × 2? */

Return * lpaddr;
}

/* Function: Read the characters in NVRAM.
* Parameter: woffset. The read location is offset relative to the NVRAM base address.
* Return: The read word.
*/
Extern word readwordnvram (word woffset)
{
Word wtmp = 0;
Lpbyte lpaddr;
/* Read high byte */
Lpaddr = (byte *) (NVRAM + woffset * 2);/* why is the offset × 2? */
Wtmp + = (* lpaddr) * 256;
/* Read low byte */
Lpaddr = (byte *) (NVRAM + (woffset + 1) * 2);/* why is the offset × 2? */
Wtmp + = * lpaddr;
Return wtmp;
}

/* Function: Write a byte to NVRAM.
* Parameter: woffset, the write location offset relative to the NVRAM base address
* Bydata: The bytes to be written.
*/
Extern void writebytenvram (word woffset, byte bydata)
{
...
}

/* Function: write a word to NVRAM */
* Parameter: woffset, the write location offset relative to the NVRAM base address
* Wdata: the word to be written.
*/
Extern void writewordnvram (word woffset, word wdata)
{
...
}

Zi Gong asked: is the why offset multiplied by 2?

Zi Yue: see Figure 1. the interconnection between 16-bit 80186 and 8-bit NVRAM can only be based on the address line A1 to A0, And the A0 of the CPU itself is not connected to NVRAM. Therefore, the NVRAM address can only be an even number, so every time we move forward in 0x10!


Figure 1 connection between CPU and NVRAM address lines

Zi Gong asked: So why 80186's address line A0 is not connected to NVRAM's A0?

Zi Yue: Please refer to the IT Analects of Confucius and the microcomputer principles, which describes the Sage about computer composition.

  Summary

This article describes the skills related to memory operations in embedded system C programming. It is a basic requirement of an excellent C language programmer to have a deep understanding of data pointers, function pointers, dynamic application memory, const, and volatile keywords. After we have mastered these skills, we have learned 99% of C language, because the essence of C language is embodied in memory operations.

The reason why we use C language for Program Design in embedded systems is 99% because of its powerful memory operation capability!

If you love programming, please love C language;

If you love C, please love pointers;

If you love pointers, please love pointers!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.