In this section, SiliconLabs will share lessons learned in software development. What are the keywords extern, staTIc and volaTIle? Should you use recursion or malloc() in your code?
1) Find an existing software example of a hardware device
The first step in developing any embedded solution is to find examples that can make your task easier. The software examples for the specific sections you find in your custom solution will help you "view" the device in another way and help you reinterpret the device specifications, even if the examples are for other computer architectures or software languages.
2) compiler code
There is no perfect computer software language. All languages ​​have their own strengths and weaknesses. The software language used in Simplicity Studio for the EFM32 family is C. The C language has a long history, it is widely trusted, and performs well in embedded design, but its syntax and features are difficult to master. When you code in C, you are actually writing instructions for the compiler and other build tools. Remember this. The C language is a "near metal" language because there are only a few steps between the code written in your human-readable format, the assembly code, and the results of the binary image's build process.
C code has a strict type that requires certain variables to match well enough to perform a secure assignment. This is to protect you from stupid things, such as the address of variables (ie pointers) and the contents of variables. But often in embedded development, you need to be able to convert pure numbers to addresses in order to specify register addresses. This requires you to be familiar with type conversions to tell the compiler that you really know what you are doing.
3) Use descriptive variables and function names
The best thing you can do is make sure your code is well designed, using descriptive variables and function names. There is no runtime loss associated with long names in C code. When the build tool converts C code to binary machine code, all identifiers are removed. Consider the following code snippet found in the FAT file system (FF) library:
The above code has some comments, which of course helps, is a very good thing, but it's hard to know the exact cause of this code by looking at variables, functions, enumerations, and preprocessor symbols. Consider using the following code instead:
Yes, the code is a bit wide and hard to type, but Simplicity Studio provides code completion with CTRL+Spacebar shortcuts that you can cut and paste at any time. The code is more readable and requires less search for variable names. We can see by looking at the second example, this code is designed to look at the target directory and break when it finds deleted (previously populated but now available) or zero (unfilled) short filename entries in the target directory. . Descriptive names allow you to read the code as you read a story and tell you what you want while reading.
4) Treat comments seriously
A good software developer adds a lot of comments to the code in a few key places. Comments, such as long variable names, do not affect the file size of the executable binary at runtime, just there to help read the documentation for the code. The top of each file in the solution should state the purpose of the file, and there should be a long comment at the top of each function indicating the purpose of the function and describing the input and output. In addition to these key points, comments should be used on a line-by-line basis, regardless of the intent of the code. Using descriptive variable names can help explain the purpose of the code and reduce the necessary comments to make the comments there more prominent. Believe me, you won't remember the purpose of writing the code a year later, so pay attention to the comments!
5) Use the emlib library
For EFM32 programmers, the emlib library is your friend. When accessing EFM32 peripherals, call these libraries as much as possible. These libraries are well tested and have extra code to help find the problem, not just the direct adjustment of the registers.
For example, the following code uses the emlib library:
TIMER_TopSet(TIMER3, 1000);
The same thing can be done by preprocessor-defined registers that address the memory-mapped peripherals, defining TIMER3 as 0x40010C00. We don't use this address because it's hard to remember, but this is where TIMER3 is mapped in main memory.
TIMER3-"TOP = 1000;
All peripherals are mapped to memory addresses in exactly the same way, so sometimes you will see an example of using this pointer notation instead of the emlib library function. If you see the TIMER_TopSet function definition in em_timer.h, you will see that the function is exactly the same as this example, so in this case the library function does not provide any additional values. However, using the emlib library sometimes gets more functionality than the simple operation map register. For example, the CMU_ClockEnable function carefully makes a lot of decisions on your behalf before finally using the "bit band" command to ensure that the register bits are automatically set. Use these library functions as often as possible to get the convenience of all EFM32 library designers.
6) Define variables to avoid stack and heap issues
Many aspects of C are not obvious to non-professional programmers, but become important when running code in embedded designs. For beginners, all locally declared variables are on the stack. These are the variables you define in a function or any block of code.
The stack is the area of ​​memory starting from the "top of memory" or the highest available address in physical RAM and then counting down until the stack limit is reached. If you define too many local variables, or your code dynamically creates these variables by using recursion or other nested functions, your stack space will be filled.
Global variables are variables defined outside of all functions and other code blocks at the module level. The compiler automatically allocates memory for globally declared variables on the heap. This is part of the main memory pool outside the stack. If you try to allocate too much RAM, a compiler error will result. However, using the malloc() command in your code dynamically allocates RAM in the heap at runtime.
Using the recursion or malloc() command on an embedded processor with limited RAM is an adventurous task! You must understand how many recursive attempts (or malloc() calls) your code will need to solve the problem and then design a solution that will never run out of stack space.
If you define all the variables in your code and let the compiler determine how to automatically manage memory, you will encounter fewer problems than stack or heap. Even with such precautions, if your code is almost available in RAM size, when you compile and build your code, you will need to learn how to monitor the stack and heap size, which is beyond the scope of this section.
7) Differences between global static variables and local static variables
Variables defined with the keyword "static" represent different ranges of different content. In an internal function, the static keyword is used before the variable to remember its value between function calls. It has a "stickiness" that you can initialize on the first call to the function and then have it hold its value instead of reinitializing the non-static variable each time the function executes. In the global scope, all variables are "sticky" because they are only initialized once at runtime, and then remember their values. However, the static keyword placed before the global variable indicates that the compiler is local to the module and is not used by the external module. This is a completely different meaning for the same "static" keyword.
8) The meaning of volatile and extern and how they affect each other
As long as variables and functions are not declared static in the module, they can be used outside the module and used in other modules. To tell the compiler that you intend to use the same variables in the module, you define a regular method variable in a module and add the keyword "extern" before the definition of all other modules in the design. Now all modules in your design can access the same variable. However, if one of the other modules in the design is intended to modify the value of a variable other than the one originally defined, the keyword "volatile" must be added before the variable. This volatile keyword tells the compiler that the variable can be changed outside of the module and prevents the optimizer from deleting statements that appear to have no effect.
In addition, when using the Release version and the Debug version, it is very important to use volatile. When the optimization settings increase, the compiler will actively try to compress unnecessary code. This means that you need to prevent the compiler from doing this, and you can change any variable outside the current scope by using the volatile keyword.
Distribution Box
Distribution Box,Distribution Boxes TUV,CE Distribution Boxes,Distribution Boxes
Wenzhou Korlen Electric Appliances Co., Ltd. , https://www.korlen-electric.com