0

我有一个队列结构,我尝试使用循环缓冲区来实现它,我在网络应用程序中使用它。我正在寻找一些指导和反馈。首先,让我介绍一下相关代码。

typedef struct nwk_packet_type
{
    uint8_t dest_address[NRF24_ADDR_LEN];
    uint8_t data[32];
    uint8_t data_len;
}nwk_packet_t;

/* The circular fifo on which outgoing packets are stored */
nwk_packet_t nwk_send_queue[NWK_QUEUE_SIZE];
nwk_packet_t* send_queue_in; /* pointer to queue head */
nwk_packet_t* send_queue_out; /* pointer to queue tail */

static nwk_packet_t* nwk_tx_pkt_allocate(void)
{
    /* Make sure the send queue is not full */
    if(send_queue_in == (send_queue_out - 1 + NWK_QUEUE_SIZE) % NWK_QUEUE_SIZE)
        return 0;

    /* return pointer to the next add and increment the tracker */
    return send_queue_in++;//TODO: it's not just ++, it has to be modular by packet size
}

/* External facing function for application layer to send network data */
// simply adds the packet to the network queue if there is space
// returns an appropriate error code if anything goes wrong
uint8_t nwk_send(uint8_t* address, uint8_t* data, uint8_t len)
{
    /* First check all the parameters */
    if(!address)
        return NWK_BAD_ADDRESS;
    if(!data)
        return NWK_BAD_DATA_PTR;
    if(!len || len > 32)
        return NWK_BAD_DATA_LEN;

    //TODO: PROBABLY NEED TO START BLOCKING HERE
    /* Allocate the packet on the queue */
    nwk_packet_t* packet;
    if(!( packet = nwk_tx_pkt_allocate() ))
        return NWK_QUEUE_FULL;

    /* Build the packet */
    memcpy(packet->dest_address, address, NRF24_ADDR_LEN);
    memcpy(packet->data, data, len);
    packet->data_len = len;
    //TODO: PROBABLY SAFE TO STOP BLOCKING HERE

    return NWK_SUCCESS;
}

/* Only called during NWK_IDLE, pushes the next item on the send queue out to the chip's "MAC" layer over SPI */
void nwk_transmit_pkt(void)
{
    nwk_packet_t tx_pkt = nwk_send_queue[send_queue_out];
    nrf24_send(tx_pkt->data, tx_pkt->data_len);
}

/* The callback for transceiver interrupt when a sent packet is either completed or ran out of retries */
void nwk_tx_result_cb(bool completed)
{
    if( (completed) && (nwk_tx_state == NWK_SENDING))
        send_queue_out++;//TODO: it's not just ++, it has to be modular by packet size with in the buffer

}

好的,现在快速解释一下,然后是我的问题。所以基本的想法是我有这个队列用于发送到网络上的数据。该函数nwk_send()可以从应用程序代码中的任何位置调用,这将是一个小型的基于抢先任务的操作系统 (FreeRTOS),因此可以从代码中的许多地方发生并被操作系统滴答中断中断。

现在,由于该函数正在修改指向全局队列的指针,我知道它在这样做时需要阻塞。我对我应该在哪里阻塞(即禁用中断)的代码的评论是否正确?使用全局布尔变量或其他东西而不是仅仅禁用中断来制作互斥锁也会更聪明吗?

另外,我认为当事情被从队列中删除时,我应该阻止第二个地方,但我不确定它到底在哪里。它是nwk_transmit_pkt()在我实际将数据从队列中复制到本地 ram 变量中的地方吗?

最后一个问题,如何对数组中的指针进行取模运算?我觉得它应该看起来像:

send_queue_in = ((send_queue_in + 1) % (NWK_QUEUE_SIZE*sizeof(nwk_packet_t))) + nwk_send_queue;

非常感谢任何反馈,谢谢。

4

2 回答 2

1

关于锁定,最好使用您使用的操作系统中的一些现有互斥原语。我不熟悉 FreeRTOS,但它应该具有用于​​在中断和用户上下文之间锁定的内置原语。

对于循环缓冲区,您可以使用这些:

检查空队列

send_queue_in == send_queue_out

检查完整队列

(send_queue_in + 1) % NWK_QUEUE_SIZE == send_queue_out

push 元素 [伪代码]

if (queue is full)
    return error;
queue[send_queue_in] = new element;
send_queue_in = (send_queue_in + 1) % NWK_QUEUE_SIZE;

pop 元素 [伪代码]

if (queue is empty)
   return error;
element = queue[send_queue_out];
send_queue_out = (send_queue_out + 1) % NWK_QUEUE_SIZE;

看起来您复制而不只是在发送之前引用数据包数据。这意味着您可以保持锁定直到复制完成。

于 2013-07-16T00:47:58.263 回答
0

如果没有要开发的整体驱动程序框架,并且在与 uC 上的中断状态通信时,您需要非常小心。

您不能使用 OS 同步原语与中断状态进行通信。尝试这样做肯定会使您的操作系统崩溃,因为中断处理程序无法阻止。

应避免复制实际的批量数据。

在 8 位 uC 上,我建议将索引排队到缓冲区数组池中,其中缓冲区的数量小于 256。这意味着只需要排队一个字节,因此,通过在更新内部字节大小索引之前存储值的适当队列类,可以安全地将缓冲区通信到 tx 处理程序,而无需过多的中断禁用。

对池数组的访问应该是线程安全的,并且“插入/删除”应该很快——我在每个缓冲区结构中都有“succ/pred”字节字段,因此形成了一个双链表,访问受互斥体保护。与 I/O 一样,我将这个缓冲区池用于所有线程间通信。

对于 tx,从池中获取缓冲区结构,填充数据,将索引推送到 tx 队列,禁用中断仅足够长的时间以确定 tx 中断是否需要“primimg”。如果需要启动,则在重新启用中断之前将数据推入 FIFO 中。

当 tx 中断处理程序发送缓冲区时,它可以将“已使用”索引推回“清除”队列并发出信号量以使处理程序线程运行。然后,该线程可以从清除队列中获取条目并将其返回到池中。

该方案仅在中断处理程序不使用相同的缓冲方案重新启用更高优先级的中断时才有效。

于 2013-07-16T01:58:48.040 回答