5
\$\begingroup\$

I have a periodic ADC conversion (every 50 micro-seconds) and I use the result to do some further calculations and update the PWM load registers. The ADC is triggered by the up-down counter of hardware PWM and has to be done exactly at that time. I am using a Teensy 3.2 (ARM M4) and although it's a beast, I have to be as fast as possible with the ADC to be able to squeeze in the rest of the calculations before the next duty cycle starts.

The general idea to operate faster seems like using DMA which transfers the conversion results directly to system memory without involving CPU, which looks like

ADC_result -> DMA -> SRAM,

But after that I have to fetch the result back from the SRAM to process it, which looks like:

ADC_result -> DMA -> SRAM -> CPU -> finally getting processed.

Of course, not involving the DMA is way slower:

ADC_result -> CPU -> SRAM -> CPU -> finally getting processed.

My question is, since I have to put the conversion result into CPU to be processed anyhow, can't I just store it directly in processor registers without involving DMA or SRAM at all and process is right away?

ADC_result -> CPU -> finally getting processed.

There are a couple of points I would like to make to further clarify the nature of my issue and test any gaps in my understanding.

First, from what I have understood, DMA is mainly used when there is real logging to be done and we would to use the CPU in parallel, especially if the data providing peripheral is considerably faster/slower than CPU. This is not my case, since all the following calculation are dependent on the ADC conversion result and I have very little data being sent every 50 micro-seconds rather than huge chunks.

Second, there is no guarantee that I will be able to use processor registers freely, since they are subjugated to compiler optimization and might be totally ignored (using the register keyword in C).

I would love to hear any feedback on the matter, especially if my understanding is somewhat flawed or plain wrong.

\$\endgroup\$
  • 2
    \$\begingroup\$ You write in a comment that you are "implementing a motor control algorithm." In almost all such circumstances I've experienced, this required very carefully designed sample-to-output timing. I will tend to keep the sample-to-sample measurement cycle-exact from one to the next (this can be achieved with DMA or, often enough, without DMA) and also keep the time from sample-to-output as close to cycle-exact as I can achieve. This almost always means "some assembly" mixed with C. If not, the closed loop control isn't competitive enough and will fail to my betters in the marketplace, eventually. \$\endgroup\$ – jonk Apr 23 at 18:47
  • 2
    \$\begingroup\$ I should have added this: I also keep the sample-to-output as short as possible. Short is even more important than consistent timing, though both are important. A control algorithm can accept some inconsistency in the delay, if the delay is very short. But long delays -- and especially long delays with variability to them -- are certain death. Keep things as tight and close as possible and keep the variability as minimal as you can achieve. The rest will just follow, if you can do that. \$\endgroup\$ – jonk Apr 23 at 18:51
  • \$\begingroup\$ What is the dominant pole in the motor? \$\endgroup\$ – copper.hat Apr 24 at 2:41
8
\$\begingroup\$

If you just write a function that reads the ADC result into a variable, does some math on it, and then writes the result to PWM, and if you use a decent compiler with optimization turned up, then the critical data will probably end up in a register whether you want it to or not. That's really not that much different from setting up DMA just so that you can fetch one memory location and do the same thing.

Where I would use DMA for this sort of thing is if I were going to do something like fetch multiple ADC samples per cycle of the controller (which I often do) -- the DMA saves a lot vs. the overhead of an ISR to collect the data from an ADC.

\$\endgroup\$
  • \$\begingroup\$ thanks for the insight, but i am implementing a motor control algorithm which requires sampling at very specific times in order to measure the current when all 3 high side switches are on. So i really cant use a function and poll the ADC. \$\endgroup\$ – Firat.Berk.Cakar Apr 23 at 15:44
  • 1
    \$\begingroup\$ @Firat.Berk.Cakar I would think that many things about a motor control system would be so low-pass that none of this would be necessary \$\endgroup\$ – Scott Seidman Apr 23 at 18:06
  • 1
    \$\begingroup\$ I didn't say to use a function to poll the ADC. I just said that you can read the ADC register, deal with it, and write to PWM. You should set up your ADC to fire off of a timer, undoubtedly. \$\endgroup\$ – TimWescott Apr 23 at 18:13
  • \$\begingroup\$ @ScottSeidman, if the OP is controlling the motor current then he wants cycle-by-cycle, or near cycle-by-cycle control (the last one I did I got away with iterating the control loop every 2 PWM cycles, but I don't claim to be very proud of that). For that you want to be able to have low and predictable delay. \$\endgroup\$ – TimWescott Apr 23 at 23:30
6
\$\begingroup\$

My question is, since i have to put the conversion result into CPU to be processed anyhow, cant i just store it directly in processor registers without involving DMA or SRAM at all and process is right away?

No, this is not possible in an Cortex-M4. Moreover, you would not want this to be possible. To make it clear why: If you write your program in "not assembly", then you're going to use a compiler. Compilers generally abide by the ARM ABI which will let them use registers for data processing. What you are suggesting will cause both the compiler and the ADC to use the same location for different purposes at the same time. Your program will behave badly. You will have no way of telling a compiler that a certain register is off limits.

Of course, not involving the DMA is way slower:

ADC_result -> CPU -> SRAM -> CPU -> finally getting processed.

This isn't quite correct or incorrect. It's application specific. An interrupt can load the ADC result from the ADC data register directly to a CPU register if the function only uses that data locally and is "short". it's possible that data may never be stored in SRAM.

Now, whether you use DMA or an interrupt to transfer your data, interrupts are certainly easier to comprehend from a data flow point of view. Additionally, interrupts allow you to respond sample by sample, which may be necessary for your application. The down side is that it forces you to eat the overhead costs of entering and exiting an interrupt. That might matter for your application or not.

If you must do so much processing, that time is precious and you can not afford to operate on one sample at a time, DMA can get you out of that bind. DMA allows you to amortize the interrupt enter/exit costs over many samples (depending on your deadline). Additionally, there are some math algorithms which are much more efficient when performed over buffers rather than sample-by-sample as they arrive. Again, it will depend on your application.

Unless you spell out your applications requirements, it's impossible to tell which is the best approach.

\$\endgroup\$
  • \$\begingroup\$ Thanks for the long and comprehensive answer. First, i have to start the conversion at a very specific time (see my comment to other answer) and i also edited the first paragraph of my question. So in essence the conversion has to be started by the interrupt and now the question is rather to save this value with or without DMA. If i use the DMA, cant it lead to concurency issues ? For example when i start the conversation in the interrupt (with DMA) and move on directly to process it with in the interrupt, isnt it possible that the data isnt still there ? \$\endgroup\$ – Firat.Berk.Cakar Apr 23 at 16:08
  • \$\begingroup\$ FWIW, if you really need exact timing, when you read the reference manual carefully, you'll see the ADC can be hardware triggered by the PDB which can be triggered by the Flex Timer. If you set your hardware up correctly, you will not need to interrupt to start the conversion. \$\endgroup\$ – pgvoorhees Apr 23 at 19:29
4
\$\begingroup\$

There are three events that matter here:

  1. ADC start of conversion
  2. ADC end of conversion
  3. Deadline for updating PWM duty cycle

Your start of conversion should be triggered by hardware. It sounds like your PWM is already doing this.

You have to do the processing between the end of conversion and the deadline. The best way to do this is to have the end of conversion trigger a CPU interrupt. Your ADC should be able to do this. In the ISR, the CPU can do the processing and update the duty cycle.

If you're writing C code, you don't need to worry about SRAM and registers. Just read the ADC result into an integer variable. Here's some pseudo-code:

void ADC_EOC_ISR(void)
{
    uint16_t adc_result = *ADC_RESULT_REG_ADDR;
    uint16_t new_duty_cycle_value = compute_new_duty_cycle(adc_result);
    *PWM_DUTY_CYCLE_REG = new_duty_cycle_value;
}

uint16_t compute_new_duty_cycle(uint16_t feedback)
{
    ... //Do processing here
}

The exact method for accessing the ADC result and PWM duty cycle registers depends on what MCU you're using and what header files/drivers have been provided.

\$\endgroup\$
1
\$\begingroup\$

Using DMA turns your hard realtime problem (process the sample before the next sample is overwritten) into a soft realtime problem (process samples faster than they accrue in the buffer and before the buffer gets full).

Hard realtime problems are annoying to handle, you usually need an interrupt source that is synchronous with the ADC (either a "conversion finished" interrupt from the ADC, or a periodic timer interrupt that also starts ADC conversion), which means that your CPU will spend quite a large amount of time entering and exiting interrupt handlers.

That can be a valid design if the ADC clock is slow enough that the interrupt handler finishes before the next interrupt arrives, and this is the highest priority interrupt (so other interrupt handlers do not stop data processing), but this is an engineering trade-off: the DMA based design is simpler, so unless delay and jitter need to be tightly controlled, it is likely the better choice.

\$\endgroup\$
1
\$\begingroup\$

I'm pretty sure you don't need to do what you want (and instead consume the ADC result directly in the interrupt as @AdamHaun suggests), but one trick that usually works is using the FPU registers. You'll have to check your compiler documentation, but with most compilers I've seen, if you don't use any float math in your code, the compiler will not touch the FPU registers (D0-D15), and you can use them yourself as a fast temporary storage.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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