Показать ранее полученные значения UART - PullRequest
1 голос
/ 26 ноября 2009

На этот вопрос должно быть легко ответить любому, кто знаком с 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;    
}

Ответы [ 5 ]

3 голосов
/ 26 ноября 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;
}
3 голосов
/ 26 ноября 2009

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

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

РЕДАКТИРОВАТЬ: еще один вариант, о котором я только что подумал, используя прерывания.

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

#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]);
}
3 голосов
/ 26 ноября 2009

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

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

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

3 голосов
/ 26 ноября 2009

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

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

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

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

0 голосов
/ 05 августа 2011

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

Идите сюда и ищите "uart":

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

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

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

...