Debug_Lib
我们的目的是开发一款使用串口字符串进行系统内的命令操作的模块组件, 如:通过字符串命令来唤起指定的函数, 通过字符串命令来对系统内变量进行修改.
当然, 目前市面上也有着很多广泛应用的成熟项目, 如Json格式数据包, terminal终端的底层逻辑等等, 他们都实现了上述的功能, 尤其是Json, 可以让用户简便地进行大批量数据的交互.
但是你都写C语言了, 不自己造一遍轮子还叫C吗
在嵌入式系统中, 简单高效是必要的要求, 本工程便是用最简单的方法实现了较简便的交互数据包格式.
如何使用 How to use?
- 导入头文件#include “N_CmdString_debug.h”
- 定义函数句柄时, 参数应定义为(void* argument).
void cmdFunction(void* argument){
printf("here is test function.\r\n");
printf("Received argument: %s\r\n", (char*)argument);
}
- 声明好符合格式的结构体变量.
debug_cmd_t cmd =
{
.name = "cmd",
.handlerFunc = cmdFunction,
};
N_debug_addCMD(&cmd);
- 将符合格式的字符串传入 N_debug_handler 函数.
N_debug_handler("CMD cmd helloNino");
使用例 example
#include <stdio.h>
#include "N_CmdString_debug.h"
void cmdFunction(void* argument){
printf("here is test function.\r\n");
printf("Received argument: %s\r\n", (char*)argument);
}
int main(void) {
int a = 0;
int b = 200;
int PWM_Compare = 500;
float f = 0;
char char_buf[100];
debug_variable_t va =
{
.name = "a",
.pData = &a,
.type = DATATYPE_INT32
};
debug_variable_t vb =
{
.name = "b",
.pData = &b,
.type = DATATYPE_INT32
};
debug_variable_t cmp =
{
.name = "cmp",
.pData = &PWM_Compare,
.type = DATATYPE_INT16
};
debug_variable_t vf =
{
.name = "f",
.pData = &f,
.type = DATATYPE_FLOAT
};
debug_cmd_t cmd =
{
.name = "cmd",
.handlerFunc = cmdFunction,
};
N_debug_addVariable(&va);
N_debug_addVariable(&vb);
N_debug_addVariable(&vf);
N_debug_addVariable(&cmp);
N_debug_addCMD(&cmd);
printf("before:\n");
printf("a:%d\t b:%d\t cmp:%d\t f:%0.2f\r\n", a, b, PWM_Compare, f);
N_debug_handler("SET a 100");
N_debug_handler("SET b 1000");
N_debug_handler("SET cmp 3456");
N_debug_handler("SET f 0.2");
N_debug_handler("CMD cmd helloNino");
printf("after:\n");
printf("a:%d\t b:%d\t cmp:%d\t f:%0.2f\r\n", a, b, PWM_Compare, f);
return 0;
}
运行结果:
before:
a:0 b:200 cmp:500 f:0.00
here is test function.
Received argument: helloNino
after:
a:100 b:1000 cmp:3456 f:0.20
函数api解析
- 将变量/命令注册进链表
DebugError_t N_debug_addVariable(debug_variable_t *var) {
list_add(&(var->node), &variable_list.node);
if (variable_list.node.next != ((void *) 0)) {
return OK;
} else {
return ERROR_OVF;
}
}
DebugError_t N_debug_addCMD(debug_cmd_t *cmd) {
list_add(&(cmd->node), &cmd_list.node);
if (cmd_list.node.next != ((void *) 0)) {
return OK;
} else {
return ERROR_OVF;
}
}
- 对变量赋值/唤起指定函数
static DebugError_t debug_set_handler(char *input) {
char buf[32];
debug_variable_t *struct_ptr;
input = getStringField(buf, input);
list_for_each_entry(struct_ptr, &variable_list.node, node) {
if (strcmp(itemtovar(struct_ptr)->name, buf) == 0) {
input = getStringField(buf, input);
set_value(itemtovar(struct_ptr)->pData, buf, itemtovar(struct_ptr)->type);
break;
}
}
return OK;
}
static DebugError_t debug_command_handler(char *input) {
char buf[32];
debug_cmd_t *struct_ptr;
input = getStringField(buf, input);
list_for_each_entry(struct_ptr, &cmd_list.node, node) {
if (strcmp(itemtovar(struct_ptr)->name, buf) == 0) {
input = getStringField(buf, input);
struct_ptr->handlerFunc(buf);
break;
}
}
return OK;
}
- 接收字符串进入模块内部处理
DebugError_t N_debug_handler(char *input) {
char buf[32];
DebugError_t ret = OK;
input = getStringField(buf, input);
if (strcmp(buf, "SET") == 0) {
debug_set_handler(input);
} else if (strcmp(buf, "CMD") == 0) {
debug_command_handler(input);
} else {
ret = ERROR_NOMATCH;
}
return ret;
}
内部功能函数解析
- 字符域的预处理
/**
* @brief: 略过字符串中的空格
*/
static inline char *stringPassSpace(char *str) {
while ((*str == ' ')) {
++str;
}
return str;
}
/**
* @brief: 将dst的指针头指向第一个参数头, src的指针头指向第二个参数头
*/
char *getStringField(char *dst, char *src) {
int i = 0;
src = stringPassSpace(src);
for (; (src[i] != '\0') && (src[i] != ' '); i++) {
dst[i] = src[i];
}
dst[i] = '\0';
if (src[i] == ' ') {
src = stringPassSpace(src + i);
} else {
src = src + i;
}
return src;
}
- 针对泛型数据的预处理函数
static void set_value(void *pdata, char *value, datatype_t type) {
switch (type) {
case DATATYPE_INT8:
*((int8_t *) pdata) = (int8_t) N_format._StringToInt(value);
break;
case DATATYPE_UINT8:
*((uint8_t *) pdata) = (uint8_t) N_format._DecStringToUint(value);
break;
case DATATYPE_INT16:
*((int16_t *) pdata) = (int16_t) N_format._StringToInt(value);
break;
case DATATYPE_UINT16:
*((uint16_t *) pdata) = (uint16_t) N_format._DecStringToUint(value);
break;
case DATATYPE_INT32:
*((int32_t *) pdata) = (int32_t) N_format._StringToInt(value);
break;
case DATATYPE_UINT32:
*((uint32_t *) pdata) = (uint32_t) N_format._DecStringToUint(value);
break;
case DATATYPE_FLOAT:
*((float *) pdata) = (float) N_format._StringToFloat(value);
break;
case DATATYPE_DOUBLE:
*((double *) pdata) = (float) N_format._StringToFloat(value);
break;
case DATATYPE_STRING:
strcpy(pdata, value);
break;
default:
break;
}
}
- 字符串转换函数(示例)
/**
* 将字符串转换为float型变量
*/
static inline float StringToFloat(const char *str) {
float ret = 0;
float float_tmp = 0;
int int_tmp = 0, int_num = 0, float_num = 0;
int i = 0, negativeFlag = 0;
if (*str == '-') {
negativeFlag = 1;
++str;
}
for (; (str[i] != '.' && str[i] != '\0'); i++) {
++int_num;
}
for (int j = 0; j < int_num; j++) {
int_tmp *= 10;
int_tmp += str[j] - '0';
}
str += i;
if (*str == '\0') {
float_tmp = 0;
} else {
++str;
for (i = 0; str[i] != '\0'; i++) {
++float_num;
}
for (; float_num > 0; --float_num) {
float_tmp += (float) (str[float_num - 1] - '0');
float_tmp /= 10.0f;
}
}
ret = (float) int_tmp + float_tmp;
return (negativeFlag) ? (0.0f - ret) : (ret);
}
- 链表的实现
这里使用的是侵入式链表, 具体内容请移步我个人主页中的
[嵌入式C语言专精]侵入式链表的原理及其实战
开源工程链接及参考
开源链接:
我的github仓库
参考工程: