Hi,
I'm using a TMS570 running with Micrium µC/OS-III. I have written a DMA driver which currently works well except for one detail: the interrupts stay in a pending state. When testing with DMA channel 0, I've enabled bit 0 of register FTCINTENAS (FTC Interrupt Enable Set) and of register GCHIENAS (Global Channel Interrupt Enable Set Register). I've also made sure that the VIM module has the address of my interrupt handler and that it is enabled for the FTC interrupt. Nevertheless, my program never enters my interrupt handler. After the first execution, the values of an array are transfered to another array. Because register FTCAOFFSET (FTCA Interrupt Channel Offset Register) contains the value 1, I know my interrupt on channel 0 is pending.
Here is my current code :
/*
* Project : Darius_TMS
*
* Module : /CSP/DMA
*
* File : dma_driver.c
*
* Owner : DARIUS (Team #3 – gegi s7/s8 project)
Université de Sherbrooke
*
* Status : UNFINISHED
*
*
* Description : DMA Driver
*
* Modification History
* --------------------
*
* Date Version Who Description
* ----------------------------------------------------------
*
* 2012-04-09 1.0.0 Maxime Initial version
*
*/
/*
*********************************************************************************************************
* INCLUDE FILES
*********************************************************************************************************
*/
#include <dma_driver.h>
#include <csp.h>
#include <log.h>
/*
*********************************************************************************************************
* GLOBAL VARIABLES
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* LOCAL DEFINES
*********************************************************************************************************
*/
#define VOID_PTR ((CPU_VOID *) 0)
/*
*********************************************************************************************************
* LOCAL VARIABLES
*********************************************************************************************************
*/
/* Registers */
static DMAPrimaryCtrlPkt *_PrimaryCtrlPkt[DMA_NBR_AVAILABLE_CHANNELS];
//static
#define _SWChannelEnableReg ((DMA_REG_SWCHENAS *) DMA_REG_ADDR_SWCHENAS)
#define _GlobalControlReg ((DMA_REG_GCTRL *) DMA_REG_ADDR_GCTRL)
#define _PortAssignmentReg ((DMA_REG_PAR *) DMA_REG_ADDR_PAR0)
#define _FTCInterruptEnableReg ((DMA_REG_FTCINTENAS *) DMA_REG_ADDR_FTCINTENAS)
#define _DMARequestAssignement ((DMA_REG_DREQASI *) DMA_REG_ADDR_DREQASI0)
#define _FTCBInterruptOffsetReg ((DMA_REG_FTCBOFFSET *) DMA_REG_ADDR_FTCBOFFSET)
#define _GlobalChannelIntEnReg ((DMA_REG_GCHIENAS *) DMA_REG_ADDR_GCHIENAS)
/* Interrupt argument */
static DMA_CHANNEL _ChannelIntArg;
/* Semaphores */
OS_MUTEX _DMATxMutex[DMA_NBR_AVAILABLE_CHANNELS];
/*
*********************************************************************************************************
* FUNCTION PROTOTYPES
*********************************************************************************************************
*/
static void DMAEnable();
static void DMAReset();
static DMA_CHANNEL_CFG *DMASetChannelCfg(DMA_CHANNEL *channel,
DMA_ELEM_SIZE *elementSize,
CPU_INT32U *initialSrcPtr,
CPU_INT32U *initialDestPtr,
CPU_INT16U *initialElemCount,
CPU_BOOLEAN *isSrcIncremented,
CPU_BOOLEAN *isDestIncremented);
static DMA_CHANNEL DMAGetChannelNumber(DMA_CHANNEL_CFG *channel);
static void DMAEnableInterrupt(DMA_CHANNEL *channel);
static void DMAFTCInterruptHandler(void *p_arg);
/*
*********************************************************************************************************
* DMA INIT
*
* Description : Initialize the DMA driver with the proper configuration.
*
* Arguments : none
*
* Returns : none
*
* Notes : none
*********************************************************************************************************
*/
void DMAInit()
{
_PrimaryCtrlPkt[0] = (DMAPrimaryCtrlPkt *) DMA_RAM_BASE_ADDRESS;
_PrimaryCtrlPkt[1] = (DMAPrimaryCtrlPkt *) (DMA_RAM_BASE_ADDRESS + 1*sizeof(DMAPrimaryCtrlPkt));
_PrimaryCtrlPkt[2] = (DMAPrimaryCtrlPkt *) (DMA_RAM_BASE_ADDRESS + 2*sizeof(DMAPrimaryCtrlPkt));
_PrimaryCtrlPkt[3] = (DMAPrimaryCtrlPkt *) (DMA_RAM_BASE_ADDRESS + 3*sizeof(DMAPrimaryCtrlPkt));
_PrimaryCtrlPkt[4] = (DMAPrimaryCtrlPkt *) (DMA_RAM_BASE_ADDRESS + 4*sizeof(DMAPrimaryCtrlPkt));
_PrimaryCtrlPkt[5] = (DMAPrimaryCtrlPkt *) (DMA_RAM_BASE_ADDRESS + 5*sizeof(DMAPrimaryCtrlPkt));
_PrimaryCtrlPkt[6] = (DMAPrimaryCtrlPkt *) (DMA_RAM_BASE_ADDRESS + 6*sizeof(DMAPrimaryCtrlPkt));
_PrimaryCtrlPkt[7] = (DMAPrimaryCtrlPkt *) (DMA_RAM_BASE_ADDRESS + 7*sizeof(DMAPrimaryCtrlPkt));
_PrimaryCtrlPkt[8] = (DMAPrimaryCtrlPkt *) (DMA_RAM_BASE_ADDRESS + 8*sizeof(DMAPrimaryCtrlPkt));
_PrimaryCtrlPkt[9] = (DMAPrimaryCtrlPkt *) (DMA_RAM_BASE_ADDRESS + 9*sizeof(DMAPrimaryCtrlPkt));
_PrimaryCtrlPkt[10] = (DMAPrimaryCtrlPkt *) (DMA_RAM_BASE_ADDRESS + 10*sizeof(DMAPrimaryCtrlPkt));
_PrimaryCtrlPkt[11] = (DMAPrimaryCtrlPkt *) (DMA_RAM_BASE_ADDRESS + 11*sizeof(DMAPrimaryCtrlPkt));
_PrimaryCtrlPkt[12] = (DMAPrimaryCtrlPkt *) (DMA_RAM_BASE_ADDRESS + 12*sizeof(DMAPrimaryCtrlPkt));
_PrimaryCtrlPkt[13] = (DMAPrimaryCtrlPkt *) (DMA_RAM_BASE_ADDRESS + 13*sizeof(DMAPrimaryCtrlPkt));
_PrimaryCtrlPkt[14] = (DMAPrimaryCtrlPkt *) (DMA_RAM_BASE_ADDRESS + 14*sizeof(DMAPrimaryCtrlPkt));
_PrimaryCtrlPkt[15] = (DMAPrimaryCtrlPkt *) (DMA_RAM_BASE_ADDRESS + 15*sizeof(DMAPrimaryCtrlPkt));
DMAReset();
DMAEnable();
_GlobalControlReg->debugMode = DMA_DEBUG_MODE_IGNORE;
CSP_IntVectReg(CSP_INT_CTRL_NBR_MAIN, /* Link the interrupt to the interrupt handler */
CSP_INT_SRC_NBR_DMA_FRAME_XFER_COMP,
DMAFTCInterruptHandler,
VOID_PTR);
CSP_IntEn(CSP_INT_CTRL_NBR_MAIN, /* Enable the interrupt */
CSP_INT_SRC_NBR_DMA_FRAME_XFER_COMP);
}
/*
*********************************************************************************************************
* DMA ADD CHANNEL
*
* Description : Set the DMA channel with the proper configuration.
*
* Arguments : channel Channel to configure.
* elementSize Size of an element. 8, 16, 32 or 64 bits.
* *initialSrcPtr Initial Source Pointer.
* *initialDestPtr Initial Destination Pointer.
* initialElemCount Number of elements to transfer. Enter the maximum number of elements
* you'll want to transfer. Value 0 - 0x1FFF.
* *isSrcIncremented Determines if the source pointer is incremented.
* *isDestIncremented Determines if the destination pointer is incremented.
*
* Returns : DMA_CHANNEL_CFG * Pointer to the configuration of the channel.
*
* Notes : none
*********************************************************************************************************
*/
DMA_CHANNEL_CFG * DMAAddChannel( DMA_CHANNEL channel,
DMA_ELEM_SIZE elementSize,
CPU_INT32U *initialSrcPtr,
CPU_INT32U *initialDestPtr,
CPU_INT16U initialElemCount,
CPU_BOOLEAN isSrcIncremented,
CPU_BOOLEAN isDestIncremented)
{
OS_ERR os_err;
DMAEnableInterrupt(&channel);
OSMutexCreate(&_DMATxMutex[channel],
"DMA Tx Mutex",
&os_err);
LogOSError(DMA_DRIVER, &os_err);
DMA_CHANNEL_CFG *channelCfg;
channelCfg = DMASetChannelCfg( &channel,
&elementSize,
initialSrcPtr,
initialDestPtr,
&initialElemCount,
&isSrcIncremented,
&isDestIncremented);
return channelCfg;
}
/*
*********************************************************************************************************
* DMA START TRANSFER
*
* Description : Start a DMA transfer.
*
* Arguments : *channel Channel number to start the transfer.
*
* Returns : none
*
* Notes : none
*********************************************************************************************************
*/
void DMAStartTransfer(DMA_CHANNEL_CFG *channel)
{
CPU_TS ts;
OS_ERR os_err;
DMA_CHANNEL channelNbr = DMAGetChannelNumber(channel); /* Find which channel it is */
OSMutexPend(&_DMATxMutex[channelNbr],
0,
OS_OPT_PEND_BLOCKING,
&ts,
&os_err);
LogOSError(DMA_DRIVER, &os_err);
_ChannelIntArg = channelNbr; /* Argument to the interrupt handler */
_SWChannelEnableReg->channelEnable |= (1 << channelNbr); /* Enable the proper channel */
}
/*
*********************************************************************************************************
* DMA CHANGE ELEM COUNT
*
* Description : Change the number of elements to be transfered.
*
* Arguments : *channel Pointer to channel address.
* elemCount Element count. Values range from 0 to 0x1FFF.
*
* Returns : none
*
* Notes : none
*********************************************************************************************************
*/
void DMAChangeElemCount(DMA_CHANNEL_CFG *channel,
CPU_INT16U elemCount)
{
CPU_TS ts;
OS_ERR os_err;
DMA_CHANNEL channelNbr = DMAGetChannelNumber(channel); /* Find which channel it is */
OSMutexPend(&_DMATxMutex[channelNbr], /* Make sure you're not transfering data */
0,
OS_OPT_PEND_BLOCKING,
&ts,
&os_err);
LogOSError(DMA_DRIVER, &os_err);
_PrimaryCtrlPkt[channelNbr]->initialTransferCount.initialElemCount = elemCount;
OSMutexPost(&_DMATxMutex[_ChannelIntArg], /* Free the mutex */
OS_OPT_POST_NONE,
&os_err);
LogOSError(DMA_DRIVER, &os_err);
}
/*
*********************************************************************************************************
* DMA CHANGE DEST PTR
*
* Description : Change the destination pointer of the channel. Can help setting up a buffer.
*
* Arguments : *channel Pointer to channel address.
* *destPtr New destination pointer.
*
* Returns : none
*
* Notes : none
*********************************************************************************************************
*/
void DMAChangeDestPtr( DMA_CHANNEL_CFG *channel,
CPU_INT32U *destPtr)
{
CPU_TS ts;
OS_ERR os_err;
DMA_CHANNEL channelNbr = DMAGetChannelNumber(channel); /* Find which channel it is */
OSMutexPend(&_DMATxMutex[channelNbr],
0,
OS_OPT_PEND_BLOCKING,
&ts,
&os_err);
LogOSError(DMA_DRIVER, &os_err);
_PrimaryCtrlPkt[channelNbr]->initialDestAddr = destPtr;
OSMutexPost(&_DMATxMutex[channelNbr],
OS_OPT_POST_NONE,
&os_err);
LogOSError(DMA_DRIVER, &os_err);
}
/*
*********************************************************************************************************
* INTERNAL ROUTINES
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* DMA ENABLE
*
* Description : Enable the use of the DMA controller.
*
* Arguments : none
*
* Returns : none
*
* Notes : none
*********************************************************************************************************
*/
static void DMAEnable()
{
_GlobalControlReg->dmaEnable = 1;
}
/*
*********************************************************************************************************
* DMA RESET
*
* Description : Reset the DMA controller.
*
* Arguments : none
*
* Returns : none
*
* Notes : none
*********************************************************************************************************
*/
static void DMAReset()
{
OS_ERR os_err;
_GlobalControlReg->dmaReset = 1;
while (_GlobalControlReg->dmaReset == 1) {
OSTimeDlyHMSM(0, 0, 0, 100,
OS_OPT_TIME_HMSM_STRICT,
&os_err);
}
}
/*
*********************************************************************************************************
* DMA SET CHANNEL CONFIGURATION
*
* Description : Set the DMA channel with the proper configuration.
*
* Arguments : channel Channel to configure.
* elementSize Size of an element. 8, 16, 32 or 64 bits.
* *initialSrcPtr Initial Source Pointer.
* *initialDestPtr Initial Destination Pointer.
* *initialElemCount Number of elements to transfer. Value 0 - 0x1FFF.
* *isSrcIncremented Determines if the source pointer is incremented.
* *isDestIncremented Determines if the destination pointer is incremented.
*
* Returns : DMA_CHANNEL_CFG * Pointer to the configuration of the channel.
*
* Notes : none
*********************************************************************************************************
*/
static DMA_CHANNEL_CFG * DMASetChannelCfg( DMA_CHANNEL *channel,
DMA_ELEM_SIZE *elementSize,
CPU_INT32U *initialSrcPtr,
CPU_INT32U *initialDestPtr,
CPU_INT16U *initialElemCount,
CPU_BOOLEAN *isSrcIncremented,
CPU_BOOLEAN *isDestIncremented)
{
_PrimaryCtrlPkt[*channel]->initialDestAddr = initialDestPtr;
_PrimaryCtrlPkt[*channel]->initialSourceAddr = initialSrcPtr;
_PrimaryCtrlPkt[*channel]->initialTransferCount.initialElemCount = *initialElemCount;
_PrimaryCtrlPkt[*channel]->initialTransferCount.initialFrameCount = 1;
_PrimaryCtrlPkt[*channel]->channelConfiguration.chain = DMA_CHAIN_NO_CHANNEL;
_PrimaryCtrlPkt[*channel]->channelConfiguration.readElemSize = *elementSize;
_PrimaryCtrlPkt[*channel]->channelConfiguration.writeElemSize = *elementSize;
_PrimaryCtrlPkt[*channel]->channelConfiguration.transferType = DMA_TTYPE_HARDWARE_FRAME;
_PrimaryCtrlPkt[*channel]->channelConfiguration.autoInitiationMode = DMA_AIM_ENABLED;
_PrimaryCtrlPkt[*channel]->channelConfiguration.addressingModeRead = DMA_ADDRM_INDEXED;
_PrimaryCtrlPkt[*channel]->channelConfiguration.addressingModeWrite = DMA_ADDRM_INDEXED;
if (isDestIncremented) {
_PrimaryCtrlPkt[*channel]->elemIdxOffset.destAddrIdx = 1;
}
else {
_PrimaryCtrlPkt[*channel]->elemIdxOffset.destAddrIdx = 0;
}
if (isSrcIncremented) {
_PrimaryCtrlPkt[*channel]->elemIdxOffset.sourceAddrIdx = 1;
}
else {
_PrimaryCtrlPkt[*channel]->elemIdxOffset.sourceAddrIdx = 0;
}
CPU_INT16U i;
for (i = 0; i < *elementSize; i++) { /* To calculate the size of the offset in bytes */
_PrimaryCtrlPkt[*channel]->elemIdxOffset.destAddrIdx = _PrimaryCtrlPkt[*channel]->elemIdxOffset.destAddrIdx << 1;
_PrimaryCtrlPkt[*channel]->elemIdxOffset.sourceAddrIdx = _PrimaryCtrlPkt[*channel]->elemIdxOffset.sourceAddrIdx << 1;
}
_PrimaryCtrlPkt[*channel]->frameIdxOffset.destAddrIdx = 0;
_PrimaryCtrlPkt[*channel]->frameIdxOffset.sourceAddrIdx = 0; /* Always 0 */
_PortAssignmentReg->channelPortAssignment[*channel].CHxPA = DMA_PA_PORT_B;
return (DMA_CHANNEL_CFG *) _PrimaryCtrlPkt[*channel];
}
/*
*********************************************************************************************************
* DMA GET CHANNEL NUMBER
*
* Description : Determines which channel number is associated with an address.
*
* Arguments : *channel Channel definition.
*
* Returns : none
*
* Notes : none
*********************************************************************************************************
*/
static DMA_CHANNEL DMAGetChannelNumber(DMA_CHANNEL_CFG *channel)
{
return (DMA_CHANNEL) (((CPU_INT32U)channel - (CPU_INT32U)DMA_RAM_BASE_ADDRESS) / (CPU_INT32U)DMA_PKT_PRIMARY_BASE_OFFSET);
}
/*
*********************************************************************************************************
* DMA ENABLE INTERRUPT
*
* Description : Enable interrupt for a channel.
*
* Arguments : *channel Channel number.
*
* Returns : none
*
* Notes : none
*********************************************************************************************************
*/
static void DMAEnableInterrupt(DMA_CHANNEL *channel)
{
_GlobalChannelIntEnReg->GCHIE |= (1 << *channel);
_FTCInterruptEnableReg->FTCInterruptEnable |= (1 << *channel);
}
/*
*********************************************************************************************************
* DMA INTERUPT HANDLER
*
* Description : DMA Frame Transfer Complete interrupt handler
*
* Arguments : *p_arg Pointer to the arguments list. No arguments are defined at the moment.
*
* Returns : none
*
* Notes : none
*********************************************************************************************************
*/
static void DMAFTCInterruptHandler(void *p_arg)
{
(void)p_arg;
OS_ERR os_err;
/*
Log(DMA_DRIVER, 0x00, "DMA FTC Interrupt");
Log(DMA_DRIVER, 0x00, "Valeur FTCB : %d", _FTCBInterruptOffsetReg->FTCB);
*/
OSMutexPost(&_DMATxMutex[_ChannelIntArg],
OS_OPT_POST_NONE,
&os_err);
/*
LogOSError(DMA_DRIVER, &os_err);
*/
}
In the previous code, I know the following lines work quite well for SPI and ADC, so I see no reason why it shouldn't work with the DMA module. As I said, I've checked the registers of the VIM module and everything is fine. CSP_INT_SRC_NBR_DMA_FRAME_XFER_COMP value is 33 like it is specified in the datasheet.
CSP_IntVectReg(CSP_INT_CTRL_NBR_MAIN, /* Link the interrupt to the interrupt handler */
CSP_INT_SRC_NBR_DMA_FRAME_XFER_COMP,
DMAFTCInterruptHandler,
VOID_PTR);
CSP_IntEn(CSP_INT_CTRL_NBR_MAIN, /* Enable the interrupt */
CSP_INT_SRC_NBR_DMA_FRAME_XFER_COMP);
Do you see what I am doing wrong?
Thanks in advance,
Maxime