1

これは、C に詳しい人なら誰でも簡単に答えられるはずです。変数 (マイクロコントローラーの UART (RS-232) の受信レジスター) の以前の値を LCD に表示したいと考えています。これは私の現在の実装であり、正常に動作します。しかし、割り込みルーチンに費やす時間を減らす方法があるかどうか知りたいです。現在、ペリフェラルは、UART フィードで新しい文字を 1 つ受信するとすぐに割り込みルーチンにジャンプするように構成されています。提案は誰ですか?

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

5 に答える 5

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

同期の非スレッド操作を想定しています。マルチスレッドに対処する必要がある場合は、インデックス変数を同時アクセスから保護する作業があります。また、バッファがいっぱいになる前の最後の 2 つ前の読み取りに対してもゼロを返します。コーディングに自信がある場合は、モジュロ演算も削除できます。

于 2009-11-26T19:23:51.790 に答える
3

エメリックが指摘したように、memmove()アクセスできる場合はうまく機能します。そうでない場合は、Google から簡単な実装を入手してください。命令メモリをあまり消費しないはずです。

マイクロコントローラのクロック周波数は? 考慮すべきもう 1 つのことは、クロック速度がボーレートよりも大幅に高い場合、これらの多くは問題にならないということです。たとえば、クロック速度が 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]);
}
于 2009-11-26T19:19:33.510 に答える
3

memmove次のように、割り込みで使用できます。

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

これは、現在手動​​で実行している割り当てを置き換えるもので、もう少し慣用的です。

あなたのことを正しく理解できたと思います。要点はmemmove、バッファを 1 バイト下にシフトするために使用することです。余談ですが、この例のように、宛先バッファーとソース バッファーがオーバーラップする場合のmemmove代わりに使用することが重要です。memcpy

于 2009-11-26T19:12:35.770 に答える
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;
}
于 2009-11-26T19:46:44.440 に答える
0

これは dspic であるため、uart DMA を処理する例の ce214 および ce114 を参照することをお勧めします。

ここに行き、「uart」を検索してください:

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

DMA アプローチはブロック指向であり、ブロックごとに 1 つの割り込みが最も軽いです。ただし、情報ストリームが連続していない場合、バイトがバッファ内に保持される可能性があるため、ブロックの向きも不利になる可能性があります。

タイマーを設定することで解決できるかもしれません。

于 2011-08-05T08:09:51.677 に答える