# How to Efficiently Decode Non-Standard Serial Signal

I'm an undergraduate member of a research team working on a project that involves an RF-transmitting ASIC, and its wireless receiver which should ultimately send data to a PC.

The receiver outputs a fast, continuous, asynchronous, non-standard serial signal (i.e. not SPI, I2C, UART, etc) so my job is to write microcontroller software to interface the receiver to the computer. Currently my approach is to use edge-triggered interrupts to place the data in a circular buffer and do the whole bit-by-bit decoding process in the main loop. The microcontroller must simultaneously output this data using USB (virtual com port) to the computer.

Here is a problem I'm having, and one I'm anticipating:

1. I can not process the buffered data fast enough even with my quite powerful 72 MHz ARM Cortex M3 processor. The bitrate is 400 Kbps (2.5 us / bit). For reference that leaves only 180 cycles per bit (including the decoding AND the ISR, which has ~30 cycles of overhead ouch!). The MCU also has to handle a lot of other tasks which it polls for in the main loop.

2. The USB virtual com port driver is also interrupt based. This makes me almost certain that the driver will eventually have the processor interrupted for so long that it misses the 2.5 microsecond (180 cycle) window in which a bit may be transmitted. I am unsure how interrupt conflicts/races like this are normally resolved.

So the question is simply, what might one do to resolve these issues or is this not the right approach at all? I'm willing to consider less software-centric approaches as well. For example, using a dedicated USB chip with some kind of hardware state machine for the decoding, but this is unfamiliar territory.

-
 I have to say, it is rare that I see that many suggestions I like answered that quickly, speaks well to your question. I would be interested to know more about the data burts. Are they bursty, suddenly full speed and then periods of low data or is it plausible you would go an extended period with continuous data? – Kortuk♦ Jun 2 '12 at 16:31 As long as the ASIC has power it sends a continuous stream of data. Not bursty at all. It's a real-time medical sensing application with a computer readout. Ever seen an EKG? – Jay Keegan Jun 2 '12 at 18:13 So many great answers here. I saw a clear divide between solutions involving changes to the interrupts, and solutions involving dedicated hardware/digital logic. Things like FPGAs and Verilog I'm familiar with, but not yet experienced in so this means they must be saved for the long-term. In the short-term @rocketmagnets less interrupt-heavy method is fine. I do like the elegance of devoting menial tasks to digital logic and saving the ARM for true computation. In the future the ARM's power will be used for analysis and filtering of the wireless serial data. – Jay Keegan Jun 2 '12 at 18:51 Is the signal synchronous or asynchronous? – markrages♦ Jun 2 '12 at 21:29 Asynchronous. 4 start bits, 10 data bits, 2 stop bits. Due to the nature of the ASIC which is transmitting, the HI and LO times vary greatly from chip to chip. I've already written an algorithm to deduce the baud rate. – Jay Keegan Jun 3 '12 at 0:40
show 1 more comment

Another answer: Stop using interrupts.

People jump to use interrupts too easily. Personally, I rarely use them because they actually waste a lot of time, as you are discovering.

It's often possible to write a main loop which polls everything so rapidly that's it's latency is within spec, and very little time is wasted.

loop
{
{
// shift serial bit into a byte
}

{
// decode serial data
}

if (enough_serial_bytes_available)
{
// more decoding
}

if (usb_queue_not_empty)
{
// handle USB data
}
}


There might be some things in the loop which happen far more often than others. Perhaps the incoming bits for example, in which case, add more of those tests, so that more of the processor is dedicated to that task.

loop
{
{
// shift serial bit into a byte
}

{
// decode serial data
}

{
// shift serial bit into a byte
}

if (enough_serial_bytes_available)
{
// more decoding
}

{
// shift serial bit into a byte
}

if (usb_queue_not_empty)
{
// handle USB data
}
}


There might be some events for which the latency of this approach is too high. For example, you might need a very accurately timed event. In which case, have that event on interrupt, and have everything else in the loop.

-
I like your answer more than other Rocketmagnet person answer. Instead of more hadrware, faster hardware, more of something else, you Rocketmagnet, suggest: do less, better, simplier. – Rocket Surgeon Jun 2 '12 at 14:38
Okay, I have seen many cases were interrupts make a solution much much better. They do great things, allow well structured code, low latency and many other advantages, but I have to agree with you here. It looks like the process is so intense 1 controller may need to dedicate every bit of its attention to handling the serial stream. The digital front end sounds ideal to me but many times you have some micros and no FPGA around when it is a school project, I would probably dedicate a micro to handling it for me first and try to fit in an FPGA later to replace it for cost. – Kortuk Jun 2 '12 at 16:27
This is probably the solution I will go with in the short run. I was hoping to avoid this because it involves re-writing quite a bit of the existing serial drivers, but it is an elegant solution that is within my abilities over a short time frame. – Jay Keegan Jun 2 '12 at 18:24
@JayKeegan - Yes, it's probably the fastest route to a solution. PSoC and FPGA might be the approach for the next project. – Rocketmagnet Jun 2 '12 at 18:28

Easy: Use a PSoC5 Microcontroller.

You have all the ease of use of a microcontroller, plus it contains a CPLD, so you can write your own hardware peripherals in Verilog. Just write your serial data decoder in verilog, and use DMA to stream it to the USB port.

Meanwhile, the powerful 32-bit ARM core can be twiddling its Thumb instrutions.

-
The overview page doesn't list clock frequencies, which raised my suspicion. Datasheet says 40MHz (I also noted 6mA at 6MHz). That's half of what OP has now. "The MCU also has to handle a lot of other tasks", so it may depend on what those are whether this is a good idea or not. – stevenvh Jun 2 '12 at 11:28
They go up to 67MHz. So it's almost as fast as the OP's current processor, except that most of the work will be done in hardware, leaving the CPU with much more free time. – Rocketmagnet Jun 2 '12 at 11:39
I didn't look at all the datasheets. The first one I picked said 40MHz. – stevenvh Jun 2 '12 at 11:40
@stevenvh - They have different speed grades. The third number in the PN is the speed grade. (4=48MHz, 6=67MHz). – Rocketmagnet Jun 2 '12 at 11:50
Not good. Too complex. Look what other Rocketmagnet answered below. I am watching both of you – Rocket Surgeon Jun 2 '12 at 14:47
show 8 more comments

You could possibly use an FPGA instead of a Microcontroller to decode and buffer the wireless datastream. Then use the ARM processor to flush the FPGAs buffers (e.g. using a SPI interface) and ship the content out the USB Comm port. It's work, but an FPGA should be able to keep up easily so long as you are able to service it often enough to guarantee that its hardware buffers don't overrun (or if you can deal with dropped data at a higher level of the protocol).

-
 This could be an excellent solution in the long run. I was hoping I received a lot of digital logic / hardware solutions in addition to software solutions because now I have an excuse to learn about these things! I don't have an experience with FPGAs yet unfortunately. – Jay Keegan Jun 2 '12 at 18:31

I think you have a classic engineering choice to make: fast, cheap, works: pick two.

@vicatcu's solution is certainly a good one, but if you can't or won't add more hardware to it (and this includes a faster processor) then you have to make a choice. If this serial link is the most important than you should sit in the ISR until all bits have been collected. 180 instructions per bit is actually not bad at all, but don't try to do everything. When you detect the start of a transfer, spin until the transfer is completed. Stuff the result into a FIFO and then resume normal processing.

You don't say how long each transmission is but if they're short and bursty this would be a viable solution. I'm willing to bet that your virtual COM port implementation has some hardware buffering as well, so a "laggy" interrupt service for it shouldn't present too much trouble. As far as the rest of what the MCU needs to do... you have some design decisions to make.

-
 This solution kind of complements rocketman's software approach with reducing the number of interrupt-based drivers. I may keep the main serial driver I mentioned as interrupt-based. Also I'll try spinning until the whole frame is read as you mention. – Jay Keegan Jun 2 '12 at 18:38

First of all, I like some of the answers here already, and some have gotten my up vote.

But just to throw in another possible solution: given the constraints of your project, would adding a second microcontroller be bad (would that involve another board run)? Maybe a simple 8-bit microcontroller that connects to your Cortex-M3 via a fast peripheral like SPI. The 8-bit controller of your choice would poll for bits and form bytes just like in the selected answer, but when it does have a byte, it could dump it to the SPI data register for transferring out.

The cortex-M3 side would simply interrupt on SPI data received. That cuts down your previous 400 KHz external edge-triggered interrupt to 50 KHz.

The two reasons why I'm suggesting this is because some of the other methods (PSoC or added FPGA) are a bit expensive (although this likely does not matter for a low volume academic project) and because it may allow you to preserve some of the structure of your current code.

Other than that, I think the PSoC idea is awesome with your own custom peripheral transferring over DMA to USB.

-
 This is actually the plan I had in mind right around posting this. If I can't streamline the software by reducing the dependence on interrupts (selected answer) than surely this is what I will do. But yes it will require another board run, probably two because I suck at getting my designs right the first time. – Jay Keegan Jun 3 '12 at 0:34 @JayKeegan, haha welcome to the club! – Jon L Jun 3 '12 at 0:40

If your data format is similar to that of a UART, but at an unpredictable-yet-consistent baud rate, my inclination would be to use a CPLD to convert every word of incoming data into either SPI or standard-async format. I don't think there's any need to push all the way into the realm of CPLDs. Actually, even discrete logic might almost be workable. If you could generate a clock that was a smidgin more than 5x your desired data rate, you could use a divide-by-five and divide-by-16 counter with a few gates. Arrange the divide-by-five counter so that it will be held in reset whenever the input is idle and the the divide-by-16 counter is at zero. Otherwise generate an SPI clock pulse and bump the divide-by-16 counter any time the divide-by-five counter hits 2.

Given the 5x clock, one could generate the SPI clock using a 16V8 (the smallest and cheapest currently-available programmable logic device). A second 16V8 or 22V10 could be used as a fractional rate divider to generate the 5x clock, or one could use a slightly-larger chip (CPLD) and do everything in one.