这是一个有点奇怪的用例,因此很难搜索现有的讨论。我正在为嵌入式系统(使用 XC16 编译器的 Microchip PIC24)编程,目前正在跨 3 个独立的 UART 通道(每个 UART 将从主数据表中获取数据)实现相同的通信协议。
我开始编写项目的方式是让每个 UART 由一个单独的模块处理,其中有很多代码重复,类似于以下伪代码:
UART1.c:
static unsigned char buffer[128];
static unsigned char pointer = 0;
static unsigned char packet_received = 0;
void interrupt UART1Receive (void) {
buffer[pointer++] = UART1RX_REG;
if (end of packet condition) packet_received = 1;
}
void processUART1(void) { // This is called regularly from main loop
if (packet_received) {
// Process packet
}
}
UART2.c:
static unsigned char buffer[128];
static unsigned char pointer = 0;
static unsigned char packet_received = 0;
void interrupt UART2Receive (void) {
buffer[pointer++] = UART2RX_REG;
if (end of packet condition) packet_received = 1;
}
void processUART2(void) { // This is called regularly from main loop
if (packet_received) {
// Process packet
}
}
虽然上述内容简洁且运行良好,但实际上通信协议本身相当复杂,因此将其复制 3 次(只需更改对 UART 寄存器的引用)会增加引入错误的机会。拥有一个函数并传递指向它的指针不是一种选择,因为这会对速度产生太大影响。代码需要在每个 UART 的内存中物理复制。
我想了很多,尽管知道从不将函数放在头文件中的规则,但还是决定尝试使用包含重复代码的特定头文件,并将引用作为#defined 值:
协议.h:
// UART_RECEIVE_NAME and UART_RX_REG are just macros to be defined
// in calling file
void interrupt UART_RECEIVE_NAME (void) {
buffer[pointer++] = UART_RX_REG;
if (end of packet condition) packet_received = 1;
}
UART1.c:
static unsigned char buffer[128];
static unsigned char pointer = 0;
static unsigned char packet_received = 0;
#define UART_RECEIVE_NAME UART1Receive
#define UART_RX_REG UART1RX_REG
#include "protocol.h"
void processUART1(void) { // This is called regularly from main loop
if (packet_received) {
// Process packet
}
}
UART2.c:
static unsigned char buffer[128];
static unsigned char pointer = 0;
static unsigned char packet_received = 0;
#define UART_RECEIVE_NAME UART2Receive
#define UART_RX_REG UART2RX_REG
#include "protocol.h"
void processUART2(void) { // This is called regularly from main loop
if (packet_received) {
// Process packet
}
}
当代码编译没有任何错误时,我有点惊讶!但它似乎确实有效,编译后 MPLAB X 甚至可以计算出所有符号引用,因此 UART1.c 和 UART2.c 中的每个宏引用都不会被识别为不可解析的标识符。然后我意识到我可能应该将protocol.h 文件重命名为protocol.c(并相应地更新#includes),但这实际上并不是什么大问题。
只有一个缺点:IDE 不知道在模拟或调试时单步执行包含在 protocol.h 中的代码时要做什么。它只是在代码执行时停留在调用指令处,因此调试会有点困难。
那么这个解决方案有多hacky?C神会因为我考虑这个而惩罚我吗?有没有我忽略的更好的选择?