English 中文(简体)
Display previously received UART values
原标题:

This should be easy to answer to anyone familiar with C. I want to display the previous values of a variable (receive register of a UART (RS-232) on a microcontroller) on an LCD. This is my current implementation and it works ok. But I d like to know if there is a way to spend less time in my interrupt routine. Currently, the peripheral is configured to jump to the interrupt routine as soon as it receives one new character in the UART feed. Suggestions anyone?

//Initialization
char U1RX_data =   ;
char p0_U1RX_data =   ;
char p1_U1RX_data =   ;
char p2_U1RX_data =   ;
char p3_U1RX_data =   ;
char p4_U1RX_data =   ;
char p5_U1RX_data =   ;
char p6_U1RX_data =   ;
char p7_U1RX_data =   ;

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,   };
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;    
}
问题回答

You can use memmove in your interrupt, like this:

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

That replaces the assignments that you re currently performing manually and is a bit more idiomatic.

I m hoping I understood you properly; the main point is to use memmove to shift the buffer down by one byte. As an aside, it s important to use memmove instead of memcpy when the destination and source buffers overlap, as in this example.

You could always make a fixed length circular buffer

char buffer[8] = {   ,   ,   ,   ,   ,   ,   ,   ,);
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;
}

As Emerick noted, memmove() will work nicely if you have access to it. If not, just grab a simple implementation of it from Google, it shouldn t take up too much instruction memory.

What s your clock frequency on the microcontroller? Another thing to think about is that if your clock speed is significantly higher than your baud rate a lot of these things become non-issues. For example, if your clock speed is 16 MHz, you really don t have to worry about creating the world s shortest ISR as long as your not doing anything insanely computationally intensive inside it. Also, if you re clocking the system significantly faster than the baud rate, polling is also an option.

EDIT: Another option I just thought of, using interrupts.

You could store your buffer as an array of chars and then keep a global index to the next empty slot, like this:

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

Then in your ISR, all you need to do is dump the new byte into the bucket that index points at and increment the index. This will automatically overwrite the oldest character in the buffer as the start position rotates back around the buffer.

void my_isr()
{
    uart_buf[uart_buf_index] = get_uart_byte();
    uart_buf_index = (uart_buf_index + 1) % UART_BUF_SIZE;
}

Basically at this point you ve got a buffer with a rotating starting position, but it saves you from having to move 16 bytes of memory every ISR. The trick comes in reading it back out, because you have to account for the wrap-around.

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

I would create an array of previous values, and treat it as a circular buffer. The interrupt routine then simply records the new value in the next slot, overwriting the last value, and incrementing the index.

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

I m assuming synchronous, non-threaded operation; if you have to deal with multi-threaded, then there s work to protect the index variable from concurrent access. It also returns zeroes for the last but one reading before the buffer is full. Etc. If you are confident of your coding, you can remove the modulo operation, too.

Since it is a dspic, you might want to have a look at exaples ce214 and ce114 that handle uart DMA.

Go here and search for "uart":

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

The DMA approach is block oriented, and with one interrupt per block the lightest. However the block orientation can also be an disadvantage if your information stream is not continuous, since bytes might be held up in a buffer.

Maybe you can remedy this by setting a timer.





相关问题
Fastest method for running a binary search on a file in C?

For example, let s say I want to find a particular word or number in a file. The contents are in sorted order (obviously). Since I want to run a binary search on the file, it seems like a real waste ...

Print possible strings created from a Number

Given a 10 digit Telephone Number, we have to print all possible strings created from that. The mapping of the numbers is the one as exactly on a phone s keypad. i.e. for 1,0-> No Letter for 2->...

Tips for debugging a made-for-linux application on windows?

I m trying to find the source of a bug I have found in an open-source application. I have managed to get a build up and running on my Windows machine, but I m having trouble finding the spot in the ...

Trying to split by two delimiters and it doesn t work - C

I wrote below code to readin line by line from stdin ex. city=Boston;city=New York;city=Chicago and then split each line by ; delimiter and print each record. Then in yet another loop I try to ...

Good, free, easy-to-use C graphics libraries? [closed]

I was wondering if there were any good free graphics libraries for C that are easy to use? It s for plotting 2d and 3d graphs and then saving to a file. It s on a Linux system and there s no gnuplot ...

Encoding, decoding an integer to a char array

Please note that this is not homework and i did search before starting this new thread. I got Store an int in a char array? I was looking for an answer but didn t get any satisfactory answer in the ...

热门标签