2009-11-26 43 views
1

这对于熟悉C的人应该很容易回答。我想在LCD上显示一个变量的前一个值(在微控制器上接收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

感谢您的所有答案。我会看看循环缓冲区。奇怪的是,这是在一个频率为7.3 MHz的dsPIC30F4013上进行的。在这种情况下,优化可能不是必需的,但如果遇到问题,我会知道如何去做。 – JcMaco 2009-11-26 21:59:52

回答

3

您可以在中断使用memmove,像这样:

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

,取代了目前正在手动执行的任务,是一个有点更地道。

我希望我能正确理解你;要点是使用memmove将缓冲区向下移动一个字节。顺便说一句,当目标缓冲区和源缓冲区重叠时,使用memmove而不是memcpy很重要,如本例中所示。

3

正如Emerick指出的那样,memmove()将很好地工作,如果您有权访问它。如果不是,只需从Google获得一个简单的实现,它不应占用太多的指令内存。

什么是你的微控制器的时钟频率?另一件需要考虑的事情是,如果你的时钟速度远远高于你的波特率,那么很多这些东西就成了问题。例如,如果您的时钟速度为16 MHz,只要您没有在其中执行任何疯狂的计算密集型任务,您就不必担心创建世界上最短的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; 
} 

基本上在这一点上,你已经有了一个旋转起始位置获得缓冲,但不必每移动ISR 16个字节的内存为您节省。诀窍在于将它读出来,因为你必须考虑环绕。

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。每个字符中断仍然有中断。 – 2011-08-04 22:43:28

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

确定你先到那里。 – Pod 2009-11-26 19:47:13

+2

在中断例程中,它可能会更快地使用模而不是分支(例如'idx =(idx + 1)%DIM(uart);') - 特别是如果缓冲区大小是2的幂(因为那么编译器应该将其优化为按位AND - 如果idx是无符号的,这将有所帮助。我通常不会提到这样的微观优化,但是尽量减少在嵌入式架构中的中断处理程序中执行的时间,这似乎是一个实际问题。 – caf 2009-11-26 21:19:06

+0

(你也可以互换使用'idx'和'index')。 – caf 2009-11-26 21:21:01

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