2009-11-26 41 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; 
}