How to write efficient and stable microcontroller code

How to write efficient and stable microcontroller code

Because the performance of a single-chip computer is different from that of a computer, it cannot be compared in terms of space resources, memory resources, and operating frequency. PC programming basically does not need to consider the space occupation and memory occupation, and the ultimate goal is to realize the function. For single-chip microcomputers, it is completely different. Generally, the Flash and Ram resources of single-chip microcomputers are measured in KB. It is conceivable that the resources of single-chip microcomputers are pitiful. For this reason, we must try to squeeze all its resources. To maximize its performance, program design must 
follow the following points for optimization:

  1. Use as small a data type as possible. 
    If you can use unsiged, you don't need signed; if you 
    can use char, you don't need int; if you 
    can use floating, you don't need it. 
    Can use bit manipulation without arithmetic.

  2. Using self-adding and self-subtracting instructions 
    Usually using self-adding, self-subtracting instructions and compound assignment expressions (such as a-=1 and a+=1, etc.) can generate high-quality 
    program code, and compilers can usually generate inc and dec with instructions like a=a+1 or a=a-1 
    , there are many C compilers that generate two to three byte instructions.

  3. Reducing the intensity of the 
    operation can replace the original complex expression with an expression that is less computationally intensive but has the same function. 
    (1) The remainder operation 
    N= N %8 can be changed to N = N &7 
    Note: Bit operations can be completed in only one instruction cycle, and most of the "%" operations of C compilers are 
    completed by calling subroutines , the code is long and the execution speed is slow. Usually, the only requirement is to find the remainder of the square of 2n, which can be replaced by the method of bit operation. 
    (2) The square operation 
    N=Pow(3,2) can be changed to N=3*3 
    Note: In the microcontroller with built-in hardware multiplier (such as the 51 series), the multiplication operation is much faster than the square operation, because the floating The square of the number of points 
    is realized by calling the subroutine. The subroutine of the multiplication operation has shorter code and faster execution speed than the subroutine of the square operation. 
    (3) Use displacement instead of multiplication and division 
    . N=M*8 can be changed to N=M<<3 
    N=M/8 can be changed to N=M>>3 
    Note: Usually, if you need to multiply or divide by 2n, you can Use the shift method instead. If you multiply by 2n, you can generate left 
    -shift code, while multiplying by other integers or dividing by any number calls the multiplication and division subroutine.
    The code obtained by the shift method is more efficient than the code generated by calling the multiplication and division  subroutine. In fact, as long as it is multiplied or divided by an integer, the result can be obtained by shifting. 
    For example, N=M*9 can be changed to N=(M<<3)+M; 
    (4) The difference between self-addition and self-subtraction. 
    For example, the delay function we usually use is realized by self-addition. 
    void DelayNms(UINT16 t) 

    UINT16 i,j; 
    for(i=0;i

define MAX(A,B) {(A)>(B)?(A):(B)}

Note: The difference between functions and macro functions is that macro functions take up a lot of space, while functions take up time. What everyone needs to know is that the 
function  call uses the system stack to save data. If the compiler has a stack check option, usually some assembly
statements are embedded in the function header to check the current stack; at the same time, the cpu also To save and restore the current scene when the function is called, push and pop the stack, so the 
function call needs some cpu time. The macro function does not have this problem. The macro function is only embedded into the current program as a pre-written code, and 
does not generate a function call, so it only takes up space. This phenomenon is especially prominent when the same macro function is frequently called.

  1. Use Algorithms Appropriately 
    If there is an arithmetic problem, find the sum of 1 to 100. 
    As programmers, we would click the keyboard without hesitation to write the following calculation method: 
    UINT16 Sum(void) 

    UINT8 i,s; 
    for(i=1;i<=100;i++) 

    s+=i; 

    return s; 

    Obviously everyone will think of this method, but the efficiency is not satisfactory. We need to use our brains to solve the problem by using mathematical algorithms to 
    improve the computational efficiency by a level. 
    UINT16 Sum(void) 

    UINT16 s; 
    s=(100 *(100+1))>>1; 
    return s; 

    The result is obvious, the same result with different calculation methods, the operating efficiency will be greatly different, so we need Maximize the efficiency of program execution through 
    mathematical methods.

  2. Replacing Arrays with Pointers 
    In many cases, pointer arithmetic can be used instead of array indexing, often resulting in faster and shorter code.
    Compared to array indexing  , pointers generally make code faster and take up less space. The difference is more pronounced when using multidimensional arrays. The following code works the same, 
    but the efficiency is not the same. 
    UINT8 szArrayA[64]; 
    UINT8 szArrayB[64]; 
    UINT8 i; 
    UINT8 *p=szArray; 
    for(i=0;i<64;i++)szArrayB[i]=szArrayA[i]; 
    for(i=0;i <64;i++)szArrayB[i]=*p++; 
    The advantage of the pointer method is that after the address of szArrayA is loaded into the pointer p, only need to increment p in each loop. In the array indexing 
    method, the complex operation of subscripting the array based on the i value must be performed in each loop.

  3. The essence of forced conversion 
    C language The first essence is the use of pointers, and the second essence is the use of forced conversions. Proper use of pointers and forced conversions can not only improve 
    program efficiency, but also make programs more concise. It occupies an important position, and the following 
    five  used as an explanation. Example
    1: Convert signed byte integer to unsigned byte integer 
    UINT8 a=0; 
    INT8 b=-3; 
    a=(UINT8)b; 
    end mode), converts the array a[2] to an unsigned 16-bit integer value. 
    Method 1: Use the displacement method. 
    UINT8 a[2]={0x12,0x34}; 
    UINT16 b=0; 
    b=(a[0]<<8)|a[1]; 
    Result: b=0x1234 
    Method 2: Coercion. 
    UINT8 a[2]={0x12,0x34}; 
    UINT16 b=0; 
    b= (UINT16 )a; //Forced conversion 
    result: b=0x1234 
    Example 3: Save the structure data content. 
    Method 1: Save one by one. 
    typedef struct _ST 

    UINT8 a; 
    UINT8 b; 
    UINT8 c; 
    UINT8 d; 
    UINT8 e; 
    }ST; 
    ST s; 
    UINT8 a[5]={0}; 
    sa=1; 
    sb=2; 
    sc=3; 
    sd=4; 
    se=5; 
    a[0]=sa; 
    a[1]=sb; 
    a[2 ]=sc; 
    a[3]=sd; 
    a[4]=se; 
    Result: The contents stored in array a are 1, 2, 3, 4, and 5. 
    Method 2: Coercion type conversion. 
    typedef struct _ST 

    UINT8 a; 
    UINT8 b; 
    UINT8 c; 
    UINT8 d; 
    UINT8 e; 
    }ST; 
    ST s; 
    UINT8 a[5]={0}; 
    UINT8 p=(UINT8 )&s;//cast 
    UINT8 i= 0; 
    sa=1; 
    sb=2; 
    sc=3; 
    sd=4; 
    se=5; 
    for(i=0;i

define Perror(FUN) printf("Err:%s %s %d: %s\n", FILE, func,LINE,FUN) The implementation of the linux-like perror function, here is the location of the file where the error occurred, the function where it is located, and it will cause The function FUN called on error.

Usage of # and ## in macros

define STR(s) #s

define CONS(a, b) int(a##e##b)

printf(STR(vck));//output vck 
printf(“%d\n”, CONS(2,3));//2e3 output 2000

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325683901&siteId=291194637
Recommended