MTB SAR DMA single transfer not working as expected

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

cross mob
rosenrot
Level 2
Level 2
First like given First like received 10 sign-ins

Hi everyone,

I'm still new to the PSoC6 and the ModusToolbox, so please excuse my questions in case I miss trivial aspects.

I want to take a single measurement from SAR Ch0 and transfer it to a variable using DMA. I'm starting slowly without multiple channels and DMA loops. I can see that the SAR is working correctly as the values I get from Cy_SAR_GetResult16(SAR, 0) change when changing the input voltage on pin10_0. However, the uint32_t adcValue variable, I would like to fill isn't changing. It stays constant. I know that SAR->CHAN_RESULT[0] has 32 bits with some status bits. However, the adcValue has to change when the input voltage changes, but it doesn't.

This is from the device configurator:

SAR

rosenrot_0-1682873828264.png

DMA

rosenrot_1-1682873865164.png

And this is the main.c

 

/*******************************************************************************
* Header Files
*******************************************************************************/
#include "cyhal.h"
#include "cybsp.h"
#include "cy_retarget_io.h"

/*******************************************************************************
* Global Variables
*******************************************************************************/
uint32_t adcValue;


int main(void)
{
    cy_rslt_t result;
    cycfg_config_init();

#if defined (CY_DEVICE_SECURE)
    cyhal_wdt_t wdt_obj;

    /* Clear watchdog timer so that it doesn't trigger a reset */
    result = cyhal_wdt_init(&wdt_obj, cyhal_wdt_get_max_timeout_ms());
    CY_ASSERT(CY_RSLT_SUCCESS == result);
    cyhal_wdt_free(&wdt_obj);
#endif

    /* Initialize the device and board peripherals */
    result = cybsp_init();

    /* Board init failed. Stop program execution */
    if (result != CY_RSLT_SUCCESS)
    {
        CY_ASSERT(0);
    }

    /* Enable global interrupts */
    __enable_irq();

    /* Initialize retarget-io to use the debug UART port */
    result = cy_retarget_io_init(CYBSP_DEBUG_UART_TX, CYBSP_DEBUG_UART_RX,
                                 CY_RETARGET_IO_BAUDRATE);

    /* retarget-io init failed. Stop program execution */
    if (result != CY_RSLT_SUCCESS)
    {
        CY_ASSERT(0);
    }

    /* Print message */
    /* \x1b[2J\x1b[;H - ANSI ESC sequence for clear screen */
    printf("\x1b[2J\x1b[;H");
    printf("-----------------------------------------------------------\r\n");
    printf("SAR DMA\r\n");
    printf("-----------------------------------------------------------\r\n\n");

    /* Initialize Aref & SAR */
    Cy_SysAnalog_Init(&pass_0_aref_0_config);
    Cy_SysAnalog_Enable();
    if (CY_SAR_SUCCESS != Cy_SAR_Init(SAR, &pass_0_sar_0_config))
    	{
    		printf("Error initializing SAR\r\n");
    		CY_ASSERT(0);
    	}
    Cy_SAR_Enable(SAR);
    Cy_SAR_StartConvert(SAR, CY_SAR_START_CONVERT_CONTINUOUS);

    /* Initialize DMA */
    if(CY_DMA_SUCCESS != Cy_DMA_Descriptor_Init(&cpuss_0_dw0_0_chan_15_Descriptor_0, &cpuss_0_dw0_0_chan_15_Descriptor_0_config))
    	{
        	printf("Error initializing DMA_D\r\n");
        	CY_ASSERT(0);
    	}
    Cy_DMA_Descriptor_SetSrcAddress(&cpuss_0_dw0_0_chan_15_Descriptor_0, &SAR->CHAN_RESULT[0]);
    Cy_DMA_Descriptor_SetDstAddress(&cpuss_0_dw0_0_chan_15_Descriptor_0, &adcValue);
    if(CY_DMA_SUCCESS != Cy_DMA_Channel_Init(cpuss_0_dw0_0_chan_15_HW, cpuss_0_dw0_0_chan_15_CHANNEL, &cpuss_0_dw0_0_chan_15_channelConfig))
    	{
            printf("Error initializing DMA_C\r\n");
            CY_ASSERT(0);
        }
    Cy_DMA_Channel_Enable(cpuss_0_dw0_0_chan_15_HW, cpuss_0_dw0_0_chan_15_CHANNEL);
    Cy_DMA_Enable(cpuss_0_dw0_0_chan_15_HW);


    for (;;)
    {
    	cyhal_system_delay_ms(500);
    	uint16_t result1 = Cy_SAR_GetResult16(SAR, 0);
    	printf("%d and %lu \r\n", result1,(unsigned long)adcValue);
    }
}

/* [] END OF FILE */

 

 

Can anyone tell me what I'm missing? It must be simple because this community has no questions about this. Of course, I can provide the MTB project if helpful.

0 Likes
1 Solution
ADSW
Moderator
Moderator
Moderator
250 sign-ins 50 solutions authored 25 likes received

Hi,
you're correct,  you miss  this 

 

Cy_DMA_Channel_SetDescriptor(cpuss_0_dw0_0_chan_15_HW, cpuss_0_dw0_0_chan_15_CHANNEL, & cpuss_0_dw0_0_chan_15_Descriptor_0);

 

 

What I see now is that when I set a sample rate of 1us for the SAR, which triggers sar_dma_complete() after about 20 measurements, the main thread seems to be blocked. The interrupt function is called repeatedly and the main thread is never active, as I see when debugging. I don't know why this happens.

Did you mean, you set the scan rate on configurator ? 

Yes, Interrupt function is called repeatedly by interrupt-controller so your main-thread didn't have chance to be executed. 
You can disable the interrupt after execute sar_dma_complete()  by calling  NVIC_DisableIRQ((IRQn_Type)ADC_DMA_IRQ);  

After we got the flag of dma_done in main-thread, we can take the adc_Value from dma_buffer and then we can clear the interrupt and enable it again. 

If you need to read an analog signal within a period of time, you can use buffer array. Once all the buffer are filled you can disable the interrupt, and take out all of values then enable the interrupt.. 

which interrupt priority do you use ? 

For interrupt in psoc you can refer to SYS_int PDL and NVIC-CMSIS

warm regards

View solution in original post

4 Replies
Andri-setyabudi
Level 5
Level 5
50 likes received 50 sign-ins 25 solutions authored

Hi, 

You need to change some of value in configuration of DMA as follows

Andrisetyabudi_0-1683020638393.png

Because you only use 1 variable as buffer, you need to change the Descriptor X and Y as shown in the picture above.

You missing some steps from your code for setup the DMA. You need to follow the following steps:

  1. Initialize the DMA descriptor and channel
  2. Set source and destination address for descriptor.
  3. Set descriptor of DMA channel
  4. Initialize and enable interrupt for DMA
  5. Enable DMA interrupt source
  6. Enable channel and DMA blocks

So your main function would be like this

// configure DMA
    cy_en_dma_status_t dma_init_status;
    /* Initialize descriptor 1 */
dma_init_status = Cy_DMA_Descriptor_Init(&cpuss_0_dw0_0_chan_15_Descriptor_0, &cpuss_0_dw0_0_chan_15_Descriptor_0_config);
	if (dma_init_status!=CY_DMA_SUCCESS)
	{
		printf("\x1b[2J\x1b[;H");
		printf( "DMA error on init Descriptor! \r\n");
		while(1);
	}

dma_init_status = Cy_DMA_Channel_Init(cpuss_0_dw0_0_chan_15_HW, cpuss_0_dw0_0_chan_15_CHANNEL, &cpuss_0_dw0_0_chan_15_channelConfig);
	if (dma_init_status!=CY_DMA_SUCCESS)
	{
		printf("\x1b[2J\x1b[;H");
		printf( "DMA error on init Channel ! \r\n");
		while(1);
	}
	/* Set source and destination address for descriptor */
	Cy_DMA_Descriptor_SetSrcAddress(&cpuss_0_dw0_0_chan_15_Descriptor_0, (uint32_t*)&(SAR->CHAN_RESULT[0]));
	Cy_DMA_Descriptor_SetDstAddress(&cpuss_0_dw0_0_chan_15_Descriptor_0, & adcValue);

	Cy_DMA_Channel_SetDescriptor(cpuss_0_dw0_0_chan_15_HW, cpuss_0_dw0_0_chan_15_CHANNEL, & cpuss_0_dw0_0_chan_15_Descriptor_0);

	/* Initialize and enable interrupt from SAR_DMA */
// setup the interrupt of DMA
    cy_stc_sysint_t SAR_DMA_INT_cfg =
	{
		.intrsrc=(IRQn_Type)ADC_DMA_IRQ,
		.intrPriority = 7u,
	};
	(void)Cy_SysInt_Init (&SAR_DMA_INT_cfg, sar_dma_complete);
	NVIC_EnableIRQ(SAR_DMA_INT_cfg.intrSrc);
	/* Enable DMA interrupt source. */
	Cy_DMA_Channel_SetInterruptMask(cpuss_0_dw0_0_chan_15_HW, cpuss_0_dw0_0_chan_15_CHANNEL, CY_DMA_INTR_MASK);
	/* Enable channel and DMA block to start descriptor execution process */
	Cy_DMA_Channel_Enable(cpuss_0_dw0_0_chan_15_HW, cpuss_0_dw0_0_chan_15_CHANNEL);
	Cy_DMA_Enable(cpuss_0_dw0_0_chan_15_HW);
	for (;;)
    	{
		if( dma_done )
    		{
    			printf("dma buffer %i  \r\n",(int32) adcValue);
    			dma_done = 0;
    		};
	};

You need to create ISR function for handling the interrupt of DMA, and some global variable as flag.

So you can know if the data has been transferred to the DMA buffer.

uint32_t adcValue;
uint8_t dma_done;
void sar_dma_complete(void)
{
    Cy_DMA_Channel_ClearInterrupt(ADC_DMA_HW, ADC_DMA_CHANNEL);

    /* Check interrupt cause to capture errors. */
    if (CY_DMA_INTR_CAUSE_COMPLETION == Cy_DMA_Channel_GetStatus(ADC_DMA_HW, ADC_DMA_CHANNEL))
    {
    	dma_done = 1;
    }
}

warm regards, 

Andri Setyabudi

ADSW
Moderator
Moderator
Moderator
250 sign-ins 50 solutions authored 25 likes received

Hi @rosenrot , 
Are you still in this project ? 

I want to follow-up this thread, 
Do you have any comment or something to discus about above answer ? 
If there is any issue, please let me know.. 

Warm regards, 

0 Likes
rosenrot
Level 2
Level 2
First like given First like received 10 sign-ins

Hi @ADSW,

Thank you very much for your answer. I was off for several days now but I had the chance to try your suggestion yesterday.

Actually, the only thing missing for my code to work was to enable chaining with the same descriptor. Now it works.

However, before I found that out, I implemented all of your code to play with the interrupt routine as well, which, finally isn't a requirement but nice to have.

What I see now is that when I set a sample rate of 1us for the SAR, which triggers sar_dma_complete() after about 20 measurements, the main thread seems to be blocked. The interrupt function is called repeatedly and the main thread is never active, as I see when debugging. I don't know why this happens.

 

One difference to your code is that I didn't set the interrupt priority to 7. But that shouldn't be the reason right?

 

Any suggestions are welcome.

ADSW
Moderator
Moderator
Moderator
250 sign-ins 50 solutions authored 25 likes received

Hi,
you're correct,  you miss  this 

 

Cy_DMA_Channel_SetDescriptor(cpuss_0_dw0_0_chan_15_HW, cpuss_0_dw0_0_chan_15_CHANNEL, & cpuss_0_dw0_0_chan_15_Descriptor_0);

 

 

What I see now is that when I set a sample rate of 1us for the SAR, which triggers sar_dma_complete() after about 20 measurements, the main thread seems to be blocked. The interrupt function is called repeatedly and the main thread is never active, as I see when debugging. I don't know why this happens.

Did you mean, you set the scan rate on configurator ? 

Yes, Interrupt function is called repeatedly by interrupt-controller so your main-thread didn't have chance to be executed. 
You can disable the interrupt after execute sar_dma_complete()  by calling  NVIC_DisableIRQ((IRQn_Type)ADC_DMA_IRQ);  

After we got the flag of dma_done in main-thread, we can take the adc_Value from dma_buffer and then we can clear the interrupt and enable it again. 

If you need to read an analog signal within a period of time, you can use buffer array. Once all the buffer are filled you can disable the interrupt, and take out all of values then enable the interrupt.. 

which interrupt priority do you use ? 

For interrupt in psoc you can refer to SYS_int PDL and NVIC-CMSIS

warm regards