We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
Hi. I'm working on a project involving a java program and the micro controller NXP P89v664.
The java program which utilises the RXTX package, outputs to the serial port a string of characters to be deciphered by the micro controller.
My java program is completed and outputs correctly as checked by virtual com ports and a c program.
I have written a keil c program to receive the input but I somehow receive random inputs instead.
I used the _getkey() fuction to receive the input and putchar() function to check the output.
I'm new to keil c can anyone shed some light on this pls?
Thanks.
Hi everyone. Sorry for the late reply. Have been testing with different crystals and baudrate.
Microcontroller: NXP P89v664 Flash Program: Flash Magic
Crystal: 14.74756 Baud: 38400 TH1: 0xFE
Results in hyper terminal:
Input: 0 1 2 3 4 5 6 7 8 9 Output: ðþ ñþ òþ óþ ôþ õþ öþ ÷þ øþ ùþ
Results in another program:
Input(HEX): 01 02 03 04 05 06 07 08 09 10 20 30 40 Output(HEX): E1 E2 E3 E4 E5 E6 E7 E8 E9 F0 E0FE(Sometimes FE) F0FE(Sometimes FE) E0FF(Sometimes FF)
Input(HEX): 81 82 83 84 85 86 87 88 89 90 A0 Output(HEX): E1 E2 E3 E4 E5 E6 E7 E8 E9 F0 E0FE(Sometimes FE)
Input(HEX): D1 D2 D3 E1 E2 E3 E4 E5 E6 E7 E8 E9 F0 F1 F2 F3 Output(HEX): F1 F2 F3 E1 E2 E3 E4 E5 E6 E7 E8 E9 F0 F1 F2 F3
Using this code:
/*------------------------------------------------------------------------------ MAIN.C: Interrupt Driver SIO Using printf. Copyright 1995-2002 KEIL Software, Inc. ------------------------------------------------------------------------------*/ #include <reg51.h> #include <stdio.h> #include "sio.h" /*------------------------------------------------------------------------------ _getkey waits until a character is received from the serial port. This may not be the exact desired operation (for example if the buffer is empty, this function hangs waiting for a character to be received). ------------------------------------------------------------------------------*/ char _getkey (void) { int k; do { k = com_getchar (); } while (k == -1); return ((unsigned char) k); } /*------------------------------------------------------------------------------ ------------------------------------------------------------------------------*/ char putchar (char c) { volatile unsigned int i; while (com_putchar (c) != 0) { for (i=0; i<1000; i++) { /*** DO NOTHING ***/ } } return (c); } /*------------------------------------------------------------------------------ Note that the two function above, _getkey and putchar, replace the library functions of the same name. These functions use the interrupt-driven serial I/O routines in SIO.C. ------------------------------------------------------------------------------*/ code char message [] = "This is a test to see if the interrupt driven serial I/O routines really work."; void main (void) { com_initialize (); /* initialize interrupt driven serial I/O */ com_baudrate (38400); /* setup for 1200 baud */ EA = 1; /* Enable Interrupts */ //printf ("Interrupt-driver Serial I/O Example\r\n\r\n"); while (1) { unsigned char c; //printf ("Press a key.\r\n"); c = _getkey(); putchar(c); // printf ("\r\n"); // printf ("You pressed '%c'.\r\n\r\n", c); } }
/*------------------------------------------------------------------------------ SIO.C: Serial Communication Routines. Copyright 1995-2002 KEIL Software, Inc. ------------------------------------------------------------------------------*/ #include <reg51.h> #include <string.h> #include "sio.h" /*------------------------------------------------------------------------------ Notes: The length of the receive and transmit buffers must be a power of 2. Each buffer has a next_in and a next_out index. If next_in = next_out, the buffer is empty. (next_in - next_out) % buffer_size = the number of characters in the buffer. ------------------------------------------------------------------------------*/ #define TBUF_SIZE 2 /*** Must be one of these powers of 2 (2,4,8,16,32,64,128) ***/ #define RBUF_SIZE 8 /*** Must be one of these powers of 2 (2,4,8,16,32,64,128) ***/ #define TBUF_SPACE idata /*** Memory space where the transmit buffer resides ***/ #define RBUF_SPACE idata /*** Memory space where the receive buffer resides ***/ #define CTRL_SPACE data /*** Memory space for the buffer indexes ***/ /*------------------------------------------------------------------------------ ------------------------------------------------------------------------------*/ #if TBUF_SIZE < 2 #error TBUF_SIZE is too small. It must be larger than 1. #elif TBUF_SIZE > 128 #error TBUF_SIZE is too large. It must be smaller than 129. #elif ((TBUF_SIZE & (TBUF_SIZE-1)) != 0) #error TBUF_SIZE must be a power of 2. #endif #if RBUF_SIZE < 2 #error RBUF_SIZE is too small. It must be larger than 1. #elif RBUF_SIZE > 128 #error RBUF_SIZE is too large. It must be smaller than 129. #elif ((RBUF_SIZE & (RBUF_SIZE-1)) != 0) #error RBUF_SIZE must be a power of 2. #endif /*------------------------------------------------------------------------------ ------------------------------------------------------------------------------*/ static TBUF_SPACE unsigned char tbuf [TBUF_SIZE]; static RBUF_SPACE unsigned char rbuf [RBUF_SIZE]; static CTRL_SPACE unsigned char t_in = 0; static CTRL_SPACE unsigned char t_out = 0; static CTRL_SPACE unsigned char r_in = 0; static CTRL_SPACE unsigned char r_out = 0; static bit ti_restart = 0; /* NZ if TI=1 is required */ /*------------------------------------------------------------------------------ ------------------------------------------------------------------------------*/ static void com_isr (void) interrupt 4 { /*------------------------------------------------ Received data interrupt. ------------------------------------------------*/ if (RI != 0) { RI = 0; if (((r_in - r_out) & ~(RBUF_SIZE-1)) == 0) { rbuf [r_in & (RBUF_SIZE-1)] = SBUF; r_in++; } } /*------------------------------------------------ Transmitted data interrupt. ------------------------------------------------*/ if (TI != 0) { TI = 0; if (t_in != t_out) { SBUF = tbuf [t_out & (TBUF_SIZE-1)]; t_out++; ti_restart = 0; } else { ti_restart = 1; } } } /*------------------------------------------------------------------------------ ------------------------------------------------------------------------------*/ #pragma disable void com_initialize (void) { /*------------------------------------------------ Setup TIMER1 to generate the proper baud rate. ------------------------------------------------*/ com_baudrate (1200); /*------------------------------------------------ Clear com buffer indexes. ------------------------------------------------*/ t_in = 0; t_out = 0; r_in = 0; r_out = 0; /*------------------------------------------------ Setup serial port registers. ------------------------------------------------*/ SM0 = 0; SM1 = 1; /* serial port MODE 1 */ SM2 = 0; REN = 1; /* enable serial receiver */ RI = 0; /* clear receiver interrupt */ TI = 0; /* clear transmit interrupt */ ti_restart = 1; ES = 1; /* enable serial interrupts */ PS = 0; /* set serial interrupts to low priority */ } /*------------------------------------------------------------------------------ ------------------------------------------------------------------------------*/ #pragma disable void com_baudrate ( unsigned baudrate) { /*------------------------------------------------ Clear transmit interrupt and buffer. ------------------------------------------------*/ TI = 0; /* clear transmit interrupt */ t_in = 0; /* empty transmit buffer */ t_out = 0; /*------------------------------------------------ Set timer 1 up as a baud rate generator. ------------------------------------------------*/ TR1 = 0; /* stop timer 1 */ ET1 = 0; /* disable timer 1 interrupt */ PCON |= 0x80; /* 0x80=SMOD: set serial baudrate doubler */ TMOD &= ~0xF0; /* clear timer 1 mode bits */ TMOD |= 0x20; /* put timer 1 into MODE 2 */ TH1 = 0xFE;//(unsigned char) (256 - (XTAL / (16L * 12L * baudrate))); TR1 = 1; /* start timer 1 */ } /*------------------------------------------------------------------------------ ------------------------------------------------------------------------------*/ #pragma disable char com_putchar ( unsigned char c) { /*------------------------------------------------ If the buffer is full, return an error value. ------------------------------------------------*/ if (com_tbuflen () >= TBUF_SIZE) return (-1); /*------------------------------------------------ Add the data to the transmit buffer. If the transmit interrupt is disabled, then enable it. ------------------------------------------------*/ tbuf [t_in & (TBUF_SIZE - 1)] = c; t_in++; if (ti_restart) { ti_restart = 0; TI = 1; /* generate transmit interrupt */ } return (0); } /*------------------------------------------------------------------------------ ------------------------------------------------------------------------------*/ #pragma disable int com_getchar (void) { if (com_rbuflen () == 0) return (-1); return (rbuf [(r_out++) & (RBUF_SIZE - 1)]); } /*------------------------------------------------------------------------------ ------------------------------------------------------------------------------*/ #pragma disable unsigned char com_rbuflen (void) { return (r_in - r_out); } /*------------------------------------------------------------------------------ ------------------------------------------------------------------------------*/ #pragma disable unsigned char com_tbuflen (void) { return (t_in - t_out); } /*------------------------------------------------------------------------------ ------------------------------------------------------------------------------*/
but what do you see with an oscilloscope?
Hi. I dont think i have done the oscilloscope test correctly. I only get 50Hz at the output.
Anyway I am able to get the correct echo of the characters after i transferred the hex file without pressing the reset button on the microcontroller.
After pressing the reset button, I get the funny characters as shown above.
i have need of this software, so please procede me.
What software?
/*------------------------------------------------ Received data interrupt. ------------------------------------------------*/ if (RI != 0) { RI = 0; if (((r_in - r_out) & ~(RBUF_SIZE-1)) == 0) { rbuf [r_in & (RBUF_SIZE-1)] = SBUF; r_in++; } }
the above looks 'strange' and i think wrong, the 'normal' way is
/*------------------------------------------------ Received data interrupt. ------------------------------------------------*/ if (RI) { RI = 0; rbuf [r_in] = SBUF; r_in++; r_in &= (RBUF_SIZE-1); // works only if RBUF_SIZE is a power of 2 }
Erik
As long as r_in and r_out are integers of size 2^n, and the buffer is of size 2^m where m is smaller than n, you don't need to clip them everytime r_in or r_out are incremented.
That means that (r_in - r_out) == 0 represents an empty buffer. And that (r_in - r_out) != 0 represents a non-empty buffer. And that ((r_in - r_out) & ~(RBUF_SIZE-1)) == 0 represents a non-full buffer. And that ((r_in - r_out) & ~(RBUF_SIZE-1)) != 0 represents a full buffer.
So the code checks if there is room to insert (at least) one more character. Since r_in are incremented without a clip, the insert code must do the clipping instead: rbuf[r_in & (RBUF_SIZE-1)]
The advantage with clipping/masking when indexing the array, instead of when incrementing the indices, is that you can fill the array completely full.
If you do clip r_in and r_out directly when they are incremented, you must never fill the ring buffer 100% full or you get into a situation where r_in == r_out means either full or empty.
And that ((r_in - r_out) & ~(RBUF_SIZE-1)) == 0 represents a non-full buffer. And that ((r_in - r_out) & ~(RBUF_SIZE-1)) != 0 represents a full buffer. the above is, of course, true for a linear buffer. However for I/O a circular buffer makes much more sense. the disadvantage of a linear buffer is that you NEED to occasionally 'catch' it when empty (in_offset = out_offset) and reset the pointers.
It just so happens that last week I had the opportunity (in an ARM project) to find out that, under certain circumstances the Input was faster than the "catch-check" and the buffer ran over. After changing to a circular buffer it ran smoothly.
No, I'm talking about a circular buffer. Just that besides your optimization when buffer size is 2^n, you can perform yet another optimization when integer overflow happens at 2^m and m > n.
Buffer sizes of 2^n means we can avoid modulo and just to & to clip overflows.
But with integers that overflows at 2^m we don't need to worry about the overflow either, and can let the read and write indices walk the full numeric range as long as we perform the & clip when addressing the buf[] array for inserting or removing an entry.
No need to catch anything and reset the pointers if the buffer size is 2^m and the integer turns around at 2^m and m > n. It doesn't matter if one index is 5 and one index is 4 billion. Why? 5 - 4 billion gives the number of items in the buffer.
Let's say we have 16-bit unsigned integers (m = 16, and numeric range 2^16, i.e. 0..0xffff. Let's say buffer size is 16 byte. n = 4, spanning 0..15. m > n so we can perform optimization and let the indices continue walking the full integer numeric range without caring - and getting the advantage of being able to use the full size of the ring buffer instead of reserving one position to separate full from empty.
So you can have an empty buffer where both index are at 0xffff. Then insert one byte and write pointer is now 0 and read pointer still 0xffff. That first byte is inserted at buf[0xffff & 0x000f] or buf[15]. The write index are then incremented fron 0xffff to 0 (because of integer overflow).
Amount of data is 0-0xffff which equals 1.
So you can start from same state (0xffff for both read and write indices) and insert 16 elements.
Then you have write position 15 and read position 0xffff. And 15-0xffff is 16 - i.e. number of elements in the array.
And (15-0xffff) & ~(16-1) <=> 0x0010 & 0xfff0 which is 0x0010 which is != 0, i.e. buffer is full.
Consume one element from the array and the read index gets incremented from 0xffff to 0.
(15-0) & ~(16-1) <= 0x000f & 0xfff0 which is zero, i.e. buffer is not full.
And next insert is at buf[15 & (16-1)] or buf[0x000f & 0xf] which is buf[15]. Next insert will be at buf[16 & (16-1)] or buf[0x0010 & 0xf] which is buf[0]. So ring buffer behaviour for insert.
If i read you right, you are saying:
or
/*------------------------------------------------ Received data interrupt. ------------------------------------------------*/ if (RI) { RI = 0; rbuf [r_in & (RBUF_SIZE-1)] = SBUF; r_in++; }
does the same, which, of course they do.
however, the line (//XXXXXXX) may keep a store from happening and give no error indication.
if (((r_in - r_out) & ~(RBUF_SIZE-1)) == 0) { rbuf [r_in & (RBUF_SIZE-1)] = SBUF; //XXXXXXX r_in++; }
// Check if we have room for more. // We have room if r_in and r_out are less than one buffer size from each other. // So we have room if r_in - r_out doesn't overflow the masking operation performed. if (((r_in - r_out) & ~(RBUF_SIZE-1)) == 0) { // This assign will never overwrite an already used position, // because of the previous test. // And it will never overflow the buffer, because of the bit-and // giving an index modulo the buffer size. rbuf [r_in & (RBUF_SIZE-1)] = SBUF; //XXXXXXX r_in++; } else { // Our input buffer was full. Might be nice to keep track of the // number of receive buffer overflows. rbuf_overflows++; }
Your code always inserts a character, possibly killing the oldest unread character in the ring-buffer. Not only that. Since you do clipping directly after increment of r_in, you get the potential problem that r_in gets set to zero while r_out might also have been zero. So suddenly you got from a totally full ring-buffer to a ring-buffer where r_in equals r_out, signalling an empty ring-buffer. When clipping is done whenever r_in or r_out are incremented, then you must always stop inserting data exactly one step before r_in reaches r_out, to avoid making empty and full look identical.
The other code instead skips the insert if there are exactly RBUF_SIZE entries already stored in the ring buffer. And since there is no clipping of r_in or r_out when they are incremented (with the exception of the overflow from the integer storing these indices), it's ok to fill every single position in the ring buffer. You would then have exactly RBUF_SIZE entries in the buffer which can be tested by either subtracting and check if answer is >= RBUF_SIZE. Or you can perform the bit-and and check if any of the the high bits in the result got set.
So there is a difference in behaviour on overflow. One code drops the most recent characters, potentially having an else clause to warn about the overflow. The other code gets into a singularity, dropping a full buffers worth of content and starting from zero.
Most circular buffer software is written incorrectly. There are several things you need to know with circ buffers.
To accomplish all of these, you obviously need a buffer, a next-in pointer (or index), and a next-out pointer (or index). Generally, you can figure out the amount of data in the buffer by subtracting the next-out from next-in (with a modulo). However, if the result is 0, does that mean that the buffer is full or empty? This is where most circ buffer implementations fall apart.
Choosing a buffer size that is a power of 2, and using indexes for next-in and next-out, and NOT mod'ing the indexes after an increment, it is easy to answer the questions of the list above.
This is how the serial I/O routines work that I wrote when I was at Keil. This is found at http://www.keil.com/download/docs/200.asp.
The examples listed in this thread look mysteriously like the code I wrote way back when.
Jon