Отображение ранее полученных значений UART

Это должно быть легко ответить любому, кто знаком с C. Я хочу отобразить предыдущие значения переменной (регистр приема UART (RS-232) на микроконтроллере) на ЖК-дисплее. Это моя текущая реализация, и она работает нормально. Но я хотел бы знать, есть ли способ тратить меньше времени на мою процедуру прерывания. В настоящее время периферийное устройство настроено на переход к процедуре прерывания, как только оно получает один новый символ в канале UART. Предложения кто-нибудь?

//Initialization
char U1RX_data = '\0';
char p0_U1RX_data = '\0';
char p1_U1RX_data = '\0';
char p2_U1RX_data = '\0';
char p3_U1RX_data = '\0';
char p4_U1RX_data = '\0';
char p5_U1RX_data = '\0';
char p6_U1RX_data = '\0';
char p7_U1RX_data = '\0';

char U1buf[] = {p7_U1RX_data, p6_U1RX_data, p5_U1RX_data,
                p4_U1RX_data, p3_U1RX_data, p2_U1RX_data,
                p1_U1RX_data, p0_U1RX_data, U1RX_data, '\0'};
disp_string(-61, 17, 1, U1buf); //X, Y, mode, string

void _U1RXInterrupt(void){
    p7_U1RX_data = p6_U1RX_data;
    p6_U1RX_data = p5_U1RX_data;
    p5_U1RX_data = p4_U1RX_data;
    p4_U1RX_data = p3_U1RX_data;
    p3_U1RX_data = p2_U1RX_data;
    p2_U1RX_data = p1_U1RX_data;
    p1_U1RX_data = p0_U1RX_data;
    p0_U1RX_data = U1RX_data;

    U1RX_data = U1RXREG;
    IFS0bits.U1RXIF = 0;    
}

person JcMaco    schedule 26.11.2009    source источник
comment
Спасибо за ответы на все вопросы. Я посмотрю на круговой буфер. Для любопытных, это на dsPIC30F4013 с тактовой частотой 7,3 МГц. В этом случае оптимизация может и не понадобиться, но я буду знать, как это сделать, если у меня возникнут проблемы.   -  person JcMaco    schedule 27.11.2009


Ответы (5)


Вы можете использовать memmove в своем прерывании, например:

void _U1RXInterrupt(void)
{
    memmove(&U1Buf[0], &U1Buf[1], 7);
    U1Buf[7] = U1RX_data;
    ...
}

Это заменяет задания, которые вы в настоящее время выполняете вручную, и немного более идиоматично.

Я надеюсь, что понял вас правильно; главное использовать memmove для сдвига буфера вниз на один байт. Кроме того, важно использовать memmove вместо memcpy, когда буферы назначения и источника перекрываются, как в этом примере.

person Emerick Rogul    schedule 26.11.2009

Вы всегда можете сделать круговой буфер фиксированной длины

char buffer[8] = { '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',);
char position = 0;


/* useable if you have a version of disp_string that takes a "number of chars"*/
char buffer_nprint()
{
    /* print from position to the end of the buffer*/
    disp_string(-61, 17, 1, &buffer[position], 8 - position);
    if (position > 0)
    {
        /* now print from start of buffer to position */
        disp_string(-61, 17, 1, buffer, position);
    }
}

/* if you _don't_ have a version of disp_string that takes a "number of chars"
   and are able to do stack allocations*/
char buffer_print()
{
    char temp[9];
    temp[8] = '/0';
    memcpy(temp, &buffer[position], 8 - position);
    memcpy(temp, buffer, position);
    temp[8] = '/0';
    disp_string(-61, 17, 1, temp);
}

char buffer_add(char new_data)
{
    char old_data = buffer[position];
    buffer[position] = new_data;
    position = ((position + 1) & 8);
}

void _U1RXInterrupt(void)
{
    buffer_add(U1RXREG);
    IFS0bits.U1RXIF = 0;
}
person Pod    schedule 26.11.2009

Как заметил Эмерик, memmove() будет хорошо работать, если у вас есть к нему доступ. Если нет, просто возьмите его простую реализацию из Google, она не должна занимать слишком много памяти инструкций.

Какая у вас тактовая частота на микроконтроллере? Еще одна вещь, о которой следует подумать, это то, что если ваша тактовая скорость значительно выше, чем скорость передачи данных, многие из этих вещей перестают быть проблемой. Например, если ваша тактовая частота составляет 16 МГц, вам действительно не нужно беспокоиться о создании самого короткого в мире ISR, если вы не выполняете внутри него ничего, требующего безумных вычислительных ресурсов. Кроме того, если вы синхронизируете систему значительно быстрее, чем скорость передачи данных, опрос также является опцией.

EDIT: еще один вариант, о котором я только что подумал, используя прерывания.

Вы можете сохранить свой буфер в виде массива символов, а затем сохранить глобальный индекс для следующего пустого слота, например:

#define UART_BUF_SIZE 16
char uart_buf[UART_BUF_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0
                                0, 0, 0, 0, 0, 0, 0, 0};
char uart_buf_index = 0;

Затем в вашем ISR все, что вам нужно сделать, это сбросить новый байт в корзину, на которую указывает индекс, и увеличить индекс. Это автоматически перезапишет самый старый символ в буфере, поскольку начальная позиция будет вращаться вокруг буфера.

void my_isr()
{
    uart_buf[uart_buf_index] = get_uart_byte();
    uart_buf_index = (uart_buf_index + 1) % UART_BUF_SIZE;
}

По сути, на этом этапе у вас есть буфер с вращающейся начальной позицией, но это избавляет вас от необходимости перемещать 16 байтов памяти при каждом ISR. Хитрость заключается в том, чтобы прочитать его обратно, потому что вы должны учитывать цикличность.

char i;
for (i = uart_buf_index; i < UART_BUF_SIZE; i++)
{
    lcd_write_byte(uart_buf[i]);
}
for (i = 0; i < uart_buf_index; i++)
{
    lcd_write_byte(uart_buf[i]);
}
person Bob Somers    schedule 26.11.2009
comment
А еще лучше использовать DMA. Прерывание по-прежнему имеет прерывания на символ. - person Marco van de Voort; 05.08.2011

Я бы создал массив предыдущих значений и рассматривал его как циклический буфер. Затем процедура прерывания просто записывает новое значение в следующий слот, перезаписывая последнее значение и увеличивая индекс.

#define DIM(x)  (sizeof(x)/sizeof(*(x)))
static int  index = 0;
static char uart[8];

void _U1RXInterrupt(void){
    if (++index >= DIM(uart))
        index = 0;
    uart[index] = U1RXREG;
    IFS0bits.U1RXIF = 0;        
}

int uart_value(unsigned n)
{
    int i = index + DIM(uart) - (n % DIM(uart));
    if (i > DIM(uart))
        i -= DIM(uart);
    return(uart[i]);
}

Я предполагаю синхронную работу без потоков; если вам приходится иметь дело с многопоточностью, то есть работа по защите индексной переменной от одновременного доступа. Он также возвращает нули для предпоследнего чтения перед заполнением буфера. И т. д. Если вы уверены в своем коде, вы также можете удалить операцию по модулю.

person Jonathan Leffler    schedule 26.11.2009
comment
В подпрограмме прерывания может быстрее использовать модуль по модулю вместо перехода (например, idx = (idx + 1) % DIM(uart);) - особенно если размер вашего буфера является степенью двойки (потому что тогда компилятор должен оптимизировать его до побитового И - поможет, если idx без знака). Обычно я бы не упоминал о такой микрооптимизации, но минимизация времени выполнения в обработчике прерываний во встроенной архитектуре кажется тем случаем, когда это действительно может иметь значение. - person caf; 27.11.2009
comment
(Также вы использовали idx и index взаимозаменяемо). - person caf; 27.11.2009

Поскольку это dspic, вы можете взглянуть на примеры ce214 и ce114, которые обрабатывают DMA uart.

Перейдите сюда и найдите «uart»:

http://www.microchip.com/TechDoc.aspx?type=CodeExamples&ctl00_MainContent_DocListGridChangePage=6

Подход DMA ориентирован на блоки, и с одним прерыванием на блок является самым легким. Однако блочная ориентация также может быть недостатком, если ваш информационный поток не является непрерывным, поскольку байты могут задерживаться в буфере.

Возможно, вы можете исправить это, установив таймер.

person Marco van de Voort    schedule 05.08.2011