3 common C language skills in embedded development
[Copy link]
1. Operation register
In embedded development, registers are often operated, such as writing to and reading from registers. Each register has its own inherent address, and it is particularly important to access these addresses through C language.
Here, we give an example. This is a macro definition of a status register. First, through unsigned int, we can know that this register is 32 bits. Because we want to avoid reading data directly from the cache during program execution, we use volatile to modify it. The value at this address must be read again every time. First, (volatile unsigned int *) is a pointer, let's assume it is p.
The address it stores is 0x560000B0, and then we take the value of this address, which is *p, so the source code becomes (* (volatile unsigned int *) 0x560000B0). Next, we can directly assign it to GSTATUS1 to change the value stored at address 0x560000B0.
Sometimes, you will see an assignment like this. In fact, this is similar to what we just talked about. The only difference is that here the pointer is assigned at the same time as it is defined. Here, the structure S3C2410_NAND is first defined, which contains all 32-bit variables. A pointer of this structure type is also defined, and it points to the address 0x4e000000, which means that at this moment, s3c2410nand points to an actual physical address.
The s3c2410nand pointer accesses the NFSTAT variable, but we want its address, not the value at the address. So we use & to get the address of NFSTAT, and then force it to be converted into an unsigned char pointer and assigned to p, so that we can directly assign a value to NFSTAT through p.
2. Operate function pointers
Pointers can not only point to variables, strings, and arrays, but also to functions. In C language, the entry address of a function can be assigned to a pointer. In this way, functions can be accessed through pointers. Function pointers can also be passed as parameters. Function pointers can simplify the code and reduce the workload when modifying the code. You will understand this through the following explanation.
It should be easy to understand through the comments that function pointers are similar to variable pointers and string pointers. If you understand this small program, then it will be much easier to understand the following source code about Nand flash.
This is a source code for operating Nand Flash. First, we see a structure is defined, which contains function pointers. They are waiting to be assigned values. Then a variable nand_chip of this structure is defined. Then comes the function declaration to be operated.
These functions will be called by functions in other files, because there is usually only one statement in these functions, which is to call the function pointer of the structure.
Next, we can see the function declarations for the two architectures. Then, in the nand_init function, we assign a value to nand_chip, which is what we just talked about, assigning the function's entry address to the pointer. Now nand_chip has been assigned a value. If we want to read and write Nand, we only need to call nand_chip.read_data() or nand_chip.write_cmd() and other functions.
This is a convenient point. Another point is that this code is highly portable. If we use a new chip, we don't need to change the whole code. We just need to add the judgment of the new chip in the nand_init function and assign a value to nand_chip. So I said that function pointers make the code portable and easy to modify.
3. Manipulate the register bits
In combination with what we just said, first define the register macro so that we can assign values to it directly. In bit operations, we need to learn how to clear the target bit to 0 in line 2 of the program, here we clear bit 3 to 0. Line 3 sets bit 3 to 1.
|