Hi to you all,
I'm currently working on a project involving the LPC-Link2 as a eval. board for it's LPC4370 (for a complete explanation see this question).What I'm trying to do is:
At the moment I managed to be fine with the first 2 bullets, my question is: what is the fastest (and most elegant) way to process data without loosing (too much) samples and whitout interferring with DMA activity?I tried the following procedure:
BUT, it seems that I'm haviong troubles because of the cortex M4 ISR overhead. A drawing will better explain this:
The * in the image means:
How do I solve this? If anything isn't that clear I will be happy to explain it better.
Any help would be highly appreciated!
Regards,
Andrea
Hi Thibaut ZEISSLOFF, thanks for your replies.I must process a signal which is basically a pulse signal. That is: gaussian shape and almost constant lenght: This signal is not periodic, but my firmware should be able to process a 30 kcount/s flow.Strictly speaking, no: I don't need to perform a continuous processing, I just need the processing to be done before the next peak is completely acquired. And yes, fixed number of samples to be processed. The pulse length is roughly 10 us, so let's say @30 kcount/s that gives a 30% duty cycle. Since I'm acquiring @40 msps I need to process 400 samples in 20us.At the momenti I managed to set up the DMA transfer in a ping-pong buffer which is always running. The DMA isr (raised at the end of a sub-buffer transfer) then contains some if statements that check for the ADCHS flag and eventually process the acquired data.I understand what you suggest, but am I wrong thinking that triggering the DMA transfer would be too slow? Is this faster that switching between interrupts?I know this question may seem rhetorical, and I'm sorry for this. Thanks again!
Hi Thibaut, I looked at the doc (here it is for your reference) and this is what I found. In chapter 48, section 48.7.4.1 DMA read:
The FIFO can be read by DMA channel 8. A DMA transfer request is generated when the FIFO fill level is equal to or more than set by FIFO_LEVEL. This is the same behavior that will raise the interrupt flag FIFO_FULL. The burst size can be set to one in the DMA channel control register (see Table 350). If the FIFO fill level is not equal to one of the other DMA-supported burst sizes (applicable DMA burst sizes are 1, 4, 8 or 16), set the burst size to 1. Reading an empty FIFO will result in a value 0x00008000 or 0x80008000.
And that's it. The problem is that looking back to scetion 4.6.9 threshold A/B register a detection can occur only after conversion, in fact:
If, for two successive conversion results on a given channel, one result is below athreshold and the other is equal-to or above this threshold, then a threshold crossing hasoccurred. In this case the THCMP_CROSS (see Section 48.6.10) status bits will indicatethat a threshold crossing has occurred. A threshold crossing event will also generate aninterrupt 1 request if enabled to do so via the SET_EN1 bits associated with each channelin the SET_EN1 register.
So I suspect the ADC must be up and running all the time.Plus, there is the option to external-trigger the ADC conversion, but that can be done only through standard GPIO which I believe are digital comparators. The amplitude of my input signal changes over time (that's where the physical information I'm interested in is hidden): I think I would need to design a specific analog amplifier in order to work this way.
I'd love to know if there's a way to fill the fifo only after a threshold cross, this should work if combined with the DMA read request at a certain fifo level.Many thanks for your help Thibaut.Regards,Andrea
Hi Thibaut, thanks for your interesting answer.I started looking at the docs to figure out how to set up a "periodic" DMA transfer, from DMA to memory. If I understood correctly the idea is to capture the current DMA-main-buffer-address with a certain frequency and then transfer it to a location into the RAM.Now, I have one question: in the list of possible DMA sources, the DMA actually does not appear.Here it is:
/** * @brief GPDMA request connections */ #define GPDMA_CONN_MEMORY ((0UL)) /**< MEMORY */ #define GPDMA_CONN_MAT0_0 ((1UL)) /**< MAT0.0 */ #define GPDMA_CONN_UART0_Tx ((2UL)) /**< UART0 Tx */ #define GPDMA_CONN_MAT0_1 ((3UL)) /**< MAT0.1 */ #define GPDMA_CONN_UART0_Rx ((4UL)) /**< UART0 Rx */ #define GPDMA_CONN_MAT1_0 ((5UL)) /**< MAT1.0 */ #define GPDMA_CONN_UART1_Tx ((6UL)) /**< UART1 Tx */ #define GPDMA_CONN_MAT1_1 ((7UL)) /**< MAT1.1 */ #define GPDMA_CONN_UART1_Rx ((8UL)) /**< UART1 Rx */ #define GPDMA_CONN_MAT2_0 ((9UL)) /**< MAT2.0 */ #define GPDMA_CONN_UART2_Tx ((10UL)) /**< UART2 Tx */ #define GPDMA_CONN_MAT2_1 ((11UL)) /**< MAT2.1 */ #define GPDMA_CONN_UART2_Rx ((12UL)) /**< UART2 Rx */ #define GPDMA_CONN_MAT3_0 ((13UL)) /**< MAT3.0 */ #define GPDMA_CONN_UART3_Tx ((14UL)) /**< UART3 Tx */ #define GPDMA_CONN_SCT_0 ((15UL)) /**< SCT timer channel 0*/ #define GPDMA_CONN_MAT3_1 ((16UL)) /**< MAT3.1 */ #define GPDMA_CONN_UART3_Rx ((17UL)) /**< UART3 Rx */ #define GPDMA_CONN_SCT_1 ((18UL)) /**< SCT timer channel 1*/ #define GPDMA_CONN_SSP0_Rx ((19UL)) /**< SSP0 Rx */ #define GPDMA_CONN_I2S_Tx_Channel_0 ((20UL)) /**< I2S0 Tx on channel 0 */ #define GPDMA_CONN_SSP0_Tx ((21UL)) /**< SSP0 Tx */ #define GPDMA_CONN_I2S_Rx_Channel_1 ((22UL)) /**< I2S0 Rx on channel 0 */ #define GPDMA_CONN_SSP1_Rx ((23UL)) /**< SSP1 Rx */ #define GPDMA_CONN_SSP1_Tx ((24UL)) /**< SSP1 Tx */ #define GPDMA_CONN_ADC_0 ((25UL)) /**< ADC 0 */ #define GPDMA_CONN_ADC_1 ((26UL)) /**< ADC 1 */ #define GPDMA_CONN_DAC ((27UL)) /**< DAC */ #define GPDMA_CONN_I2S1_Tx_Channel_0 ((28UL)) /**< I2S1 Tx on channel 0 */ #define GPDMA_CONN_I2S1_Rx_Channel_1 ((29UL)) /**< I2S1 Rx on channel 0 */ // mch: added for HSADC #define GPDMA_CONN_HSADC_READ ((30UL)) /**< HSADC Samples */ #define GPDMA_CONN_HSADC_WRITE ((31UL)) /**< HSADC Descriptors 0 */
How am I supposed to set up a transfer from DMA to memory? Maybe I just misunderstood your suggestions and in that case I apologize for this.The problem is that I can imagine the
a simple index indicating at which position in the circular buffer the other channel is writing
just as the DMA writing address. Am I right?
I think that your suggestions are good Thibaut, especially about the fact that hardware flags can do the trick.Right now I looked at the timer manual and I can trigger DMA transfers from timers: that should not be difficult, but still, I don't know how to catch the position in the buffer.Just to clarify.This DMA works with Linked List: objects that specify the characteristics of each transfer. I have now 2 linked lists pointing to each other and continuously being called one after the other. That's how I fill my sub-buffers. I searched for minor/major loops in the docs, but no luck. Option 1 should be feasible, even if I don't know at the moment how to configure the DMA for this.
What is not clear to me is: how do I relate (without using software) a constant array in RAM/ROM with the position being accessed by the DMA in another array in RAM/ROM?
^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ Timer 1 @ f1 | | | | | | | | | | | ^ ^ ^ Timer 2 @ f2/4 | | | last sample position 0 1 2 3 4 5 6 7 8 9 ... last transfered index 255 0 1 ... ^ ADCHS comparison Interrupt | index = 0 sample_position = index * 4
Hi Thibaut, sorry for me being this late, but I'm attending lessons every day at university right now and I'm a bit busy. Nevertheless I must keep this project alive.Absolutely useful picture on what the firmware should do, thanks Thibaut. Hopefully I'll be able to test it with "real" signals soon enough.Therefore I'm trying to implement the timer triggered memory-to-memory DMA transfer. Unfortunately that's not easy to me. At the moment I have one main problem.
Connect the DMA channel to the timer, to use it (the timer) as a trigger: I looked at an LPCOpenV2.20 example, but I think is not working. Also, after writing the function to setup the timer and trigger the DMA, they kindly commented it. Nice one. This is the setup:
/* Setup DMA M2M transfer to trigger on timer match */ static void setupDMATrigger(void) { /* Initialize GPDMA controller */ Chip_GPDMA_Init(LPC_GPDMA); /* Get DMA channel */ dmaCh = Chip_GPDMA_GetFreeChannel(LPC_GPDMA, GPDMA_CONN_MAT0_0); /* Setup DMA transfer */ Chip_GPDMA_Transfer(LPC_GPDMA, dmaCh, (uint32_t) &source[0], GPDMA_CONN_MAT0_0, //(uint32_t) &dest[0], GPDMA_TRANSFERTYPE_M2M_CONTROLLER_DMA, sizeof(source)); // Chip_GPDMA_Stop(LPC_GPDMA, dmaChSSPTx); /* Enable GPDMA interrupt */ NVIC_EnableIRQ(DMA_IRQn); }
int main(void) { int numDmaXfers = 0; SystemCoreClockUpdate(); Board_Init(); /* Setup tiemr capture input used for DMA trigger */ setupTimerCapInput(); /* Setup timer for match event on each capture event */ setupTimerTrigger(); /* Setup DMA for memory to memory transfer on timer match event */ // setupDMATrigger(); /* Enable timer interrupt */ NVIC_EnableIRQ(TIMER0_IRQn); NVIC_ClearPendingIRQ(TIMER0_IRQn); /* Idle in background waiting for DMA events */ while (1) { ... etc...
Hi Thibaut, I saw in the past few months that this microcontroller is really rarely used and (I think) a bit underrated. I feel somewhat lost in trying to understand how it works. Anyway, maybe I'll just switch back to my previous solution as you said, but I'm also considering to purchase from NXP a Pro licence to have 1 year of email support. I'd really like to thank you for the help and I won't forget to update this post if I'll get this thing work.All the best,Andrea