In embedded development, as projects progress, issues such as reinventing the wheel, code dispersion, and maintenance difficulties are common. How to systematically build your own general-purpose function library (“library of wheels”) that can be efficiently reused and continuously evolved is a challenge every developer faces.
Benefits of Building a Function Library
- • Reduce Redundant Work: Highly reusable common functions avoid “implementing the same functionality in multiple places”.
- • Improve Development Efficiency: Unified interfaces and comprehensive documentation lower the entry barrier for new members.
- • Enhance Code Quality: Centralized optimization and unified testing reduce hidden bugs.
- • Facilitate Maintenance and Expansion: Modular design makes it easier to expand functionalities and locate issues later.
How to Achieve This?
Requirement Analysis and Module Division
- 1. Prioritize High-Frequency General Functions: Such as data types, memory operations, checksum algorithms, file/storage, data structures, string processing, protocol parsing, logging, time, etc.
- 2. Clear Module Boundaries: Each module should only perform one type of task, with a single interface and clear responsibilities.
- 3. Layered Design: Basic utility libraries (such as types, memory, strings) should be independent of business logic, while business-related general modules (such as protocols, device abstractions) should be separated into their own layers.
- 4. Prioritize Portability: Minimize platform dependencies and use adaptation layers when necessary.
Design Principles
- • Simplified and Unified Interfaces: Consistent naming conventions and parameter styles, avoiding “universal interfaces”.
- • Comprehensive Documentation and Examples: Each module should come with a README and test cases.
- • Easy Integration and Customization: Support for static/dynamic library compilation, clear organization of Makefiles, and ease of customization.
Design and Usage of General Modules
Data Types and Common Macros (types.h)
Standardize data types and common macros to enhance code readability and portability.
// types.h
#ifndef TYPES_H
#define TYPES_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
typedef int8_t s8;
typedef uint8_t u8;
typedef int16_t s16;
typedef uint16_t u16;
typedef int32_t s32;
typedef uint32_t u32;
typedef float f32;
typedef double f64;
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0]))
#endif
Checksum Algorithm Module (crc.h/.c)
Provides common CRC16/CRC32 algorithms with a simple interface for easy portability.
// crc.h
#ifndef CRC_H
#define CRC_H
#include <stdint.h>
uint16_t crc16_ccitt(const uint8_t *data, uint32_t len, uint16_t init);
uint32_t crc32_simple(const uint8_t *data, uint32_t len, uint32_t init);
#endif
// crc.c
#include "crc.h"
uint16_t crc16_ccitt(const uint8_t *data, uint32_t len, uint16_t init) {
uint16_t crc = init;
for (uint32_t i = 0; i < len; ++i) {
crc ^= (uint16_t)data[i] << 8;
for (int j = 0; j < 8; ++j)
crc = (crc & 0x8000) ? (crc << 1) ^ 0x1021 : (crc << 1);
}
return crc;
}
uint32_t crc32_simple(const uint8_t *data, uint32_t len, uint32_t init) {
uint32_t crc = init;
for (uint32_t i = 0; i < len; ++i) {
crc ^= data[i];
for (int j = 0; j < 8; ++j)
crc = (crc & 1) ? (crc >> 1) ^ 0xEDB88320 : (crc >> 1);
}
return crc;
}
File Operation Module (fileutil.h/.c)
Encapsulates common file operations with safe and easy-to-use interfaces.
// fileutil.h
#ifndef FILEUTIL_H
#define FILEUTIL_H
#include <stddef.h>
int file_read_all(const char *path, void *buf, size_t maxlen);
int file_write_all(const char *path, const void *buf, size_t len);
#endif
// fileutil.c
#include "fileutil.h"
#include <stdio.h>
int file_read_all(const char *path, void *buf, size_t maxlen) {
FILE *fp = fopen(path, "rb");
if (!fp) return -1;
size_t n = fread(buf, 1, maxlen, fp);
fclose(fp);
return (int)n;
}
int file_write_all(const char *path, const void *buf, size_t len) {
FILE *fp = fopen(path, "wb");
if (!fp) return -1;
size_t n = fwrite(buf, 1, len, fp);
fclose(fp);
return (int)n;
}
General Data Structure Module
Dynamic Array (dynarray.h/.c)
Supports dynamic arrays of any type, with an interface style similar to C++ vectors.
// dynarray.h
#ifndef DYNARRAY_H
#define DYNARRAY_H
#include <stddef.h>
typedef struct {
void *data;
size_t elem_size;
size_t size;
size_t capacity;
} dynarray_t;
void dynarray_init(dynarray_t *arr, size_t elem_size);
void dynarray_free(dynarray_t *arr);
int dynarray_push_back(dynarray_t *arr, const void *elem);
void *dynarray_at(dynarray_t *arr, size_t idx);
#endif
// dynarray.c
#include "dynarray.h"
#include <stdlib.h>
#include <string.h>
void dynarray_init(dynarray_t *arr, size_t elem_size) {
arr->data = NULL; arr->elem_size = elem_size; arr->size = 0; arr->capacity = 0;
}
void dynarray_free(dynarray_t *arr) { free(arr->data); arr->data = NULL; arr->size = arr->capacity = 0; }
int dynarray_push_back(dynarray_t *arr, const void *elem) {
if (arr->size == arr->capacity) {
size_t newcap = arr->capacity ? arr->capacity * 2 : 4;
void *newdata = realloc(arr->data, newcap * arr->elem_size);
if (!newdata) return -1;
arr->data = newdata; arr->capacity = newcap;
}
memcpy((char*)arr->data + arr->size * arr->elem_size, elem, arr->elem_size);
arr->size++;
return 0;
}
void *dynarray_at(dynarray_t *arr, size_t idx) {
return (idx < arr->size) ? (char*)arr->data + idx * arr->elem_size : NULL;
}
Simple Hash Table (hashtable.h/.c)
Suitable for configurations, parameters, etc., supporting string keys.
// hashtable.h
#ifndef HASHTABLE_H
#define HASHTABLE_H
#include <stddef.h>
typedef struct hashtable hashtable_t;
hashtable_t *hashtable_create(size_t buckets);
void hashtable_destroy(hashtable_t *ht);
int hashtable_set(hashtable_t *ht, const char *key, void *value);
void *hashtable_get(hashtable_t *ht, const char *key);
#endif
Single Linked List (slist.h/.c)
Suitable for lightweight scenarios with a very simple interface.
// slist.h
#ifndef SLIST_H
#define SLIST_H
typedef struct slist_node {
void *data;
struct slist_node *next;
} slist_node_t;
void slist_push_front(slist_node_t **head, void *data);
void slist_free(slist_node_t *head);
#endif
// slist.c
#include "slist.h"
#include <stdlib.h>
void slist_push_front(slist_node_t **head, void *data) {
slist_node_t *node = malloc(sizeof(slist_node_t));
node->data = data; node->next = *head; *head = node;
}
void slist_free(slist_node_t *head) {
while (head) { slist_node_t *tmp = head; head = head->next; free(tmp); }
}
JSON and Struct Conversion (jsonutil.h/.c)
Encapsulates struct and JSON conversion using cJSON, suitable for configurations, protocols, etc.
// jsonutil.h
#ifndef JSONUTIL_H
#define JSONUTIL_H
#include <cJSON.h>
typedef struct {
int id;
char name[32];
} user_t;
cJSON *user_to_json(const user_t *user);
int json_to_user(const cJSON *json, user_t *user);
#endif
// jsonutil.c
#include "jsonutil.h"
cJSON *user_to_json(const user_t *user) {
cJSON *obj = cJSON_CreateObject();
cJSON_AddNumberToObject(obj, "id", user->id);
cJSON_AddStringToObject(obj, "name", user->name);
return obj;
}
int json_to_user(const cJSON *json, user_t *user) {
if (!cJSON_IsObject(json)) return -1;
user->id = cJSON_GetObjectItem(json, "id")->valueint;
strncpy(user->name, cJSON_GetObjectItem(json, "name")->valuestring, sizeof(user->name));
return 0;
}
Maintain Standards
- 1. Unify Directory Structure and Naming Conventions: For example, store modules under
<span><span>libcommon/</span></span>, concentrate header files in<span><span>include/</span></span>, and ensure global naming style consistency. - 2. Interface Documentation and Usage Examples: Each module should have header file comments, README, and typical usage code.
- 3. Regular Refactoring: Regularly review and clean up redundant and outdated code in the library.

END
Author:Psyducking
Source:Embedded Software GuesthouseCopyright belongs to the original author. If there is any infringement, please contact for removal..▍Recommended ReadingShare a powerful debugging tool for embedded development!Why do assessments for technical personnel mostly only consider overtime hours?I never expected that with this VSCode plugin, my embedded development efficiency would double!→ Follow for more updates ←