Everyone engaged in embedded development writes code in C language. Many people are accustomed to using if and switch, and when defining data structures, they only use character types, integer types, arrays, and bits; rarely utilizing structs, unions, or enums. Today, I will summarize my own set of C programming methods.
1. Clever Use of C Language Header File Inclusion
When a header file is included by multiple C files, and that header file defines common variables for these C files, it will lead to redefinition errors during compilation. We usually adopt the following two methods to solve the above problem. (To clarify the problem, let’s assume three C files C1, C2, C3, and one header file H1. C1, C2, C3 have two common variables V1 and V2.) 1. Define variables V1 and V2 in C1, and declare V1, V2 as extern in C2 and C3; 2. Define variables V1 and V2 in C1, declare V1, V2 as extern in H1, and then include H1 in C2 and C3. Clearly, both methods require writing V1 and V2 at least twice, once for definition and once for external declaration, and not in the same file, which is not conducive to management and modification. Is there a way to place these common variables in one file and only write them once? First, we will write all the common variables we need into com.h file, adding a symbol EXT_ before each variable definition. When this header file is included in main.c, EXT_ is defined as empty, indicating that the variables in com.h are defined in main.c. When included in other files, EXT_ is defined as extern, indicating external declaration, as follows:
Com.h file:
// Prevent redefinition
#ifdef root
#define EXT_
#else
#define EXT_ extern
#endif
// Global variable EXT_ u8 variable1;
// This variable needs to be used in three C files Main.c
#define root // Define root before including com.h
#include “com.h”
2. Define Bus or Peripheral Addresses Using Structs When an overall structure contains multiple members of different types, it is usually defined using a struct. This way, memory will allocate these variables to adjacent addresses in an increasing manner (there will be padding in unaligned places), accessed via the format “struct name.member name”. However, there is also a pointer to the struct variable that can convert an address into a pointer of that struct type, such as the definition of registers: (The following is an excerpt from the STM32 firmware function library, regarding GPIO definitions)
typedef struct
{
vu32 CRL; // 0
vu32 CRH; // Offset 4
vu32 IDR; // Offset 8
vu32 ODR;
vu32 BSRR;
vu32 BRR;
vu32 LCKR;
} GPIO_TypeDef;
#define GPIOA_BASE ((u32)0x40010800) // The base address of GPIOA is 0x40010800
#define GPIOA (GPIO_TypeDef *) GPIOA_BASE; // Force type conversion to GPIO_TypeDef pointer
Thus, when operating on GPIOA’s registers, one can simply write:
Read: X = GPIOA->CRL; Write: GPIOA->CRL = X; or Read: X = (*GPIOA).CRL; Write: (*GPIOA).CRL = X; Of course, to achieve the above, one can also use the following method:
#define GPIOA_CRL 0x40010800
#define GPIOA_CRH 0x40010804
#define GPIOA_IDR 0x40010808
#define GPIOA_ODR 0x4001080C
#define GPIOA_BSRR 0x40010810
#define GPIOA_LCKR 0x40010814
Clearly, the first writing method is more standardized, and when defining multiple GPIOs, one only needs to force convert the base address of other GPIOs to that struct type pointer. Now let’s look at an example defining an external bus:
typedef struct
{
vu8 CH375_DATA;
vu8 CH375_CMD; // Offset 1
} CH375_TypeDef;
#define CH375 ((CH375_TypeDef *) 0x6c000000)
CH375-> CH375_DATA = data; // Write data to address 0x6c000000
CH375-> CH375_CMD = cmd; // Write command to address 0x6c000001
Isn’t this much more convenient? Importantly, the code’s aesthetics and readability have improved.
3. Use Enum Data Type to Define Specific States In practical problems, some variables may have values limited to a finite range. For example, a function may return several specific states during its operation: success, failure, busy, etc. If we directly use 0, 1, and 2 in the function to represent these three states, there may occasionally be mismatches between values and actual states, leading to incorrect state settings and judgments. Instead, we can use macros or enums to define these states in advance.
For example, using macro definitions:
#define Success 0
#define Failure 1
#define Busy 2
Using enum:
typedef enum
{
Success = 0, Failure, Busy
} FlagStatus;
4. Define Shared Memory Space Using Union Type The data defined by a union type shares the same memory space among multiple members, with the size of this space being the size of the largest member. Its usage is completely similar to that of a struct, but it is worth noting that multiple members cannot be referenced simultaneously; only one member can be used at a time. If there are many global variables, containing several structs and arrays, defining all of them would inevitably occupy a large amount of memory, potentially leading to insufficient memory in the microcontroller. If we can allow several arrays and struct variables that are not used simultaneously to share a segment of memory, it can save a lot of memory space. For example, the following input and output can share the same memory space if not used simultaneously.
union
{
struct
{
unsigned char Flag;
unsigned char Type;
unsigned char State;
unsigned long DataLen;
unsigned char Buffer[64];
} DataOut;
struct {
unsigned char Flag;
unsigned char Type;
unsigned char State;
unsigned long DataLen;
unsigned char Buffer[64];
} DataIn;
} BOC;
The C language is vast and profound, rich and colorful. When used well, it can play its role effectively. At the same time, developing good programming methods and cultivating good programming habits are extremely important for a designer. The above four points are some summaries I have accumulated and learned. I hope to exchange and learn together with everyone.
Our official QQ group 1: 281549832
Special thanks to netizens for their strong support.
Our open-source team is constantly expanding, and we hope everyone will join us soon.
Here, I still want to thank everyone for their strong support!
Everyone, come and follow us!