I need to generate PWM signal with timer and modulate it by my signal so I decided to use DMA. Unfortunately I found out that DMA transaction doesn't occur by PWM event. I made an example project which shows a problem. In this project DMA controls GPIO pin (just for example) and it only works if I use TIMER_CFG_PERIODIC mode of the timer. If I choose TIMER_CFG_A_PWM DMA transactions never happend (interrupt never occurs). Could you tell me is there any problem in my code or it's just a feature of the processor?
#include <stdint.h> #include <stdbool.h> #include "stdlib.h" #include "inc/hw_ints.h" #include "inc/hw_memmap.h" #include "inc/hw_uart.h" #include "inc/hw_gpio.h" #include "inc/hw_types.h" #include "driverlib/interrupt.h" #include "driverlib/pin_map.h" #include "driverlib/rom.h" #include "driverlib/rom_map.h" #include "driverlib/sysctl.h" #include "driverlib/uart.h" #include "driverlib/udma.h" #include "driverlib/timer.h" #include "driverlib/gpio.h" #include <string.h> #define GPIO_BASE_OUTPUT1 GPIO_PORTF_BASE #define GPIO_PERIPH_OUTPUT1 SYSCTL_PERIPH_GPIOF void InituDMA(void); void InitGPIO(void); void InitTimer(void); void setup(); void loop(void); void TimerInt(void); //Saves the system clk volatile uint32_t g_ui32SysClock; //Array to save the GPIO states static uint32_t OutputState[4] = {0x00000002, 0x00000000, 0x00000000, 0x00000002}; static uint32_t OutputState2[4] = {0x00000002, 0x00000000, 0x00000002, 0x00000000}; //***************************************************************************** // // The control table used by the uDMA controller. This table must be aligned // to a 1024 byte boundary. // //***************************************************************************** #pragma data_alignment=1024 uint8_t DMAcontroltable[1024]; /* After 4 transfers the DMA is done and calls this interrupt This is to reset the DMA and re-enable it */ void TimerInt(void) { TimerIntClear(TIMER3_BASE, TIMER_TIMA_DMA); if(uDMAChannelModeGet(UDMA_CH2_TIMER3A | UDMA_PRI_SELECT) == UDMA_MODE_STOP) { //Set again the same source address and destination uDMAChannelTransferSet(UDMA_CH2_TIMER3A | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, OutputState, (void *)(GPIO_BASE_OUTPUT1 + 0x03FC), 4); } if(uDMAChannelModeGet(UDMA_CH2_TIMER3A | UDMA_ALT_SELECT) == UDMA_MODE_STOP) { uDMAChannelTransferSet(UDMA_CH2_TIMER3A | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, OutputState2, (void *)(GPIO_BASE_OUTPUT1 + 0x03FC), 4); } //Always needed since after it's done the DMA is disabled uDMAChannelEnable(UDMA_CH2_TIMER3A); } /* Function to setup the DMA */ void InituDMA() { //Just disable to be able to reset the peripheral state SysCtlPeripheralDisable(SYSCTL_PERIPH_UDMA); SysCtlPeripheralReset(SYSCTL_PERIPH_UDMA); SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA); SysCtlDelay(10); uDMAEnable(); uDMAControlBaseSet(DMAcontroltable); /* * This is for seting up the GPIO_BASE_OUTPUT1 with CH2 TimerA */ //Set the channel trigger to be Timer3A uDMAChannelAssign(UDMA_CH2_TIMER3A); //Disable all the atributes in case any was set uDMAChannelAttributeDisable(UDMA_CH2_TIMER3A, //UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST | UDMA_ATTR_USEBURST | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK); /* This sets up the item size to 8bits, source increment to 8bits and destination increment to none and arbitration size to 1 */ uDMAChannelControlSet(UDMA_CH2_TIMER3A | UDMA_PRI_SELECT, UDMA_SIZE_32 | UDMA_SRC_INC_32 | UDMA_DST_INC_NONE | UDMA_ARB_1); uDMAChannelControlSet(UDMA_CH2_TIMER3A | UDMA_ALT_SELECT, UDMA_SIZE_32 | UDMA_SRC_INC_32 | UDMA_DST_INC_NONE | UDMA_ARB_1); /* This will setup the transfer mode to basic, source address to the array we want and destination address to the GPIO state we chosed. It also sets the total transfer size to 2. */ uDMAChannelTransferSet(UDMA_CH2_TIMER3A | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, OutputState, (void *)(GPIO_BASE_OUTPUT1 + 0x03FC), 4); uDMAChannelTransferSet(UDMA_CH2_TIMER3A | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, OutputState2, (void *)(GPIO_BASE_OUTPUT1 + 0x03FC), 4); //Enable the DMA chanel uDMAChannelEnable(UDMA_CH2_TIMER3A); } /* This is to set all the pins of the GPIO chosen to output */ void InitGPIO() { SysCtlPeripheralDisable(GPIO_PERIPH_OUTPUT1); SysCtlPeripheralReset(GPIO_PERIPH_OUTPUT1); SysCtlPeripheralEnable(GPIO_PERIPH_OUTPUT1); SysCtlDelay(10); GPIOPinTypeGPIOOutput(GPIO_BASE_OUTPUT1, GPIO_PIN_1); GPIOPinWrite(GPIO_BASE_OUTPUT1, GPIO_PIN_1, 0); } /* Function to setup the timer to count down periodic with 1 second period. It also enables the DMA trigger and the event to timeout (counter reach 0) */ void InitTimer() { SysCtlPeripheralDisable(SYSCTL_PERIPH_TIMER3); SysCtlPeripheralReset(SYSCTL_PERIPH_TIMER3); SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER3); SysCtlDelay(10); TimerConfigure(TIMER3_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PWM);//TIMER_CFG_PERIODIC); // Periodic works fine TimerLoadSet(TIMER3_BASE, TIMER_A, 2499); TimerMatchSet(TIMER3_BASE, TIMER_A, 800); TimerIntClear(TIMER3_BASE, TIMER_TIMA_DMA); TimerIntRegister(TIMER3_BASE, TIMER_A, TimerInt); TimerIntEnable(TIMER3_BASE, TIMER_TIMA_DMA); TimerDMAEventSet(TIMER3_BASE, TIMER_DMA_TIMEOUT_A); } void main() { //Set CLK to ~80Mhz g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_20MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480), 80000000); InitTimer(); InitGPIO(); InituDMA(); //Enable the timer to start counting TimerEnable(TIMER3_BASE, TIMER_A); //Infinite loop and just watch the pins toggle while(true) { } }