如何在MCU的串口上接收数据包?

时间:2010-06-11 04:06:59

标签: embedded

考虑在我的微控制器单元(MCU)上运行的代码:

while(1){

 do_stuff;
 if(packet_from_PC)
  send_data_via_gpio(new_packet); //send via general purpose i/o pins
 else
  send_data_via_gpio(default_packet); 
 do_other_stuff;

}

MCU也通过UART连接到PC。无论何时PC向MCU发送数据,都会发送 new_packet , 否则发送 default_packet 。每个数据包可以是5个或更多字节,具有预定义的数据包结构。

我的问题是:

1.我应该在UART中间服务程序(ISR)中使用PC收到整个数据包吗?在这种情况下,我必须实施 ISR中的状态机来组装数据包(if-else或switch-case块可能很长)。

                  OR

2.让PC发送某种REQUEST命令(一个字节),在我的ISR中检测它设置一个标志,单独禁用UART中断并通过检查标志和我的while(1)循环形成数据包轮询UART?在这种情况下,UART中断将在整个数据包形成后的while(1)循环中重新启用。

1 个答案:

答案 0 :(得分:1)

这不是唯一的两个选择,第二个似乎不是最理想的。

我的第一种方法是一个简单的循环队列,并从ISR将字节推入其中,并从主循环中读取字节。这样你就可以获得一个小而简单的ISR,并且可以在主循环中进行处理而不会禁用中断。

假设您可以明智地编码ISR,那么第一种选择是可能的。您可能希望在处理构造数据包时有超时;您需要能够在ISR中正确处理。这取决于线路速度,MCU的速度以及您还需要做什么。

更新

在ISR中这样做当然是合理的。但是,使用循环队列非常简单,在您的技巧包中有标准实现。这是一个循环队列实现;读者和作家可以独立运作。

#ifndef ARRAY_ELEMENTS
#define ARRAY_ELEMENTS(x) (sizeof(x) / sizeof(x[0]))
#endif

#define QUEUE_DEFINE(name, queue_depth, type) \
        struct queue_type__##name { \
            volatile size_t m_in; \
            volatile size_t m_out; \
            type m_queue[queue_depth]; \
        }

#define QUEUE_DECLARE(name) struct queue_type__##name name

#define QUEUE_SIZE(name) ARRAY_ELEMENTS((name).m_queue)

#define QUEUE_CALC_NEXT(name, i) \
        (((name).i == (QUEUE_SIZE(name) - 1)) ? 0 : ((name).i + 1))

#define QUEUE_INIT(name) (name).m_in = (name).m_out = 0

#define QUEUE_EMPTY(name) ((name).m_in == (name).m_out)

#define QUEUE_FULL(name) (QUEUE_CALC_NEXT(name, m_in) == (name).m_out)

#define QUEUE_NEXT_OUT(name) ((name).m_queue + (name).m_out)
#define QUEUE_NEXT_IN(name) ((name).m_queue + (name).m_in)

#define QUEUE_PUSH(name) ((name).m_in = QUEUE_CALC_NEXT((name), m_in))
#define QUEUE_POP(name) ((name).m_out = QUEUE_CALC_NEXT((name), m_out))

像这样使用:

QUEUE_DEFINE(bytes_received, 64, unsigned char);
QUEUE_DECLARE(bytes_received);

void isr(void)
{
    /* Move the received byte into 'c' */
    /* This code enqueues the byte, or drops it if the queue is full */
    if (!QUEUE_FULL(bytes_received)) {
        *QUEUE_NEXT_IN(bytes_received) = c;
        QUEUE_PUSH(bytes_received);
    }
}

void main(void)
{
    QUEUE_INIT(bytes_received);

    for (;;) {
        other_processing();
        if (!QUEUE_EMPTY(bytes_received)) {
            unsigned char c = *QUEUE_NEXT_OUT(bytes_received);
            QUEUE_POP(bytes_received);
            /* Use c as you see fit ... */
        }
    }
 }