Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Analog Buffer cannot be dynamically allocated on Teensy 4 #54

Open
drjyeh opened this issue Apr 10, 2020 · 2 comments
Open

Analog Buffer cannot be dynamically allocated on Teensy 4 #54

drjyeh opened this issue Apr 10, 2020 · 2 comments

Comments

@drjyeh
Copy link

drjyeh commented Apr 10, 2020

I am trying to use dynamically allocated buffers and AnalogBufferDMA, and found the following:

An instantiation of AnalogBufferDMA must be global if two buffers are used for continuous operation, is this correct.

An instantiation of AnalogBufferDMA can be dynamic if one buffer is used for IntervalTimer operation.

The buffers must be global. I tried malloc and new the buffer(s), and they will fill up the first time by the DMA, but will never be filled up again with new data.

I tried using double buffer for IntervalTimer operation, by setting stopOnCompletion(true) after init. But this does not work. I looked through the code and during init some other methods are called other then just setting _stop_on_completion.

@KurtE
Copy link
Collaborator

KurtE commented Apr 11, 2020

Yep - this was a quick and dirty class developed to test out the Teensy4 ADC operations. And so far it only has the ability to set the buffers with the constructor...

Would not be hard to add another member like: setBuffers or the like that can be called before init.
And/or could add support of having constructor allow for null ptr and have init do malloc...

But if DMAMEM or malloc memory is used will probably need to add code to discard the cache data ...

@drjyeh
Copy link
Author

drjyeh commented Apr 24, 2020

Hi, I have check the code and found some items of interest for the Teensy 4.

There seems to be some precompiler item regarding double buffer that can be made better:

  1. replaceSettingsOnCompletion() seems to not work for one-shot DMA operation. If I removed it, the library works with double buffering with one-shot rather than one shot operation.
  2. one of the #ifdef for double buffer and may have been misplaced.
  3. it seems that micros() may be more relavent for timing critical operation than milis() for _last_isr_time, and a method to access it for timestamp. For rollover issue, maybe a combination of millis and micros?

Please see the code as follows:

//=============================================================================
// Init - Initialize the object including setup DMA structures
//=============================================================================
void CustomAnaBufDMA::init(ADC *adc, int8_t adc_num)
{
// enable DMA and interrupts
#ifdef DEBUG_DUMP_DATA
SERIAL_println("CustomAnaBufDMA::init"); Serial.flush();
#endif

#ifndef KINETISL
// setup a DMA Channel.
// Now lets see the different things that RingbufferDMA setup for us before
// See if we were created with one or two buffers. If one assume we stop on completion, else assume continuous.
if (_buffer2 && _buffer2_count && false) {
#ifdef ADC_DUAL_ADCS
_dmasettings_adc[0].source((volatile uint16_t&)((adc_num == 1) ? SOURCE_ADC_1 : SOURCE_ADC_0));
#else
_dmasettings_adc[0].source((volatile uint16_t&)(SOURCE_ADC_0));
#endif
_dmasettings_adc[0].destinationBuffer((uint16_t*)_buffer1, _buffer1_count * 2); // 2*b_size is necessary for some reason
_dmasettings_adc[0].replaceSettingsOnCompletion(_dmasettings_adc[1]); // go off and use second one...
_dmasettings_adc[0].interruptAtCompletion(); //interruptAtHalf or interruptAtCompletion

_dmasettings_adc[0].disableOnCompletion();    // we will disable on completion.

#ifdef ADC_DUAL_ADCS
_dmasettings_adc[1].source((volatile uint16_t&)((adc_num == 1) ? SOURCE_ADC_1 : SOURCE_ADC_0));
#else
_dmasettings_adc[1].source((volatile uint16_t&)(SOURCE_ADC_0));
#endif
_dmasettings_adc[1].destinationBuffer((uint16_t*)_buffer2, _buffer2_count * 2); // 2*b_size is necessary for some reason
_dmasettings_adc[1].replaceSettingsOnCompletion(_dmasettings_adc[0]);    // Cycle back to the first one
_dmasettings_adc[1].interruptAtCompletion(); //interruptAtHalf or interruptAtCompletion

_dmasettings_adc[1].disableOnCompletion();    // we will disable on completion.

_dmachannel_adc = _dmasettings_adc[0];

_stop_on_completion = true;

} else {
// Only one buffer so lets just setup the dmachannel ...
// SERIAL_printf("CustomAnaBufDMA::init Single buffer %d\n", adc_num);
#ifdef ADC_DUAL_ADCS
_dmachannel_adc.source((volatile uint16_t&)((adc_num == 1) ? SOURCE_ADC_1 : SOURCE_ADC_0));
#else
_dmachannel_adc.source((volatile uint16_t&)(SOURCE_ADC_0));
#endif
_dmachannel_adc.destinationBuffer((uint16_t*)_buffer1, _buffer1_count * 2); // 2*b_size is necessary for some reason
_dmachannel_adc.interruptAtCompletion(); //interruptAtHalf or interruptAtCompletion
_dmachannel_adc.disableOnCompletion(); // we will disable on completion.
_stop_on_completion = true;
}

if (adc_num == 1) {
#ifdef ADC_DUAL_ADCS
_activeObjectPerADC[1] = this;
_dmachannel_adc.attachInterrupt(&adc_1_dmaISR);
_dmachannel_adc.triggerAtHardwareEvent(DMAMUX_ADC_1); // start DMA channel when ADC finishes a conversion
#endif
} else {
_activeObjectPerADC[0] = this;
_dmachannel_adc.attachInterrupt(&adc_0_dmaISR);
_dmachannel_adc.triggerAtHardwareEvent(DMAMUX_ADC_0); // start DMA channel when ADC finishes a conversion
}
//arm_dcache_flush((void*)dmaChannel, sizeof(dmaChannel));
_dmachannel_adc.enable();

adc->adc[adc_num]->continuousMode();
adc->adc[adc_num]->enableDMA();

#ifdef DEBUG_DUMP_DATA
dumpDMA_TCD(&_dmachannel_adc);
dumpDMA_TCD(&_dmasettings_adc[0]);
dumpDMA_TCD(&_dmasettings_adc[1]);
#if defined(IMXRT1062) // Teensy 4.0

if (adc_num == 1) {
SERIAL_printf("ADC2: HC0:%x HS:%x CFG:%x GC:%x GS:%x\n", ADC2_HC0, ADC2_HS, ADC2_CFG, ADC2_GC, ADC2_GS);
} else {
SERIAL_printf("ADC1: HC0:%x HS:%x CFG:%x GC:%x GS:%x\n", ADC1_HC0, ADC1_HS, ADC1_CFG, ADC1_GC, ADC1_GS);
}
#endif
#endif
#else
// Kinetisl (TLC)
// setup a DMA Channel.
// Now lets see the different things that RingbufferDMA setup for us before
_dmachannel_adc.source((volatile uint16_t&)(SOURCE_ADC_0));;
_dmachannel_adc.destinationBuffer((uint16_t*)_buffer1, _buffer1_count * 2); // 2*b_size is necessary for some reason
_dmachannel_adc.disableOnCompletion(); // ISR will hae to restart with other buffer
_dmachannel_adc.interruptAtCompletion(); //interruptAtHalf or interruptAtCompletion
_activeObjectPerADC[0] = this;
_dmachannel_adc.attachInterrupt(&adc_0_dmaISR);
_dmachannel_adc.triggerAtHardwareEvent(DMAMUX_ADC_0); // start DMA channel when ADC finishes a conversion
_dmachannel_adc.enable();

adc->startContinuous(adc_num);
adc->enableDMA(adc_num);
#ifdef DEBUG_DUMP_DATA
dumpDMA_TCD(&_dmachannel_adc);
#endif

#endif

_last_isr_time = micros();
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants