EmbeddedRelated.com
Forums
Memfault Beyond the Launch

writing ISR for UART

Started by sohagiut February 7, 2008
In article <K-KdneBYMKLUgjHanZ2dnUVZ_rKtnZ2d@giganews.com>, 
sohagiut@yahoo.com says...
> > Dear Mark Borgerson, > can i have a look your program to test. it might give me some idea for my > code. > > thanks > > Reza > >
You can find the source code at www.oes.to. Click on the button for the U4S and you'll see a link to the source code. Mark Borgerson
Mark Borgerson wrote:
> In article <47ac2c51$0$14988$8404b019@news.wineasy.se>, > david@westcontrol.removethisbit.com says... >> Mark Borgerson wrote: >>> In article <MN2dnVFbIrGdszbanZ2dnUVZ_uidnZ2d@giganews.com>, dta@e3ft.com >>> says... >>>> "sohagiut" <sohagiut@yahoo.com> wrote in message >> <snip> >>>> For example, in the code below, if the interrupt occurs between >>>> approximately points /* 1 */ and /* 2 */ there may be serious logical >>>> trouble. I'll leave it to you to figure out why. >>>> >>>> struct >>>> { >>>> int putpoint; >>>> int getpoint; >>>> int nchars; >>>> unsigned char body[QSIZE]; >>>> } queue; >>>> >>>> _interrupt_ void rx_isr(void) >>>> { >>>> unsigned char c; >>>> >>>> /* Get c from hardware */ >>>> >>>> if (queue.nchars < QSIZE) >>>> { >>>> queue.body[queue.putpoint] = c; >>>> queue.nchars++; >>>> queue.putpoint++; >>>> if (queue.putpoint >= QSIZE) >>>> queue.putpoint = 0; >>>> } >>>> } >>>> >>>> >>>> unsigned char get_a_char_from_queue(void) >>>> { >>>> unsigned char return_value = 0; >>>> >>>> /* 1 */ >>>> if (queue.nchars) >>>> { >>>> queue.nchars--; >>>> return_value = queue.body[queue.getpoint]; >>>> queue.getpoint++; >>>> if (queue.getpoint >= QSIZE) >>>> queue.getpoint = 0; >>>> } >>>> /* 2 */ >>>> >>>> return(return_value); >>>> } >>> This looks a lot like code I've used successfully for more than >>> a decade. Is there really a problem if q.nchars is accessed >>> atomically and the q.nchars-- operation occurs in a single >>> uninterruptible instruction? On the 68K machine where i >>> run the code, >>> q.nchars--; becomes subq.w #1, 4(A1) >>> >>> >>> so if the interrupt handler adds a character to the queue while >>> the get_a_char_from_queue() function is executing there >>> should be no problems. q.nchars is the only variable that >>> is modified by both the interrupt handler and by the >>> getchar routine. >>> >>> I forsee a problem if queue.nchars was a 16-bit or >>> 32-bit integer and the code was running on an 8-bit >>> processor. nchars could get messed up if an interrupt >>> occured between the mulitple instructions needed to >>> decrement the value. >>> >> The reason that your code (with the modifications you mentioned in >> another post, and a little care to avoid buffer overflows) is safe on >> the m68k is that the m68k has an atomic decrement instruction. For many >> embedded processors, that is not the case. First, it could be that the >> nchars variable is wider than the processor (16-bit int on an 8-bit >> processor, for example). This is generally due to the programmer not >> understanding their target - it's rare that you would need more than 256 >> bytes of buffer on an 8-bit micro, so the programmer has picked the >> wrong types when they used "int". Secondly, many modern processors are >> RISC load-store architectures, so that the decrement is done in three >> operations (load the old value, decrement it, store it again) and the >> interrupt can break into any of these. >> >> If you *don't* have access to such an atomic decrement, you have two >> options - disable interrupts around the critical code to make the >> operation atomic, or use a better buffer structure! (Hint - do you >> really need nchars?) >> >> > I've seen and used queue code that compares the get and put pointers to > detect whether a character is available. I avoided it for a couple of > reasons: > > * the pointer comparisons are not atomic on the 68K although that may > not matter when checking for getptr != putptr
You simply calculate "nchars = putptr - getptr", then adjust if there has been a wrap around the end of the buffer. It does not matter that this is not atomic - if a new character has come in during this operation, then the new putptr will be greater than the one used, and your nchars will be one character too small. But that's just the same as if the new character came in after your code had finished.
> * there are times when it is handy to have a count of the characters > in the buffer. With that count you can implement a faster loop to > pull characters from the queue. It's easier to increment and > decrement a counter at one instruction each, than it is to get > the queue length by pointer arithmetic that accounts for queue > wraparound. >
Thus you calculate "nchars = putptr - getptr" at the start of your code, and any new incoming characters are ignored until you next run the code - just as if they arrived after the code was finished.
> I do use that technique in my serial handler for the AT91SAM7 > processor. There, I use the DMA support in the Atmel chips, > so there is not an interrupt for each character or FIFO. > That leaves me with comparing the getptr with the DMA > receive pointer to detect incoming characters. > > One of the first uses for my serial I/O code on a new system > is always as part of the monitor used to upload new code. If > you start getting a lot of checksum errors when uploading > new code, it's a good indication that you've missed something > in the serial I/O driver. > > > The simple example code here also lacks a few elements: > 1: there is no error reporting for queue full errors > or queue empty errors. Without those error reports, > the program can't distinguish between a valid 0x00 > character and the result of a get operation on an > empty queue. > 2. There should be a ChAvailable() function that returns > the number of characters available. This prevents either > blocking on an empty queue or the queue empty 0x00 > character problem. > 3. Many UARTS have just one interrupt, which has to handle > receive, transmit and error interrupts, so the > ISR will be more complex than that shown. If the UART > has a FIFO, it gets to be even more fun! >
All this is correct - the code above is (hopefully!) just an example.
In article <47ac830d$0$15002$8404b019@news.wineasy.se>, 
david@westcontrol.removethisbit.com says...
> Mark Borgerson wrote: > > In article <47ac2c51$0$14988$8404b019@news.wineasy.se>, > > david@westcontrol.removethisbit.com says... > >> Mark Borgerson wrote: > >>> In article <MN2dnVFbIrGdszbanZ2dnUVZ_uidnZ2d@giganews.com>, dta@e3ft.com > >>> says... > >>>> "sohagiut" <sohagiut@yahoo.com> wrote in message > >> <snip> > >>>> For example, in the code below, if the interrupt occurs between > >>>> approximately points /* 1 */ and /* 2 */ there may be serious logical > >>>> trouble. I'll leave it to you to figure out why. > >>>> > >>>> struct > >>>> { > >>>> int putpoint; > >>>> int getpoint; > >>>> int nchars; > >>>> unsigned char body[QSIZE]; > >>>> } queue; > >>>> > >>>> _interrupt_ void rx_isr(void) > >>>> { > >>>> unsigned char c; > >>>> > >>>> /* Get c from hardware */ > >>>> > >>>> if (queue.nchars < QSIZE) > >>>> { > >>>> queue.body[queue.putpoint] = c; > >>>> queue.nchars++; > >>>> queue.putpoint++; > >>>> if (queue.putpoint >= QSIZE) > >>>> queue.putpoint = 0; > >>>> } > >>>> } > >>>> > >>>> > >>>> unsigned char get_a_char_from_queue(void) > >>>> { > >>>> unsigned char return_value = 0; > >>>> > >>>> /* 1 */ > >>>> if (queue.nchars) > >>>> { > >>>> queue.nchars--; > >>>> return_value = queue.body[queue.getpoint]; > >>>> queue.getpoint++; > >>>> if (queue.getpoint >= QSIZE) > >>>> queue.getpoint = 0; > >>>> } > >>>> /* 2 */ > >>>> > >>>> return(return_value); > >>>> } > >>> This looks a lot like code I've used successfully for more than > >>> a decade. Is there really a problem if q.nchars is accessed > >>> atomically and the q.nchars-- operation occurs in a single > >>> uninterruptible instruction? On the 68K machine where i > >>> run the code, > >>> q.nchars--; becomes subq.w #1, 4(A1) > >>> > >>> > >>> so if the interrupt handler adds a character to the queue while > >>> the get_a_char_from_queue() function is executing there > >>> should be no problems. q.nchars is the only variable that > >>> is modified by both the interrupt handler and by the > >>> getchar routine. > >>> > >>> I forsee a problem if queue.nchars was a 16-bit or > >>> 32-bit integer and the code was running on an 8-bit > >>> processor. nchars could get messed up if an interrupt > >>> occured between the mulitple instructions needed to > >>> decrement the value. > >>> > >> The reason that your code (with the modifications you mentioned in > >> another post, and a little care to avoid buffer overflows) is safe on > >> the m68k is that the m68k has an atomic decrement instruction. For many > >> embedded processors, that is not the case. First, it could be that the > >> nchars variable is wider than the processor (16-bit int on an 8-bit > >> processor, for example). This is generally due to the programmer not > >> understanding their target - it's rare that you would need more than 256 > >> bytes of buffer on an 8-bit micro, so the programmer has picked the > >> wrong types when they used "int". Secondly, many modern processors are > >> RISC load-store architectures, so that the decrement is done in three > >> operations (load the old value, decrement it, store it again) and the > >> interrupt can break into any of these. > >> > >> If you *don't* have access to such an atomic decrement, you have two > >> options - disable interrupts around the critical code to make the > >> operation atomic, or use a better buffer structure! (Hint - do you > >> really need nchars?) > >> > >> > > I've seen and used queue code that compares the get and put pointers to > > detect whether a character is available. I avoided it for a couple of > > reasons: > > > > * the pointer comparisons are not atomic on the 68K although that may > > not matter when checking for getptr != putptr > > You simply calculate "nchars = putptr - getptr", then adjust if there > has been a wrap around the end of the buffer. It does not matter that > this is not atomic - if a new character has come in during this > operation, then the new putptr will be greater than the one used, and > your nchars will be one character too small. But that's just the same > as if the new character came in after your code had finished. > > > * there are times when it is handy to have a count of the characters > > in the buffer. With that count you can implement a faster loop to > > pull characters from the queue. It's easier to increment and > > decrement a counter at one instruction each, than it is to get > > the queue length by pointer arithmetic that accounts for queue > > wraparound. > > > > Thus you calculate "nchars = putptr - getptr" at the start of your code, > and any new incoming characters are ignored until you next run the code > - just as if they arrived after the code was finished.
That sounds simple--but there are a few details to remember: 1. putptr can be smaller than getptr if the buffer has just wrapped around. 2. because of #1, you have to be careful about correcting for buffer wrap if you intend to actually use the character count in a loop and not just compare it with zero. 3. pointer arithmetic on 8 or 16-bit processors may involve quite a large number of instructions. With 8-bit processors, it may be best to limit the buffer size to something less than 256 bytes. If you need maximum efficiency, you could also use the technique of making the buffer length equal to an even power of 2. Then you can handle buffer wrap with a single AND instruction. I don't think I've had to worry that much about performance since my 6502 and 6800 assembly coding days.
> > > I do use that technique in my serial handler for the AT91SAM7 > > processor. There, I use the DMA support in the Atmel chips, > > so there is not an interrupt for each character or FIFO. > > That leaves me with comparing the getptr with the DMA > > receive pointer to detect incoming characters. > > > > One of the first uses for my serial I/O code on a new system > > is always as part of the monitor used to upload new code. If > > you start getting a lot of checksum errors when uploading > > new code, it's a good indication that you've missed something > > in the serial I/O driver. > > > > > > The simple example code here also lacks a few elements: > > 1: there is no error reporting for queue full errors > > or queue empty errors. Without those error reports, > > the program can't distinguish between a valid 0x00 > > character and the result of a get operation on an > > empty queue. > > 2. There should be a ChAvailable() function that returns > > the number of characters available. This prevents either > > blocking on an empty queue or the queue empty 0x00 > > character problem. > > 3. Many UARTS have just one interrupt, which has to handle > > receive, transmit and error interrupts, so the > > ISR will be more complex than that shown. If the UART > > has a FIFO, it gets to be even more fun! > > > > All this is correct - the code above is (hopefully!) just an example. >
Yep. There's usually a lot more going on under the hood of a good serial driver than we've discussed in these examples. It gets to be even more fun if there are FIFOs or DMA and you really want to minimize time in the handlers in high-traffic situations. I went through all that in a data multiplexer/concentrator application a decade or two back. We didn't exactly have to count cycles, but with 4 input channels and no FIFOs, we did have to optimize the receive interrupt handlers as best we could. The input data could be pretty 'bursty' and we had to make sure we could handle four separate channels in less than one character time at 115KB. IIRC the result involved a separate handler for each channel and each handler used hard-coded register addresses rather than computing addresses from a channel index. Mark Borgerson
"Mark Borgerson" <mborgerson@comcast.net> wrote in message 
news:MPG.221592a1db7ff0a498979b@newsgroups.comcast.net...
>> >> It might also point out the fact that I'm never wrong (*). > > The only programmers I know that have never produced a bug are those > that haven't finished their first real application!
I was just clowning around, naturally. There was some famous discussion in a computer science journal (it had to do with early IPC work), where something like 6 people, all professional researchers, wrote in to correct the comments of the previous person who wrote in, and each one made a new mistake. These were people experienced in the field with IQ's way up there. Human beings weren't made for software. -- David T. Ashley (dta@e3ft.com) http://www.e3ft.com (Consulting Home Page) http://www.dtashley.com (Personal Home Page) http://gpl.e3ft.com (GPL Publications and Projects)
"David Brown" <david@westcontrol.removethisbit.com> wrote in message 
news:47ac2c51$0$14988$8404b019@news.wineasy.se...
> > If you *don't* have access to such an atomic decrement, you have two > options - disable interrupts around the critical code to make the > operation atomic, or use a better buffer structure! (Hint - do you really > need nchars?)
Good eye! You are correct! Holding too much state is a common cause of software defects. Between getpoint, putpoint, and nchars : I think you can show that only two (any two) of the three are required. I think though that if you eliminate nchars you can't fill the queue. If (getpoint == putpoint), the buffer is either empty or full, and you can't tell which without slightly more information. However, if you have (putpoint and nchars) or (getpoint and nchars) I think it will work. -- David T. Ashley (dta@e3ft.com) http://www.e3ft.com (Consulting Home Page) http://www.dtashley.com (Personal Home Page) http://gpl.e3ft.com (GPL Publications and Projects)
On Feb 8, 9:56 pm, "David T. Ashley" <d...@e3ft.com> wrote:
> "David Brown" <da...@westcontrol.removethisbit.com> wrote in message > > news:47ac2c51$0$14988$8404b019@news.wineasy.se... > > > > > If you *don't* have access to such an atomic decrement, you have two > > options - disable interrupts around the critical code to make the > > operation atomic, or use a better buffer structure! (Hint - do you really > > need nchars?) > > Good eye! > > You are correct! Holding too much state is a common cause of software > defects. > > Between getpoint, putpoint, and nchars : I think you can show that only two > (any two) of the three are required. > > I think though that if you eliminate nchars you can't fill the queue. If > (getpoint == putpoint), the buffer is either empty or full, and you can't > tell which without slightly more information. > > However, if you have (putpoint and nchars) or (getpoint and nchars) I think > it will work.
I've always liked the following: #define SIZE 64 // should be power of 2, and < 256 volatile unsigned char putptr; unsigned char getptr; unsigned char buffer[SIZE]; isr( ) { unsigned char c = GET_CHAR_FROM_HARDWARE(); if( (int) (putptr - getptr) < SIZE ) buffer[putptr++ % SIZE] = c; } int uart_getchar() { if( putptr == getptr ) return -1; return buffer[getptr++ % SIZE]; } SIZE should be a power of two, and less than 256 so that you can tell the difference between an empty and completely full buffer. As long as the incremented value for 'putptr++' can be atomically written to memory, interrupts don't need to be disabled in the uart_getchar() routine.
"Arlet" <usenet+5@c-scape.nl> wrote in message 
news:177da924-90c0-47be-9a6d-2386c04564cf@d21g2000prf.googlegroups.com...
> > SIZE should be a power of two, and less than 256 so that you can tell > the difference between an empty and completely full buffer.
Thanks. Your solution is _amazingly_ clever. It seems that essentially the extra required state to tell the difference between empty and full is stored in the upper bits of getptr and putptr. I think your solution is a special case of a more general problem that occurs in computer systems, but I'm not a good enough mathematician to state the problem. The problem is essentially how to arrange the state vector in a way that maps to the desired result under the machine instructions available. Your solution has the property that the C and Z flags from a subtraction give the desired information. One can arrange the state vector any way that one wants, but the question is how efficiently the machine instructions "equivalence class" it. Let me give a related example. In implementing state machines, one may see that the statement: if ((state == X) || (state == Y) || (state == Z)) occurs frequently. Clearly, testing against 3 different values isn't efficient. But if we choose X as 7, Y as 15, and Z as 31, then: if (state & 0x3) may do the trick. The critical question is the machine instructions available. It just so happens that with your choice of state vector (for the queue), the result of a subtraction equivalence classes in the way desired. Your queue implementation is related to vertical counters and to the problem stated above; but like I said, I'm not a good enough mathematician to make a general statement. -- David T. Ashley (dta@e3ft.com) http://www.e3ft.com (Consulting Home Page) http://www.dtashley.com (Personal Home Page) http://gpl.e3ft.com (GPL Publications and Projects)
David T. Ashley wrote:
> Between getpoint, putpoint, and nchars : I think you can show that only two > (any two) of the three are required. > > I think though that if you eliminate nchars you can't fill the queue. If > (getpoint == putpoint), the buffer is either empty or full, and you can't > tell which without slightly more information. > > However, if you have (putpoint and nchars) or (getpoint and nchars) I think > it will work.
Not really. nchars=0 and nchars=256 are just as indistinguishable (assuming the canonical case of keeping everything in 8-bit variables) as the two cases 'completely full' and 'completely empty' in the implementation using only pointers. A simpler solution is to step back a bit and re-consider how many bytes have to be in the buffer for it to be considered "full". If you keep the head and tail pointer separated by at least one position at all times, it becomes easier to implement the ring buffer. Squeezing out that one last byte of size is not always worth the effort.
Mark Borgerson wrote:
> In article <47ac830d$0$15002$8404b019@news.wineasy.se>, > david@westcontrol.removethisbit.com says... >> Mark Borgerson wrote: >>> In article <47ac2c51$0$14988$8404b019@news.wineasy.se>, >>> david@westcontrol.removethisbit.com says... >>>> Mark Borgerson wrote: >>>>> In article <MN2dnVFbIrGdszbanZ2dnUVZ_uidnZ2d@giganews.com>, dta@e3ft.com
<snip>
>>>> >>>> If you *don't* have access to such an atomic decrement, you have two >>>> options - disable interrupts around the critical code to make the >>>> operation atomic, or use a better buffer structure! (Hint - do you >>>> really need nchars?) >>>> >>>> >>> I've seen and used queue code that compares the get and put pointers to >>> detect whether a character is available. I avoided it for a couple of >>> reasons: >>> >>> * the pointer comparisons are not atomic on the 68K although that may >>> not matter when checking for getptr != putptr >> You simply calculate "nchars = putptr - getptr", then adjust if there >> has been a wrap around the end of the buffer. It does not matter that >> this is not atomic - if a new character has come in during this >> operation, then the new putptr will be greater than the one used, and >> your nchars will be one character too small. But that's just the same >> as if the new character came in after your code had finished. >> >>> * there are times when it is handy to have a count of the characters >>> in the buffer. With that count you can implement a faster loop to >>> pull characters from the queue. It's easier to increment and >>> decrement a counter at one instruction each, than it is to get >>> the queue length by pointer arithmetic that accounts for queue >>> wraparound. >>> >> Thus you calculate "nchars = putptr - getptr" at the start of your code, >> and any new incoming characters are ignored until you next run the code >> - just as if they arrived after the code was finished. > > That sounds simple--but there are a few details to remember: > > 1. putptr can be smaller than getptr if the buffer has just > wrapped around.
As I said above, "You simply calculate "nchars = putptr - getptr", then adjust if there has been a wrap around the end of the buffer."
> 2. because of #1, you have to be careful about correcting > for buffer wrap if you intend to actually use the character > count in a loop and not just compare it with zero.
It's not exactly hard to do: sint8_t nchars = putptr - getptr; if (nchars < 0) nchars += lengthOfBuffer; You might want to think about different cases if your buffer sizes are close to your counter type sizes (i.e., a buffer of more than 128 bytes when using 8-bit counters). If your buffers are a power of two in length, the an AND mask is even smaller and faster.
> 3. pointer arithmetic on 8 or 16-bit processors may involve > quite a large number of instructions. >
That's true, but irrelevant. We are working here with indexes into an array, not pointers - that way we can use minimal sized types, and happily do arithmetic, masking, and comparisons on them. Actually putting data into or taking data out of the buffer uses array access - it's the same whether or not you try to track nchars independently!
> > With 8-bit processors, it may be best to limit the buffer > size to something less than 256 bytes. If you need maximum > efficiency, you could also use the technique of making > the buffer length equal to an even power of 2. Then you > can handle buffer wrap with a single AND instruction. > I don't think I've had to worry that much about performance > since my 6502 and 6800 assembly coding days.
That's always the case when working with small micros - think about your types, and choose appropriate ones. It is unlikely, but not impossible, that you'll need a buffer of more than 256 bytes (255 actually - you can never have the buffer entirely full as it will be indistinguishable from entirely empty). The way to make your code flexible, efficient and portable is to typedef a "qcounter_t" type which is used throughout the implementation, and which is typedefed somewhere at the start of the module depending on the size of the buffers you need. Ideally you should typedef it to "uint_fast8_t" rather than just "uint8_t" - that will produce good code on everything from an 8-bit AVR to a ColdFire (which works faster with 32-bit data than 8-bit data).
>>> I do use that technique in my serial handler for the AT91SAM7 >>> processor. There, I use the DMA support in the Atmel chips, >>> so there is not an interrupt for each character or FIFO. >>> That leaves me with comparing the getptr with the DMA >>> receive pointer to detect incoming characters. >>> >>> One of the first uses for my serial I/O code on a new system >>> is always as part of the monitor used to upload new code. If >>> you start getting a lot of checksum errors when uploading >>> new code, it's a good indication that you've missed something >>> in the serial I/O driver. >>> >>> >>> The simple example code here also lacks a few elements: >>> 1: there is no error reporting for queue full errors >>> or queue empty errors. Without those error reports, >>> the program can't distinguish between a valid 0x00 >>> character and the result of a get operation on an >>> empty queue. >>> 2. There should be a ChAvailable() function that returns >>> the number of characters available. This prevents either >>> blocking on an empty queue or the queue empty 0x00 >>> character problem. >>> 3. Many UARTS have just one interrupt, which has to handle >>> receive, transmit and error interrupts, so the >>> ISR will be more complex than that shown. If the UART >>> has a FIFO, it gets to be even more fun! >>> >> All this is correct - the code above is (hopefully!) just an example. >> > > Yep. There's usually a lot more going on under the hood of a good > serial driver than we've discussed in these examples. It gets to > be even more fun if there are FIFOs or DMA and you really want to > minimize time in the handlers in high-traffic situations. > I went through all that in a data multiplexer/concentrator application > a decade or two back. We didn't exactly have to count cycles, > but with 4 input channels and no FIFOs, we did have to optimize > the receive interrupt handlers as best we could. The input data > could be pretty 'bursty' and we had to make sure we could handle > four separate channels in less than one character time at 115KB. > IIRC the result involved a separate handler for each channel and > each handler used hard-coded register addresses rather than > computing addresses from a channel index. > > > Mark Borgerson >
David T. Ashley wrote:
> "David Brown" <david@westcontrol.removethisbit.com> wrote in message > news:47ac2c51$0$14988$8404b019@news.wineasy.se... >> If you *don't* have access to such an atomic decrement, you have two >> options - disable interrupts around the critical code to make the >> operation atomic, or use a better buffer structure! (Hint - do you really >> need nchars?) > > Good eye! > > You are correct! Holding too much state is a common cause of software > defects. >
Holding too much state can be useful for efficiency, if you can be sure that the extra data is consistent. In this particular case, keeping consistency requires atomic operations or interrupt disabling, which is too expensive for the potential gains from knowing all three numbers.
> Between getpoint, putpoint, and nchars : I think you can show that only two > (any two) of the three are required. >
Yes - the number of characters in the buffer at any time is the number of characters put in the buffer, minus the number of characters removed.
> I think though that if you eliminate nchars you can't fill the queue. If > (getpoint == putpoint), the buffer is either empty or full, and you can't > tell which without slightly more information. >
That is correct. Without nchars, you can't fill the last byte of the buffer. But generally that is a cost worth paying for the advantages of the getpoint/putpoint method. Note that even with nchars, you come across the same situation if you have a 256 byte buffer and are keeping your variables as 8-bit data for efficiency.
> However, if you have (putpoint and nchars) or (getpoint and nchars) I think > it will work. >
True, but then you lose your independence of the variables. The point of using putpoint and getpoint is that the interrupt routine only ever modifies putpoint (it *reads* getpoint, but that's no problem), and the extraction routine only ever modifies getpoint (it *reads* putpoint - that's no problem if it can be read atomically, and if not, you can work around the issue without having to disable interrupts). If you track nchars, then the two functions both have to modify the same variable.

Memfault Beyond the Launch