显示先前收到的UART值

时间:2009-11-26 18:55:07

标签: c microcontroller microchip uart

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

答案 0 :(得分:3)

您可以在中断中使用memmove,如下所示:

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

它取代了您当前正在手动执行的分配,而且更加惯用。

我希望我能理解你;重点是使用memmove将缓冲区向下移动一个字节。另外,当目标和源缓冲区重叠时,使用memmove而不是memcpy非常重要,如本例所示。

答案 1 :(得分:3)

正如Emerick所说,memmove()如果你有权访问它将会很好地工作。如果没有,只需从谷歌获取一个简单的实现,它不应占用太多的指令内存。

微控制器的时钟频率是多少?另一件需要考虑的事情是,如果你的时钟速度明显高于你的波特率,那么很多东西都会变成非问题。例如,如果你的时钟速度是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]);
}

答案 2 :(得分: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]);
}

我假设是同步的,非线程的操作;如果你必须处理多线程,那么就可以保护索引变量不受并发访问的影响。它还会在缓冲区已满之前为最后一个读数返回零。等等。如果您对编码有信心,也可以删除模运算。

答案 3 :(得分: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;
}

答案 4 :(得分:0)

因为它是一个dspic,你可能想看一下处理uart DMA的exaples ce214和ce114。

到这里搜索“uart”:

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

DMA方法是面向块的,每个块一个中断最轻。但是,如果您的信息流不连续,则块方向也可能是不利的,因为字节可能会被保留在缓冲区中。

也许您可以通过设置计时器来解决这个问题。