You have a very good start from what you posted. It was a little unclear to me what you meant by a frame, since it could be a single UART character (typically 10 bits), or if it was a multi-byte packet.
It sounds like the issue you're running into is that you are using the first two bits of the UART character to set the baud rate, and so if the difference between the UART's configured baud rate and incoming baud rate is great enough, the UART peripheral fails to recognize that there has been any data.
Generally, I wouldn't think to change baud rate in the middle of receiving a character, and I think the problem is that the UART is not adjusting as you expect.
I would suggest using a sync character. ASCII 'U' makes a good choice because it generates an alternating sequence of 1 and 0 on the serial line. Depending on your application needs, you may be able to send one sync character to determine the initial baud rate, or you could send one before every data transfer if the baud rate is expected to change significantly. If you are just compensating for fluctuations in speed, you could use a hybrid approach where you set a baud rate based on a single sync character, and then tune that baud rate on every other character received.
For a single sync, your process would look a bit like this:
- Disable UART RX, enable input capture
- Wait until your input capture collects all the edges of the sync character.
- Calculate and set baud rate.
- Enable UART RX.
For a repeated sync, you would have the same process as above, but when the receiver went idle (this would need to be predetermined on both transmitter and receiver), it would return to step 1.
For a single sync and constant tuning, you would perform the same steps as above, but you would continue using input capture. When a character is received, you would use your first and last input capture values since the last complete character (whole character timing, 10 bits typically) to calculate a new baud rate, and set it, then clear your stored input capture values to adjust for the next character.
Alternatively, based on your comment, it sounds like you cannot use a sync byte, so I think your solution, if the microcontroller's UART peripheral is not able to handle the method on its own, would be to implement the receiver entirely with the timer module. Here's how I would do it, roughly:
- Use one channel of the timer as input capture to trigger on either edge.
- In a buffer of edge information (time and direction) 10 long (maximum of 10 transitions), on each interrupt record the counter value and direction of edge.
- After receiving your first two transitions (three if you want to use the two sync bits instead of start bit and one sync bit), you have the counter values needed to calculate a baud rate.
- Based on the baud rate calculated in 3, set a second timer channel in capture compare to trigger an interrupt 10 (or more, if you're using 2 stop bits) bit times from the first edge detected.
- When the capture compare channel fires an interrupt, you now have in your buffer the time and direction of each edge and you should be able to reassemble that into a byte.
- Disable the capture compare channel, and reset your index in the edge buffer.
If you're running in a Cortex-M3, even at a modest 32 MHz, you should have plenty of time to operate at 200 kbps. You may need to cycle through a few edge buffers, as I'm not sure how much time will be needed to convert edges into bytes, and if you are sending bytes back to back, you'll want at least two buffers you can ping pong between.
The details of how steps 1-6 would be implemented are going to vary based on the microcontroller you are using, but pretty much any timer peripheral should be able to handle that. Limitations would potentially be having to change which edge you trigger on if it doesn't support triggering on both simultaneously, and reading the state of the pin to figure out what edge triggered the interrupt if the timer doesn't report which edge cause it. You'll also have to carefully choose your time base for the timer so that you have both enough resolution to time your bits at high rates and enough runway in your counter to time your bits without multiple wrap-arounds on low rates.