Changing BAUD rate on the fly for LPC2214

Started by ARK48 7 years ago9 replieslatest reply 7 years ago313 views
I'm using the LPC2214 (#LPC2200) as a controller on one of our controller boards. We normally communicate with this @ 115200 baud, which is quite sufficient for our application. We are now trying to change this baud to 460800 on the fly, such that we can download data to it faster. We do this by firstly sending a command to the controller, which it interprets and changes the baud rate. We can then send the data at the higher speed, before we resend the command to reset the baud rate back to 115200. The code is working quite well, for a limited number of changes of baud. However on the 8th attempt of baud rate change, the processor hangs... or I actually loose communications with it. Looking at the interrupts, THRE has been set. Can you please point me in a direction that can help me?
[ - ]
Reply by jorickJanuary 12, 2017

I'm not familiar with the LPC2214 but I do work with the LPC17xx/LPC40xx series so I'll give you my 2 cents based on that.  NXP likes to reuse functionality across their processor lines.

1. Make sure the UART is idle.  Nothing being received and the transmitter and transmit holding register are empty.

2. Disable interrupts while changing the baud rate.  All interrupts, not just the UART's.  You want to quickly switch without being interrupted in the middle, and it doesn't take long to do the switch.

On the LPC17xx, the baud rate divisor registers are shared with the Tx/Rx buffer and interrupt enable registers.  If you switch over to the baud rate divisor registers while the UART is active, you could really mess things up.  At the minimum the baud rate will change to a random value or your interrupts will go away.

[ - ]
Reply by ARK48January 12, 2017

Thanks for your prompt response. By disabling all interrupts beforehand and re-enabling after setting the baud, the system works perfect. Thanks again....

[ - ]
Reply by Tim WescottJanuary 12, 2017

I may be preaching to the choir here, but disable the interrupts for the least possible amount of time, and do it in a way that is interrupt safe (meaning, save the interrupt state, disable, and then restore -- that way if interrupts are already disabled you won't enable them at an inopportune moment).  Having a long interrupt disable just increases the worst-case interrupt response time for everything.

If the problem is caused by a race condition, then disabling interrupts will only narrow the window of opportunity for a problem rather than closing it.  This could possibly result in the switch disabling serial every one out of a thousand times, and possibly randomly, instead of exactly the eighth time every time.  That, in turn, means that you've turned a problem in the engineering lab into a time bomb that may go off in manufacturing or even in front of a customer.

If it were me, I'd feel a lot safer disabling the port, then changing the baud rate, then enabling the port.  This isn't based on any experience with your chip directly -- it's just general good (or perhaps paranoid) practice.

[ - ]
Reply by jorickJanuary 12, 2017

Below is my code for setting the baud rate, which resides between the interrupt save-and-disable and enable code.  This is the (modified) output from my compiler which is set to optimize for space.  Assembler lines start with a backslash and the number after the backslash is the number of clock cycles the instruction takes to execute, taken from a document at ARM Infocenter.  One clock is 8.33 nS at 120 MHz, the speed of the LPC1788.

The total time is 9 clocks, or 75 nS, which should be short enough for almost any application.  Of course, if you have a real-time application that requires exact interrupt timing, you'll probably want to disable the UART as per Tim Wescott.

Note: Bit-banding is used to allow quicker access to UART registers containing bits.

   xptUARTBit->sauwLineControl [UART__LC_DIVISOR_ACCESS] = YES;  // Bit-band
        \ 1   MOVS     R1,#+1
        \ 1   STR      R1,[R8, #+412]
   xptUART->suwBaudRateDivisorMSB = Byte1 (zuwDivisor);
        \ 1   LSLS     R1,R4,#+16
        \ 1   LSRS     R1,R1,#+24
        \ 1   STR      R1,[R7, #+4]
   xptUART->suwBaudRateDivisorLSB = Byte0 (zuwDivisor);
        \ 1   UXTB     R4,R4
        \ 1   STR      R4,[R7, #+0]
   xptUARTBit->sauwLineControl [UART__LC_DIVISOR_ACCESS] = NO;  // Bit-band
        \ 1   MOVS     R1,#+0
        \ 1   STR      R1,[R8, #+412]

[ - ]
Reply by Tim WescottJanuary 12, 2017

> The total time is 9 clocks, or 75 nS, which should be short enough for almost any application.

Short enough not to mess up other things, yes -- but if the Bad Thing that shorts out the UART is a hardware event and not some collision of software events, then it's only going to make the problem occur less frequently, it won't make it go away.

I've worked in groups where our less stellar software engineers would realize that they could fix problems by disabling interrupts around large chunks of code, without paying attention to the fact that they were creating more problems than they fixed -- so any time anyone talks of disabling interrupts I feel constrained to point out that you want to keep the time SHORT.

[ - ]
Reply by jorickJanuary 12, 2017

I've been there too.  I had a software engineer under me that put doubles in interrupts and even tried to do SPI transactions in an interrupt.  Fortunately he doesn't work here any more.  Another engineer wanted to do all software builds by hand and managed to get himself fired when he deleted all of our build scripts.

Normally I wouldn't bracket something like changing baud rates in an interrupt-disabled area but NXP devices don't have a way of switching off a UART other than disabling interrupts in some way.  There are three levels of interrupt disabling for a UART:

1) Globally, as seen above.  Very short code, even in the Debug node.

2) Disable the UART interrupt in the NVIC.  More involved, and takes more time to do than #1 since pending interrupts should be cleared prior to reenabling interrupts (NXP UARTs have a tendency to fire spurious interrupts on enable with this method).  At least it doesn't affect other interrupts in the system but it may slow down communications a bit.

3) Disable each of the three UART interrupts (THRE, RDA, and RXIE).  This is more involved than #1 or #2 and could be a real problem if a UART interrupt fires during the disable or enable process.  These interrupts should only be used by the UART handler and not touched by outside callers.

So I opted to mention the global method since the code is super short and won't affect applications except for the reeeeally time-sensitive ones.

If it's a hardware event that's causing the Bad Thing, then extensive testing needs to be done to weed it out.  Where I work, we test communications overnight (minimum) to a week (typical) to ensure that there are no problems.  Hopefully the OP does extensive testing as well.

Note:  I mentioned disabling the UART in a previous post but it was after I posted that I realized that the UART can't really be disabled, therefore prompting this post.

[ - ]
Reply by pawelkJanuary 12, 2017

If everyghing is working fine but always 8 times, then to me it sounds like a bug in software. What if you try to not send all data in fast mode, but just say 10bytes, and then you switch. Is it still 8 times, or you can do more?

[ - ]
Reply by ARK48January 12, 2017

Thanks for you prompt response, I've got it sorted. See below.

[ - ]
Reply by kevboJanuary 12, 2017

To the other answers I will add that it is possible that the trouble is on the other end of the serial link, especially (but not exclusively) if it is a Windows based system. Buffering delays and dropped characters will be common if you don't have hardware handshaking.  The higher the baud, the more likely this is to bite you.    Typically the Windows system drops one or more characters, and never sees a completed reply message.