The serial UART's "overflow error" and "framing error" flags are not valid until after you read data from from the UART's received character register. The mere fact that the UART has tugged on the CPU core's interrupt line, and caused an interrupt handler to be called, does NOT make these flags valid.
Yes, it would be nice if the documentation explained this. No, it definitely does not.
So now you know. So you don't have to spend a week tearing your hair out, trying to figure out why half the characters you receive on the serial port are evaporating into thin air.
Edit: Oh yes. The baud rate generator (BRG) sucks ass too. Using a 16 bit baud rate generator for more accuracy actually makes things worse. The data sheets claim .16% error. I found more like 11% error - enough to cause the UART to (correctly) throw a framing error at every single byte received.
This is going to be a long night. I have a feeling at least one more silicon bug is yet to be found. I'm getting really, really pissed off with Microchip Corp. They can't seem to make the UARTs on these 18xx series parts work. Maybe they'd be more careful with their testing if I were to pay the main offices in Arizona a visit... with my sword.
Edit 2: Sure enough. Change the BRG value from the datasheet recommended 103 to 98 and guess what? Suddenly the receive baud rate generator is finding all the characters. Know why the clock is so screwed up? We're running the part at 2.0 V, and the RC time constants on the main oscillator (which drives the BRG through a prescaler) aren't anywhere near what they're like at 5.0 V. Even with the correction, the baud rate generator is still screwed up. 0 bits are 100 us long, and 1 bits are 112 us long. Correct timing for 9600 baud is ~104-105 us.
(exasperated sigh) You know, I'm an engineer. The things I make, they WORK. That's what makes me an engineer. The reason they work is that, surprise surprise, I TEST THEM! When I say something will work under certain conditions, it's because *I tested it that way*. Apparently, Microchip has failed to do their engineering on this part. And so I had to do it for them.
That pisses me off. See, when I buy an engineered product, I expect it to WORK. That's what I'm paying them for - something that WORKS. I understand this is not always easy. And if someone doesn't think their part will work at 2.0 V, or hasn't actually tried it at 2.0 V... they need to say so. They need to *not* put things like "works at 2.0 V!" on their data sheets. They need to *not* put things like "internal oscillator block calibrated to 1%!" on their data sheets. But apparently that's just too hard...
Yes, good engineering is hard. That's why I'm still at work at 1am on a Friday night. But hopefully, because I put in this time (at my customer's expense, yet), someone else after me WON'T have to. It's a lesson that perhaps the people at Microchip Corp. could use to learn.
Edit 3: Changed things so it uses interrupts for reception. The ISR just reads the UART's receive reg into a global variable, checks for an overflow error (and twiddles CREN if found), sets a global flag, and then exits.
Everything works great now. :P
You can try and do things a million ways from Sunday, but unless you do it exactly the same way the morons tested it when the chips were made, it doesn't work for shit. That would be okay... if they would TELL YOU which of the nine hundred possible ways to do it is the "right" one.