This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

SCI DMA with FreeRTOS

Hello there,

I am trying to get some bytes received in SCI using DMA (FreeRTOS application), and I am getting STRANGE results. I only get ONE interrupt and then the code goes to some _dabort section and some self-test routines.

I pasted some parts of the code, which is really simple. I think I am setting things up properly but missing something. It also looks like I should do something inside the ISR (which I don't exactly know what it is, clearing some bits somewhere?).

void dmaSciConfigTXCtrlPacket(uint32 sadd,uint32 dadd,uint32 dsize,g_dmaCTRL* ctrl_packet)
{
	ctrl_packet->SADD      = sadd;                          /* source address             */
	ctrl_packet->DADD      = dadd;                          /* destination  address       */
	ctrl_packet->CHCTRL    = 0;                 /* channel control            */
	ctrl_packet->FRCNT     = dsize;                 /* frame count                */
	ctrl_packet->ELCNT     = 1;             /* element count              */
	ctrl_packet->ELDOFFSET = 0;                 /* element destination offset */
	ctrl_packet->ELSOFFSET = 0;                     /* element destination offset */
	ctrl_packet->FRDOFFSET = 0;                     /* frame destination offset   */
	ctrl_packet->FRSOFFSET = 0;                 /* frame destination offset   */
	ctrl_packet->PORTASGN  = 4;                 /* port b                     */
	ctrl_packet->RDSIZE    = ACCESS_8_BIT;         /* read size                  */
	ctrl_packet->WRSIZE    = ACCESS_8_BIT;         /* write size                 */
	ctrl_packet->TTYPE     = FRAME_TRANSFER ;   /* transfer type              */
	ctrl_packet->ADDMODERD = ADDR_INC1;         /* address mode read          */
	ctrl_packet->ADDMODEWR = ADDR_FIXED;       /* address mode write         */
	ctrl_packet->AUTOINIT  = AUTOINIT_OFF;       /* autoinit                   */
}

void dmaSciConfigRXCtrlPacket(uint32 sadd,uint32 dadd,uint32 dsize,g_dmaCTRL* ctrl_packet)
{
	ctrl_packet->SADD      = sadd;                          /* source address             */
	ctrl_packet->DADD      = dadd;                          /* destination  address       */
	ctrl_packet->CHCTRL    = 0;                 /* channel control            */
	ctrl_packet->FRCNT     = dsize;                 /* frame count                */
	ctrl_packet->ELCNT     = 1;             /* element count              */
	ctrl_packet->ELDOFFSET = 0;                 /* element destination offset */
	ctrl_packet->ELSOFFSET = 0;                     /* element destination offset */
	ctrl_packet->FRDOFFSET = 0;                     /* frame destination offset   */
	ctrl_packet->FRSOFFSET = 0;                 /* frame destination offset   */
	ctrl_packet->PORTASGN  = 4;                 /* port b                     */
	ctrl_packet->RDSIZE    = ACCESS_8_BIT;         /* read size                  */
	ctrl_packet->WRSIZE    = ACCESS_8_BIT;         /* write size                 */
	ctrl_packet->TTYPE     = FRAME_TRANSFER ;   /* transfer type              */
	ctrl_packet->ADDMODERD = ADDR_FIXED;         /* address mode read          */
	ctrl_packet->ADDMODEWR = ADDR_INC1;       /* address mode write         */
	ctrl_packet->AUTOINIT  = AUTOINIT_OFF;       /* autoinit                   */
}

void dmaSciInit(void){

	dmaEnable();

	dmaReqAssign(DMA_CH0,31U); // Assign DMA request channel 0
	dmaEnableInterrupt(DMA_CH0,BTC); // Enable interrupt of block complete
	dmaSciConfigTXCtrlPacket((uint32_t)&DMA_SCI_TX_DATA,((uint32)&(sciREG->TD))+3U, 10,&g_dmaCTRLPKT_TX); // Configure DMA control packet (big endian device!)
	dmaSetCtrlPacket(DMA_CH0, g_dmaCTRLPKT_TX); // Set DMA control packet

	dmaReqAssign(DMA_CH1,30U); // Assign DMA request channel 1
	dmaEnableInterrupt(DMA_CH1,BTC); // Enable interrupt of block complete
	dmaSciConfigRXCtrlPacket(((uint32)&(sciREG->RD))+3U,(uint32_t)&DMA_SCI_RX_DATA, 10,&g_dmaCTRLPKT_RX); // Configure DMA control packet (big endian device!)
	dmaSetCtrlPacket(DMA_CH1, g_dmaCTRLPKT_RX); // Set DMA control packet

	//dmaSetChEnable(DMA_CH0, DMA_HW);//Trigger DMA transmission
	dmaSetChEnable(DMA_CH1, DMA_HW);//Trigger DMA reception

}


//ISR 
void dmaBTCAInterrupt(void)
{
    uint32 offset = dmaREG->BTCAOFFSET;

/* USER CODE BEGIN (0) */
/* USER CODE END */

    if (offset != 0U)
    {
        //dmaGroupANotification(BTC, offset - 1U); //I am doing nothing here
    }

/* USER CODE BEGIN (1) */
/* USER CODE END */

}

// Simple task to trigger DMA and blik a LED

void vTask1(void *pvParameters){

sciREG->SETINT |= SET_RX_DMA | SET_RX_DMA_ALL; //Trigger RX using DMA on SCI

    for(;;){
        /* Toggle HET[1] with timer tick */
        gioSetBit(hetPORT1, 17, gioGetBit(hetPORT1, 17) ^ 1); //just blink a LED
        vTaskDelay(200);
    }
}

//Main part
void main(void)
{
/* USER CODE BEGIN (3) */
/* USER CODE END */

gioSetDirBit(hetPORT1,17,1); //LED PIN


sciInit();
dmaSciInit();


if (xTaskCreate(vTask1,"Task1", configMINIMAL_STACK_SIZE, NULL, 1, &xTask1Handle) != pdTRUE){
	while(1);
}

/* Start Scheduler */
vTaskStartScheduler();

/* Run forever */
while(1);


}
 


  • The DMA normally requires privilege in order to configure it.
    Try creating a task with privilege - see www.freertos.org/FreeRTOS-MPU-memory-protection-unit.html
  • Hello Anthony, thanks for replying.

    I tried with privileged tasks, and I still get the code going to _dabort. Interesting thing is that if I run a simple example of SCI with DMA with no RTOS whatsoever, things seem to work. As long as the FreeRTOS scheduler enters the scene, things stop working (I never return from dmaBTCAInterrupt ISR, no matter how simple this ISR is). Is there any SCI/SCIlin with DMA example including FreeRTOS around? That would be of a great help.

    Best.

    Ignacio
  • Ok, so, I don't exactly know why, but playing a bit with privileged tasks plus some MPU settings (apparently) did the trick. It is a bit unsettling when things get solved and you really don't know why.

    I suppose we can close this thread. Thanks.
  • Take a look at the TRM (reference manual).

    For each register if the register bit or bitfield you will see attributes listed under the field.

    If you see R-0 it means something like 'Read Only, reset value is 0'.

    Sometimes you will see 'R/WP' where WP means write in PRIVILEGE MODE only.

    Some of the DMA control registers you need to write have this P attribute. That's why.

    It is designed that way so that you can limit the code which is able to configure the DMA to just code that runs in privilege mode. That way you don't need to worry about your divide routine crashing and writing over the DMA registers which may take down the system..
  • You can temporary raise privilege using code below.

    #if defined(NO_OS)
    #define RAISE_PRIVILEGE
    #define RESET_PRIVILEGE
    #else
    #pragma SWI_ALIAS(prvRaisePrivilege, 1);
    extern BaseType_t prvRaisePrivilege( void );
    #define RAISE_PRIVILEGE BaseType_t xRunningPrivileged = prvRaisePrivilege ()
    #define RESET_PRIVILEGE if( xRunningPrivileged == 0 ) portSWITCH_TO_USER_MODE()
    #endif
    
    void configureTimerForRunTimeStats(void) {
        if (timer_already_init) {
            return;
        }
        _pmuInit_ ();
        _pmuEnableCountersGlobal_ ();
        _pmuStartCounters_ (pmuCYCLE_COUNTER);
        timer_already_init = true;
    }
    
    inline uint32_t getRunTimeCounterValue_(void) {
        RAISE_PRIVILEGE;
        if (!timer_already_init) {
            configureTimerForRunTimeStats();
        }
        uint32_t counter=_pmuGetCycleCount_();
        RESET_PRIVILEGE;
        return counter;
    }