XMC4500...DMA and transfer of captured data from 32-bit timer

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
User10215
Level 4
Level 4
First like received
Hi everybody,

I'm trying to do the following:
- concatenation of two CCU4-timers (CCU42.CC40 and CCU42.CC41) to get a 32-bit timer
- capture the timer value of the concatenated timers when a falling edge occurs at the timer input
- the capture event from the timer triggers DMA0.CH0 to transfer the data from capture register 1 of each of the two timer slices to a place in RAM
- the DMA shall place the data in RAM in such a way, so that I get a 32-bit value out of the two 16-bit values from the two capture registers

This should effectively give me a timestamp each time the CCU sees a falling edge.
Using the XmcLib I was able to capture the 32-bit-timer value into the capture register 1 when a falling edge occurs. However, I'm not able to get the DMA running in the way I described above.
For configuration of the DMA I used Dave4.1.4. I also read Application Note AP32290 which describes pretty well how to transfer 16-bit-data from a peripheral to RAM using the ADC as an example.
Unfortunately it doesn't work for my application. This is my main.c, which shows how I configured the timers:

#include 
#include
#include
#include

uint32_t u32_CaptureVal[32];

/* Capture Slice configuration */
XMC_CCU4_SLICE_CAPTURE_CONFIG_t st_CaptureConfigSlice0 =
{
.fifo_enable = false,
.timer_clear_mode = XMC_CCU4_SLICE_TIMER_CLEAR_MODE_NEVER,
.same_event = false,
.ignore_full_flag = true,
.prescaler_mode = XMC_CCU4_SLICE_PRESCALER_MODE_NORMAL,
.prescaler_initval = (uint32_t) 0,
.float_limit = (uint32_t) 0,
.timer_concatenation = (uint32_t) 0
};

XMC_CCU4_SLICE_CAPTURE_CONFIG_t st_CaptureConfigSlice1 =
{
.fifo_enable = false,
.timer_clear_mode = XMC_CCU4_SLICE_TIMER_CLEAR_MODE_NEVER,
.same_event = false,
.ignore_full_flag = true,
.prescaler_mode = XMC_CCU4_SLICE_PRESCALER_MODE_NORMAL,
.prescaler_initval = (uint32_t) 0,
.float_limit = (uint32_t) 0,
.timer_concatenation = (uint32_t) 1
};

XMC_CCU4_SLICE_EVENT_CONFIG_t capture_event0_config = //off time capture
{
.mapped_input = XMC_CCU4_SLICE_INPUT_K, //CAPTURE on P1.15 via ERU1
.edge = XMC_CCU4_SLICE_EVENT_EDGE_SENSITIVITY_DUAL_EDGE,
.level = XMC_CCU4_SLICE_EVENT_LEVEL_SENSITIVITY_ACTIVE_HIGH,
.duration = XMC_CCU4_SLICE_EVENT_FILTER_DISABLED
};

XMC_GPIO_CONFIG_t st_CaptureInputPinConfig =
{
.mode = XMC_GPIO_MODE_INPUT_PULL_DOWN,
.output_level = XMC_GPIO_OUTPUT_LEVEL_LOW,
.output_strength = XMC_GPIO_OUTPUT_STRENGTH_STRONG_SHARP_EDGE,
};

uint16_t u16_DebugTimerValLsb = 0;
uint16_t u16_DebugTimerValMsb = 0;
uint32_t u32_DebugTimerVal = 0;
uint16_t u16_DebugTimerCaptValLsb = 0;
uint16_t u16_DebugTimerCaptValMsb = 0;
uint32_t u32_DebugTimerCaptVal = 0;

/**

* @brief main() - Application entry point
*
* Details of function

* This routine is the application entry point. It is invoked by the device startup code. It is responsible for
* invoking the APP initialization dispatcher routine - DAVE_Init() and hosting the place-holder for user application
* code.
*/
int main(void)
{
DAVE_STATUS_t status;
uint8_t u8_Counter = 0;

status = DAVE_Init(); /* Initialization of DAVE APPs */

if(status == DAVE_STATUS_FAILURE)
{
/* Placeholder for error handler code. The while loop below can be replaced with an user error handler. */
XMC_DEBUG("DAVE APPs initialization failed\n");

while(1U)
{

}
}
NVIC_SetPriority(ERU1_0_IRQn, 4U);
NVIC_EnableIRQ(ERU1_0_IRQn);

for(u8_Counter = 0; u8_Counter < 32; u8_Counter++)
{
u32_CaptureVal[u8_Counter] = 0xFFFFFFFF;
}

/* Enable clock, enable prescaler block and configure global control */
XMC_CCU4_Init(CCU42, XMC_CCU4_SLICE_MCMS_ACTION_TRANSFER_PR_CR);

/* Start the prescaler and restore clocks to slices */
XMC_CCU4_StartPrescaler(CCU42);
/* Start of CCU4 configurations */
/* Ensure fCCU reaches CCU42 */
XMC_CCU4_SetModuleClock(CCU42, XMC_CCU4_CLOCK_SCU);

/* Initialize the Slice */
XMC_CCU4_SLICE_CaptureInit(CCU42_CC40, &st_CaptureConfigSlice0);
XMC_CCU4_SLICE_CaptureInit(CCU42_CC41, &st_CaptureConfigSlice1);

XMC_CCU4_SLICE_SetTimerPeriodMatch(CCU42_CC40, 0xFFFF);
XMC_CCU4_SLICE_SetTimerPeriodMatch(CCU42_CC41, 0xFFFF);

/* Enable shadow transfer for PWM and Capture Slices */
XMC_CCU4_EnableShadowTransfer(CCU42, (uint32_t)(XMC_CCU4_SHADOW_TRANSFER_SLICE_0));
XMC_CCU4_EnableShadowTransfer(CCU42, (uint32_t)(XMC_CCU4_SHADOW_TRANSFER_SLICE_1));
/* Configure events */
XMC_CCU4_SLICE_Capture0Config(CCU42_CC40, XMC_CCU4_SLICE_EVENT_0);
XMC_CCU4_SLICE_Capture0Config(CCU42_CC41, XMC_CCU4_SLICE_EVENT_0);

XMC_CCU4_SLICE_ConfigureEvent(CCU42_CC40, XMC_CCU4_SLICE_EVENT_0, &capture_event0_config);
XMC_CCU4_SLICE_ConfigureEvent(CCU42_CC41, XMC_CCU4_SLICE_EVENT_0, &capture_event0_config);

XMC_CCU4_SLICE_EnableEvent(CCU42_CC40, XMC_CCU4_SLICE_IRQ_ID_EVENT0);
/* Connect capture on event 0 to SR0 */
XMC_CCU4_SLICE_SetInterruptNode(CCU42_CC40, XMC_CCU4_SLICE_IRQ_ID_EVENT0, XMC_CCU4_SLICE_SR_ID_0);

/* Get the slices out of idle mode */
XMC_CCU4_EnableClock(CCU42, 0);
XMC_CCU4_EnableClock(CCU42, 1);

XMC_CCU4_SLICE_StartTimer(CCU42_CC40);
XMC_CCU4_SLICE_StartTimer(CCU42_CC41);

NVIC_SetPriority(CCU42_0_IRQn, 3U);
NVIC_EnableIRQ(CCU42_0_IRQn);

/* Start DMA transfer */
XMC_DMA_CH_Enable(XMC_DMA0, 0);

uint32_t u32_DebugDelayCnt = 0;

/* Placeholder for user application code. The while loop below can be replaced with user application code. */
while(1U)
{
u16_DebugTimerCaptValLsb = (uint16_t)CCU42_CC40->CV[1];
u16_DebugTimerCaptValMsb = (uint16_t)CCU42_CC41->CV[1];
u32_DebugTimerCaptVal = (u16_DebugTimerCaptValMsb << 16) | u16_DebugTimerCaptValLsb;

for(u32_DebugDelayCnt = 0; u32_DebugDelayCnt < 10000; u32_DebugDelayCnt++)
{

}
}
}

void CCU42_0_IRQHandler(void)
{

}

void ERU1_0_IRQHandler(void)
{
}



I'm using a relax kit for the hardware and since the buttons on the board don't have a direct link to CCU-units I used the ERU to get the signal to the timer.
So now, when I press Button 2 on the board I can see in the debugger that the timer-values are captured into the capture registers. However, the DMA does nothing.
This is the "dma_ch_conf.c" that Dave did for me with the parameters I thought would be working:

#include "dma_ch.h"

extern uint32_t u32_CaptureVal[32]; /* Destination address symbol */

void DMA_CH_0_reload(DMA_CH_t *obj)
{
XMC_DMA_CH_SetBlockSize(obj->dma_global->dma, obj->ch_num, 2);
XMC_DMA_CH_SetSourceAddress(obj->dma_global->dma, obj->ch_num, (uint32_t)&CCU42_CC40->CV[1] /*0x40014178*/);
XMC_DMA_CH_SetDestinationAddress(obj->dma_global->dma, obj->ch_num, (uint32_t)u32_CaptureVal);
}

DMA_CH_CONFIG_t DMA_CH_0_CONFIG =
{
{
{
.enable_interrupt = false, /* Interrupts disabled */
.dst_transfer_width = XMC_DMA_CH_TRANSFER_WIDTH_16, /* Destination transfer width */
.src_transfer_width = XMC_DMA_CH_TRANSFER_WIDTH_16, /* Source transfer width */
.dst_address_count_mode = XMC_DMA_CH_ADDRESS_COUNT_MODE_INCREMENT, /* Destination address count mode */
.src_address_count_mode = XMC_DMA_CH_ADDRESS_COUNT_MODE_INCREMENT, /* Source address count mode */
.dst_burst_length = XMC_DMA_CH_BURST_LENGTH_4, /* Destination burst length */
.src_burst_length = XMC_DMA_CH_BURST_LENGTH_4, /* Source burst length */
.enable_src_gather = true, /* Source gather enabled? */
.enable_dst_scatter = false, /* Destination scatter enabled? */
.transfer_flow = XMC_DMA_CH_TRANSFER_FLOW_P2M_DMA, /* Transfer flow */
},
.src_gather_count = (uint32_t)1, /* Gather count */
.src_gather_interval = (uint32_t)127, /* Gather interval */
.block_size = 2U, /* Block size */
.transfer_type = XMC_DMA_CH_TRANSFER_TYPE_MULTI_BLOCK_SRCADR_RELOAD_DSTADR_CONTIGUOUS, /* Transfer type */
.priority = XMC_DMA_CH_PRIORITY_0, /* Priority level */
.src_handshaking = XMC_DMA_CH_SRC_HANDSHAKING_HARDWARE, /* Source handshaking */
.src_peripheral_request = DMA0_PERIPHERAL_REQUEST_CCU42_SR0_0, /* Source peripheral trigger */
.dst_handshaking = XMC_DMA_CH_DST_HANDSHAKING_HARDWARE /* Destination handshaking */
},
NULL,
0U
};


Notice the gather function that is supposed to get the data from capture register 1 of CCU42.CC40 and then jump to capture register 1 of CCU42.CC41.
When I start the program and press the button nothing happens in the array that the data should be transferred to (I observe that array in the debugger).
Then I modified the DMA-configuration and did the following changes:

- set "src_transfer_width" to " XMC_DMA_CH_TRANSFER_WIDTH_32"
- set "dst_burst_length" and "src_burst_length" to "XMC_DMA_CH_BURST_LENGTH_1"
- set "src_gather_interval" to "(uint32_t)63"

Everything else was kept unchanged. When I press the button with that configuration the first element of the destination array is filled with the complete 32-bit content
of CCU42_CC40->CV[1] (which I think is pretty weird because"dst_transfer_width" is still set to " XMC_DMA_CH_TRANSFER_WIDTH_16", so I expected a 16-bit value in the array).
When I press the button a second time the second element of the destination array is filled with CCU42_CC41->CV[1]. For each button press this repeats and the destination array is filled up.
I played around a little more with the configuration of the DMA but I wasn't able to achieve the behavior I wanted (described at the beginning of this post).

Is it only possible to get the capture register value with a transfer width of 32-bit? I never saw a DMA-transfer happening with a transfer width of 16-bit while playing around.
How is it possible to do what I described at the beginning of the post? Is it even possible?

Regards,
Niclas
0 Likes
2 Replies
User10215
Level 4
Level 4
First like received
Hi again,

I still haven't figured out how to solve the problem. Is what I'm trying to do even possible?

Also I have an additional question concerning the "hardware"-connections in the XMC4500 (for instance the ERU-signal that triggers a capture of the CCU or the CCU-capture triggering the DMA).
I mean the connection of peripherals via their Service Request Line. Is it correct that I can only connect those peripherals using the NVIC? This means that I get an interrupt which interferes with the CPU.
Is there a way to connect two peripherals without triggering an interrupt (not using ERU)?

Regards,
Niclas
0 Likes
jferreira
Employee
Employee
10 sign-ins 5 sign-ins First like received
Hi Niclas,

Your use case is perfectly possible although not straight foward:
1. You need to use two DMA channels.
2. One DMA channel transfers the LSB (CC40 capture register) (fixed address) to destination address (lower half word). Higher priority
2. Other DMA channel transfers the MSB (CC41 capture register) (fixed address) to destination address (upper half word). Lower priority

With this setup, the sequence is a follows (if destination address is an array):
Trigger 1 DMA_CH0: capture reg LSB to address 0
DMA_CH1: capture reg MSB to addres 2
Trigger 2 DMA_CH0: capture reg LSB to address 4
DMA_CH1: capture reg MSB to addres 6
...

Regarding the hardware connections, the XMC peripherals generate service request signals that can be connected directly to other peripherals (i.e. IOUT of ERU_OGU slice to CCUxCCy input event) without the need to generate an interrupt.
The service request signals in addition can also be connected to the NVIC to generate an IRQ in the CPU.

Best regards,
Jesus
0 Likes