我目前正在尝试为基于 AVR 的步进电机控制器构建一个字符串解析器。这个想法是通过 UART 观察一些输入字符串,然后将该字符串分解为几个子缓冲区,这些子缓冲区最终将进入一组电机控制行为。考虑以下分隔符:
'<' - START
'%' - 更改缓冲区
'>' - 停止
“A”- 顺时针
'B' - 逆时针
我会采用一个看起来像 <1234A%7743B> 的任意宽的字符串,并将其转换为“电机 1 上顺时针旋转 1234 步,电机 B 上逆时针旋转 7743 步”。
到目前为止,我已经想出了以下解决方案
#include <stdio.h>
#include <stdint.h>
#define TARGET 12
typedef struct
{
uint8_t STRING_SEL;
char X [32]; // buffer X
char Y [32]; // buffer Y
uint8_t iX; // buffer X iterator
uint8_t iY; // buffer Y iterator
uint8_t PTR_CTR;
char* Xptr; // pointer to buffer X
char* Yptr; // pointer to buffer Y
uint8_t * iXptr; // pointer to buffer X iterator
uint8_t * iYptr; // pointer to buffer Y iterator
char ** PTR_ARR [2]; //pointer to array of buffer initial value pointers
uint8_t ** CTR_ARR[2]; //pointer to array of buffer iterator pointers
} parser_manager;
static parser_manager mgr = {
.STRING_SEL = 0,
.X = {[0 ... 31] = 0},
.Y = {[0 ... 31] = 0},
.iX = 0,
.iY = 0,
.PTR_CTR = 0,
.Xptr = &mgr.X[0],
.Yptr = &mgr.Y[0],
.iXptr = &mgr.iX,
.iYptr = &mgr.iY,
.PTR_ARR = {&mgr.Xptr, &mgr.Yptr},
.CTR_ARR = {&mgr.iXptr, &mgr.iYptr}
};
这让我陷入了三星级编程地狱,并导致令人讨厌的会员访问,例如:
char EXPECT_CHAR = *(*(*(mgr.PTR_ARR + mgr.PTR_CTR)) + (***(mgr.CTR_ARR + mgr.PTR_CTR)));
(mgr.Y[TARGET] == EXPECT_CHAR) ? printf("1\n") : printf("0\n");
那么为什么要做这么丑陋的事情呢?我这样做的理由是,在解析传入字符时,我会为每个“%”字符增加 PTR_CTR,从而干净地切换迭代器和缓冲区。然后,我可以通过添加缓冲区和指向结构的指针,然后扩大 PTR 和 CTR 数组,将其扩展到 N 个电机。
我可以看到我可以使用计数器数组为自己节省一定的间接级别,因为它不需要是双指针数组。
这感觉是解决这个问题的一种极其不优雅的方式。如何使这个过程更干净?到目前为止,我唯一的想法是转向固定宽度的数据包,消除对这种虚假动态的需求。
无需深入了解实现细节,当您将属于同一黄油的东西收集到一个对象中时,实现可能会变得更容易理解,例如:
typedef struct
{
char buf[32]; // Buffer
uint8_t it; // Buffer iterator
char ** PTR_ARR; //pointer to array of buffer initial value pointers
uint8_t ** CTR_ARR; //pointer to array of buffer iterator pointers
...
} motor_t
#define N_MOTORS 2
motor_t motors[N_MOTORS];
然后有一个函数,它需要一个
motor_t*
指针来完成特定任务,例如:填充缓冲区、将值映射到运动行为等。
PTR_ARR
和CTR_ARR
仍然看起来像黑客,并且有一个更直接的数据结构。
您有
X
和 Y
作为结构成员 parser_manager
:
char X [32]; // buffer X
char Y [32]; // buffer Y
你也提到过
然后,我可以通过向结构添加缓冲区和指针,然后扩大 PTR 和 CTR 数组,将其扩展到 N 个电机。
你打算怎么做? (通过在
X
结构中添加更多成员,例如 Y
和 parser_manager
!)
最好创建一个具有成员
X
和 Y
的不同结构。在 parser_manager
结构中获取该结构的指针,以便可以通过重新分配内存来扩展它。这样,您就可以在访问 X
和 Y
缓冲区时消除多层间接寻址。
用于演示目的的示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char X[32];
char Y[32];
} buffers;
typedef struct {
buffers * ptr_x_y;
int numele;
} parser_manager;
int main()
{
parser_manager mgr = {NULL, 0};
mgr.numele = 1;
mgr.ptr_x_y = malloc (sizeof (buffers) * mgr.numele);
if (!mgr.ptr_x_y) {
exit (EXIT_FAILURE);
}
strcpy (mgr.ptr_x_y[0].X, "DUMMY_X1");
strcpy (mgr.ptr_x_y[0].Y, "DUMMY_Y1");
printf ("%s, %s\n", mgr.ptr_x_y[0].X, mgr.ptr_x_y[0].Y);
mgr.numele = 2;
buffers * temp;
mgr.ptr_x_y = realloc (temp = mgr.ptr_x_y, mgr.numele * sizeof (buffers));
if (!mgr.ptr_x_y) {
free (temp);
exit (EXIT_FAILURE);
}
strcpy (mgr.ptr_x_y[1].X, "DUMMY_X2");
strcpy (mgr.ptr_x_y[1].Y, "DUMMY_Y2");
printf ("%s, %s\n", mgr.ptr_x_y[0].X, mgr.ptr_x_y[0].Y);
printf ("%s, %s\n", mgr.ptr_x_y[1].X, mgr.ptr_x_y[1].Y);
// free dynamically allocted memory
return 0;
}
iX
和iY
是缓冲区X
和Y
的迭代器,因此您也可以使它们成为buffers
结构的成员。