2009-11-26 20 views
1

Это должно быть легко ответить любому, кто знаком с C. Я хочу отобразить предыдущие значения переменной (регистр приема UART (RS-232) на микроконтроллере) на ЖК-дисплее. Это моя текущая реализация, и она работает нормально. Но я хотел бы знать, есть ли способ провести меньше времени в моей процедуре прерывания. В настоящее время периферийное устройство настроено на переход к процедуре прерывания, как только он получает один новый символ в канале UART. Предложения кто-нибудь?Отображение ранее полученных значений 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;  
} 
+0

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

ответ

3

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

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

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

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

3

Как отметил Эмерик, 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]); 
} 
+0

Лучше даже, используйте DMA. Прерывание по-прежнему имеет прерывания для каждого символа. –

3

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

#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]); 
} 

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

+0

ack, вы попали туда первым. – Pod

+2

В подпрограмме прерывания он может * быстрее использовать modulo вместо ветки (например, 'idx = (idx + 1)% DIM (uart);') - особенно если размер вашего буфера равен 2 (потому что то компилятор должен оптимизировать его до поразрядного И - это поможет, если 'idx' без знака). Обычно я не упоминал такую ​​микрооптимизацию, но минимизация времени, выполняемого в обработчике прерываний во встроенной архитектуре, похоже на случай, когда это может иметь значение. – caf

+0

(Также вы использовали 'idx' и' index' взаимозаменяемые). – caf

3

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

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; 
} 
0

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

Зайдите сюда и искать «УАППЫ»:

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

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

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