[嵌入式C语言专精]利用链表实现有效的串口命令行模块

Debug_Lib

我们的目的是开发一款使用串口字符串进行系统内的命令操作的模块组件, 如:通过字符串命令来唤起指定的函数, 通过字符串命令来对系统内变量进行修改.

当然, 目前市面上也有着很多广泛应用的成熟项目, 如Json格式数据包, terminal终端的底层逻辑等等, 他们都实现了上述的功能, 尤其是Json, 可以让用户简便地进行大批量数据的交互.

但是你都写C语言了, 不自己造一遍轮子还叫C吗
在嵌入式系统中, 简单高效是必要的要求, 本工程便是用最简单的方法实现了较简便的交互数据包格式.

如何使用 How to use?

  1. 导入头文件#include “N_CmdString_debug.h”
  2. 定义函数句柄时, 参数应定义为(void* argument).
void cmdFunction(void* argument){
    printf("here is test function.\r\n");
    printf("Received argument: %s\r\n", (char*)argument);
}
  1. 声明好符合格式的结构体变量.
    debug_cmd_t cmd =
    		{
            .name = "cmd",
            .handlerFunc = cmdFunction,
    		};
    		
	N_debug_addCMD(&cmd);
  1. 将符合格式的字符串传入 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解析

  1. 将变量/命令注册进链表
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;
    }
}
  1. 对变量赋值/唤起指定函数
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;
}
  1. 接收字符串进入模块内部处理
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;
}

内部功能函数解析

  1. 字符域的预处理
/**
* @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;
}
  1. 针对泛型数据的预处理函数
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;
    }
}
  1. 字符串转换函数(示例)
/**
* 将字符串转换为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);
}
  1. 链表的实现
    这里使用的是侵入式链表, 具体内容请移步我个人主页中的
    [嵌入式C语言专精]侵入式链表的原理及其实战

开源工程链接及参考

开源链接:
我的github仓库

参考工程:

  1. cx_debug
  2. cJson
  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值