Как получать пакеты по последовательному порту MCU? - PullRequest
2 голосов
/ 11 июня 2010

Рассмотрим код, выполняемый на моем микроконтроллерном блоке (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. Когда ПК отправляет данные в MCU, new_packet отправляется, в противном случае default_packet отправляется. Каждый пакет может иметь 5 или более байтов с предварительно определенной структурой пакета.

Мой вопрос:

1Должен ли я получить весь пакет с ПК, используя подпрограмму обслуживания прерывания UART (ISR)?В этом случае мне нужно реализовать конечный автомат внутри ISR для сборки пакета (который может быть длинным с блоками if-else или switch-case).

                  OR

2.После отправки ПКсортировать команду REQUEST (один байт), обнаружить ее в моем ISR, установить флаг, отключить только прерывание UART и сформировать пакет в моем цикле while (1), проверив наличие флага и опросив UART? В этом случае прерывание UARTбудет повторно включен в цикле while (1) после формирования всего пакета.

1 Ответ

1 голос
/ 11 июня 2010

Это не единственные два варианта, и второй вариант кажется неоптимальным.

Мой первый подход - создать простую циклическую очередь, вставлять байты в нее из 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 ... */
        }
    }
 }
...