13
\$\begingroup\$

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.

\$\endgroup\$
6
  • \$\begingroup\$ 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? \$\endgroup\$
    – Kortuk
    Jun 2, 2012 at 16:31
  • \$\begingroup\$ 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? \$\endgroup\$
    – Keegan Jay
    Jun 2, 2012 at 18:13
  • \$\begingroup\$ 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. \$\endgroup\$
    – Keegan Jay
    Jun 2, 2012 at 18:51
  • \$\begingroup\$ Is the signal synchronous or asynchronous? \$\endgroup\$
    – markrages
    Jun 2, 2012 at 21:29
  • \$\begingroup\$ 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. \$\endgroup\$
    – Keegan Jay
    Jun 3, 2012 at 0:40

6 Answers 6

6
\$\begingroup\$

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
{
    if (serial_bit_ready)
    {
        // shift serial bit into a byte
    }

    if (serial_byte_ready)
    {
        // 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
{
    if (serial_bit_ready)
    {
        // shift serial bit into a byte
    }

    if (serial_byte_ready)
    {
        // decode serial data
    }

    if (serial_bit_ready)
    {
        // shift serial bit into a byte
    }

    if (enough_serial_bytes_available)
    {
        // more decoding
    }        

    if (serial_bit_ready)
    {
        // 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.

\$\endgroup\$
4
  • \$\begingroup\$ 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. \$\endgroup\$
    – user924
    Jun 2, 2012 at 14:38
  • \$\begingroup\$ 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. \$\endgroup\$
    – Kortuk
    Jun 2, 2012 at 16:27
  • \$\begingroup\$ 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. \$\endgroup\$
    – Keegan Jay
    Jun 2, 2012 at 18:24
  • 1
    \$\begingroup\$ @JayKeegan - Yes, it's probably the fastest route to a solution. PSoC and FPGA might be the approach for the next project. \$\endgroup\$ Jun 2, 2012 at 18:28
7
\$\begingroup\$

Easy: Use a PSoC5 Microcontroller.

PSoC

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.

\$\endgroup\$
14
  • \$\begingroup\$ 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. \$\endgroup\$
    – stevenvh
    Jun 2, 2012 at 11:28
  • \$\begingroup\$ 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. \$\endgroup\$ Jun 2, 2012 at 11:39
  • 1
    \$\begingroup\$ I didn't look at all the datasheets. The first one I picked said 40MHz. \$\endgroup\$
    – stevenvh
    Jun 2, 2012 at 11:40
  • \$\begingroup\$ @stevenvh - They have different speed grades. The third number in the PN is the speed grade. (4=48MHz, 6=67MHz). \$\endgroup\$ Jun 2, 2012 at 11:50
  • 1
    \$\begingroup\$ This is also a fantastic solution in the long-run, much like the FPGA idea. I've never heard of this type of chip but it brings a lot of the functionality on the rest of my board into one chip. In the future this could mean the whole receiver fits on something the size of a thumb drive, which is my project lead's vision. I will be learning Verilog next semeseter. \$\endgroup\$
    – Keegan Jay
    Jun 2, 2012 at 18:29
6
\$\begingroup\$

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).

\$\endgroup\$
1
  • \$\begingroup\$ 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. \$\endgroup\$
    – Keegan Jay
    Jun 2, 2012 at 18:31
4
\$\begingroup\$

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.

\$\endgroup\$
1
  • \$\begingroup\$ 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. \$\endgroup\$
    – Keegan Jay
    Jun 2, 2012 at 18:38
3
\$\begingroup\$

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.

\$\endgroup\$
2
  • \$\begingroup\$ 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. \$\endgroup\$
    – Keegan Jay
    Jun 3, 2012 at 0:34
  • \$\begingroup\$ @JayKeegan, haha welcome to the club! \$\endgroup\$
    – Jon L
    Jun 3, 2012 at 0:40
2
\$\begingroup\$

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.

Edit/Addendum

Upon some further consideration, if one is going to use a CPLD, one can easily add a few additional enhancements to the circuit. For example, one may fairly easily add logic to have the circuit stall until it receives at least 1.5 bit times of stop bit, followed by 3.5 bit times of start bit; if it receives a too-short start bit, it should go back to looking for the stop bit. Also, if one is using SPI, one could use the /CS signal to ensure that the receiving device will see correctly-framed data. If the device receiving the SPI data can handle 10-bit frames, one could send such frames directly. Otherwise, each ten-bit frame could be sent as an 8-bit frame with the LSB set (7 bits of data), and a frame with all the LSB's clear (3 bits of data); the SPI clock would be accelerated during the stop bits so all the data would be sent. If the the second stop bit wasn't received correctly, the decoder chip could release CS before clocking out the entire last byte, or else set one of the lower (later-transmitted) bits which would normally have been left clear.

Some microcontrollers have rather versatile PWM-generation modules which include things like the ability to be held in reset by an external signal, and synchronize their timing to the release of such a signal. If your microcontroller can do that, depending upon its exact features, that might considerably simplify the CPLD or timing-generation circuitry.

Another approach which Rocketmagnet somewhat touched on, would be to have a small micro whose sole purpose is to decode the serial data and convert it into a format usable by the main micro. Your 400KHz data rate is pretty fast for software decoding, but something like a PIC could handle it if it didn't have to do anything else at the same time. Depending upon what devices you are familiar with, this could either be easier or harder than using a CPLD.

\$\endgroup\$
1
  • \$\begingroup\$ This will all be very valuable when designing the digital logic for decoding. I will indeed be outputting as SPI. For now, I am just doing the decoding using a standalone MCU (time constraints). Thank you! \$\endgroup\$
    – Keegan Jay
    Jun 7, 2012 at 2:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.