[英]C Message Encapsulation (SPI/nRF24 Driver): Any good practices?
我目前正在自制 SPI 驱动程序之上开发无线电 (nRF24) 模块驱动程序。 驱动非常简单:要通过无线电模块发送消息,您需要通过 SPI 发送消息,并以命令为前缀:
我让它完美地工作,但我正在努力封装用户有效载荷(即在用户有效载荷之前添加 NRF24_CMD)。
截至今天,每次发送消息时,我都会为 SPI 有效负载分配一个新的 char 数组。 我在数组的第一个字节中编写无线电模块命令,然后复制用户有效负载,如下所示:
void nrf24_send(char * payload, size_t length){
char spi_payload[MAX_DATA];
spi_payload[0] = RF_SEND_CMD;
for(int i=0; i<length; i++){
spi_payload[i+1] = payload[i];
}
spi_write(spi_payload, length + 1);
}
这会产生很多无意义的 memory 访问,只是为了复制用户有效负载,我觉得还有改进的余地。
封装用户消息的有效方法是什么?
驱动程序开发社区中是否有针对此类问题的良好实践?
供参考:
以下是我的一些想法:
用结构隐藏复杂性:
struct nrf24_message_t {
char cmd;
char payload;
}
谢谢你的帮助!
雨果
由于命令字节和有效负载数组的基础数据类型都是char
类型,因此您无需关心 alignment 问题——最重要的是,如果您一个接一个地定义,则两者之间不会有任何间隙。
我现在允许用户适当地分配 memory 并提供某种转换 function,例如:
// header:
// TODO: include guards, etc
struct nrf24_message;
struct nrf24_message* nrf24_createFromRaw(size_t size, char* raw);
// to allow the user to write to
size_t nrf24_bufferSize(struct nrf24_message* message);
char* nrf24_buffer(struct nrf24_message* message);
void nrf24_send(struct nrf24_message* message, size_t length);
// still a length parameter:
// maybe a message gets shorter than maximum the buffer size!
这是可见的用户界面,而实现可以看到此结构的详细信息:
//source:
struct nrf24_message
{
size_t payloadSize;
char cmd;
char payload[]; // flexible array member...
};
struct nrf24_message* nrf24_createFromRaw(size_t size, char* raw)
{
if(size <= sizeof(size_t) + 1) // < if you want to allow empty message
{
// failure! buffer is too short
return NULL;
}
struct message* m = (struct message*)raw;
m->payloadSize = size - sizeof(size_t) - 1;
return m;
}
size_t nrf24_bufferSize(struct message* nrf24_message)
{
return message->payloadSize;
}
char* nrf24_buffer(struct nrf24_message* message)
{
return message->payload;
}
void nrf24_send(struct nrf24_message* message, size_t length)
{
spi_write(&message->cmd, length + 1);
}
等等:现在用户可以提供 memory,而数据前面仍有空间。
使用示例:
char raw[256];
struct* nrf24_message = nrf24_createFromRaw(sizeof(raw), raw);
size_t length = snprintf(nrf24_buffer(message), nrf24_bufferSize(message), "...");
nrf24_send(message, length);
在非常低的级别上工作肯定是具有挑战性的,这是我过去使用的主要是 C 的一部分,它通过向调用者直接提供指向缓冲区的指针来避免这种复制
struct nrf24_message_t {
char buffer[MAX_SIZE];
};
static struct nrf24_message_t transmitBuffer; // assuming you have just one
// give the caller a pointer where it can stuff bytes, letting it
// know how much space is available.
char *prepareForTransmit(int *nbytes)
{
*nbytes = sizeof(transmitbuffer.buffer) - 1; // room for CMD
transmitbuffer.buffer[0] = RF_SEND_CMD; // or whatever
return transmitbuffer.buffer + 1; // room for CMD:
}
void transmit(int nbytes)
{
// do the SPI thing. be sure to +1 for the cmd byte.
}
然后在调用者中它可以请求缓冲区-注意最大大小-然后传输。 我不知道您的数据包的格式,但您应该能够理解。
{
....
int bufsize;
char *tbuf = prepareForTransmit(&bufsize);
// put stuff in tbuf up to the # of bytes available. Keep
// track of how many bytes you actually used!
transmit(nbytes);
}
编辑:如果您有多个可能的访问权限,那就更有趣了。 最好的是,如果您可以使用小型 memory 分配器,以便调用者拥有缓冲区,但它确实必须保护实际的传输 function,这样它们就不会相互重叠。
以上只是一个框架; 它甚至不是一个可以编译的完整示例。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.