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.
Hi,
I am new to the forums and this is my first question posted here. Anyway, I am doing some development with the LM4F120 Stellaris Launchpad and the on board chip is a TM4C1233H6PM. I am using Keil uVision4 as my IDE to develop my code. For the start up file I use startup_TM4C123.s and I use the driver library and header files that are found in C:\ti\TivaWare_C_Series-1.0.
I am trying to write code to receive incoming data on one SSI module (SSI3 in my code) and then send it on another SSI module (SSI 1 in my code). I am trying to set up uDMA ping pong mode for the receive side on SSI3. My basic vision of how my code should work is to initialize everything and then sit in a while loop until all the data has been received and transmitted. While in that while loop I expect the SSI to make DMA request when the FIFO has 4 data pieces in it. When that request happens I expect it to go to my SSI3_Handler interrupt handler and to put the code onto the SSI0 sending FIFO and repeat.
My problem is, is that for some reason, I never get into the interrupt handler. It appears as the the DMA request is never being asserted.
In writing this code I followed the udma_demo.c code provided in the tivaware file and also tried to follow the API and TM4C1233H6PM manuals.
The udma_demo code/project works properly on my board so I am not sure why following the same general outline but with SSI instead of UART is not working.
My code is attached.
/* DM_Comm_Initialize is the C file that contains all of the initialization functions needed to run the DM_Comm.c file. Written by Barrett Taylor on July 3, 2013. */ #include "DM_Headers.h" uint8_t g_uDMAControlTable[1024] __attribute__ ((aligned(1024))); uint16_t g_ROERxBufA[8]; uint16_t g_ROERxBufB[8]; /********************************************************************************************** void InitializeClock(): This function is used to set the system clock of the board to the desired values. **********************************************************************************************/ void InitializeClock(void) { //Set the system clock to run from the PLL and set to run at 66.6MHz. Set the main oscillator to 16MHz. SysCtlClockSet(SYSCTL_SYSDIV_3 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); } /********************************************************************************************** void Init_uDMA_Controller(void (* pfnHandler )(void));: This function is used to configure the uDMA controller. **********************************************************************************************/ void Init_uDMA_Controller() { SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA); //Enable the uDMA peripheral. IntEnable(INT_UDMAERR); //Enable the uDMA error interrupt. uDMAEnable(); //Enable the uDMA to allow configuration. uDMAControlBaseSet(&g_uDMAControlTable[0]); //Give the address of the control table parameters. } /********************************************************************************************** void InitializeSSI(): This function is used to initialize SSI0 over port A pins. This sets up the Master SSI to send data to the OSIRIS. **********************************************************************************************/ void InitializeSSI(void) { SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); //Enable GPIO port A pins which are used for SSI0. SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD); //Enable GPIO port D pins which are used for SSI3. SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); //Enable GPIO port F pins which are used for SSI1. SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0); //Enable SSI0 so the clock can be configured. SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI1); //Enable SSI1 so the clock can be configured. SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI3); //Enable SSI3 so the clock can be configured. SSIClockSourceSet(SSI0_BASE, SSI_CLOCK_SYSTEM); //Use the system clock as the clock for the SSI0. SSIClockSourceSet(SSI1_BASE, SSI_CLOCK_SYSTEM); //Use the system clock as the clock for the SSI1. SSIClockSourceSet(SSI3_BASE, SSI_CLOCK_SYSTEM); //Use the system clock as the clock for the SSI3. GPIOPinConfigure(GPIO_PA2_SSI0CLK); //Configure the four GPIO pins to be used for the SSI0. GPIOPinConfigure(GPIO_PA3_SSI0FSS); GPIOPinConfigure(GPIO_PA4_SSI0RX); GPIOPinConfigure(GPIO_PA5_SSI0TX); GPIOPinConfigure(GPIO_PF2_SSI1CLK); //Configure the four GPIO pins to be used for the SSI1. GPIOPinConfigure(GPIO_PF3_SSI1FSS); GPIOPinConfigure(GPIO_PF0_SSI1RX); GPIOPinConfigure(GPIO_PF1_SSI1TX); GPIOPinConfigure(GPIO_PD0_SSI3CLK); //Configure the four GPIO pins to be used for the SSI3. GPIOPinConfigure(GPIO_PD1_SSI3FSS); GPIOPinConfigure(GPIO_PD2_SSI3RX); GPIOPinConfigure(GPIO_PD3_SSI3TX); //Configure pins for use by the SSI0. GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5); //Configure pins for use by the SSI1. GPIOPinTypeSSI(GPIO_PORTF_BASE, GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_0 | GPIO_PIN_1); //Configure pins for use by the SSI3. GPIOPinTypeSSI(GPIO_PORTD_BASE, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3); //Set the SSI0 (OCE Comm) as a slave and clock it from the system clock previously defined and run at 4MHz with 16 bit words. SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_2, SSI_MODE_SLAVE, 4000000, 16); //Set the SSI1 (DM Tx) as a slave and clock it from the system clock previously defined and run at 2MHz with 16 bit words. SSIConfigSetExpClk(SSI1_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_2, SSI_MODE_SLAVE, 2000000, 16); //Set the SSI3 as a slave (DM Rx) and its clock to run from the system clock at 2MHz with 16 bit words. SSIConfigSetExpClk(SSI3_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_2, SSI_MODE_SLAVE, 2000000, 16); SSIEnable(SSI0_BASE); //Enable the SSI0. SSIEnable(SSI1_BASE); //Enable the SSI1. SSIEnable(SSI3_BASE); //Enable the SSI3. SSIDMAEnable(SSI3_BASE, SSI_DMA_RX); IntEnable(INT_SSI3); } /********************************************************************************************** void Configure_uDMA(): This functions configures the uDMA after it has been initialized and also after the SSI has been set up. **********************************************************************************************/ void Configure_uDMA(void) { uDMAChannelAssign(UDMA_CH14_SSI3RX); //Assign channel 14 to SSI0_Rx. uDMAChannelAttributeDisable(14, UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK); //Disable attributes on channel 14 to set to a known state. uDMAChannelAttributeEnable(14, UDMA_ATTR_USEBURST | UDMA_ATTR_HIGH_PRIORITY); //Use burst mode (hard set at 4). uDMAChannelControlSet(14 | UDMA_PRI_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_4); //Set the control words for channel 14 primary buffer. uDMAChannelControlSet(14 | UDMA_ALT_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_4); //Set the control words for channel 14 alternate buffer. uDMAChannelTransferSet(14 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *) (SSI3_BASE + SSI_O_DR), g_ROERxBufA, 4); //Set the transfer characteristics for channel 14 primary buffer. uDMAChannelTransferSet(14 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *) (SSI3_BASE + SSI_O_DR), g_ROERxBufB, 4); //Set the transfer characteristics for channel 14 alternate buffer. uDMAChannelEnable(14); } /********************************************************************************************** void Init_TIMER(): This function is used to configure the TIMER. **********************************************************************************************/ void Init_TIMER (void) { SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER3); //Enable TIMER3 peripheral function. SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB); //Enable port B peripheral function. GPIOPinConfigure(GPIO_PB2_T3CCP0); //Configure pin PB2 to be a timer. GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_2); //Specify the pin PB2 as a timer pin. TimerConfigure(TIMER3_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PWM); //Configure timer 3 to use timer A on this port and output a pulse ot the pin. TimerLoadSet(TIMER3_BASE, TIMER_A, 32); //Start/reset the timer to 32 every timer (divides down to ~2MHz). TimerMatchSet(TIMER3_BASE, TIMER_A, 16); //Set the match case (toggle case) to half the set value for 50% duty cycle. TimerEnable(TIMER3_BASE, TIMER_A); //Enable the timer to start it running. SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER2); //Enable the TIMER2 peripheral function. SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB); //Enable port B for peripheral function. GPIOPinConfigure(GPIO_PB0_T2CCP0); //Configure pin PB0 to be a timer. GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_0); //Specify pin PB0 as a timer pin. IntMasterEnable(); //Enable the interrupts. TimerConfigure(TIMER2_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_COUNT); //Configure timer 2 to use timer A and be an input capture counter. TimerControlEvent(TIMER2_BASE, TIMER_A, TIMER_EVENT_POS_EDGE); //Configure the capture event to be positive icoming edges. TimerLoadSet(TIMER2_BASE, TIMER_A, 14); //Set the timer to the desired start count. TimerMatchSet(TIMER2_BASE, TIMER_A, 0); //Set the match condition to the value where the counter ends and triggers the interrupt. IntEnable(INT_TIMER2A); //Enable the timer 2 interrupt for timer A. TimerIntEnable(TIMER2_BASE, TIMER_CAPA_MATCH); //Enable and configure the timer interrupt to be in capture mode. SysCtlPeripheralEnable(SYSCTL_PERIPH_WTIMER0); //Enable wide timer 0 (32 bit). SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC); //Enable port C peripheral function. GPIOPinConfigure(GPIO_PC5_WT0CCP1); //Configure pin PC5 to be a timer. GPIOPinTypeTimer(GPIO_PORTC_BASE, GPIO_PIN_5); //Specify pin PC5 as a timer pin. TimerConfigure(WTIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_B_CAP_COUNT); //Configure the timer to count on timer B. TimerControlEvent(WTIMER0_BASE, TIMER_B, TIMER_EVENT_POS_EDGE); //Configure the timer to count positive edges. TimerLoadSet(WTIMER0_BASE, TIMER_B, 0x01312D00); //Set the timer to take about 10 seconds (running off of a 2MHz clock). TimerMatchSet(WTIMER0_BASE, TIMER_B, 0); //Set the match constant to 0 so that it counts the whole way down before interrupting. IntEnable(INT_WTIMER0B); //Enable the interrupt for this timer. TimerIntEnable(WTIMER0_BASE, TIMER_CAPB_MATCH); //Enable the interrupt to trigger on the match constant. SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1); //Enable the TIMER4 peripheral. SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB); //Enable port C peripheral function. GPIOPinConfigure(GPIO_PB5_T1CCP1); //Configure pin PC0 to be a timer. GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_5); //Specify pin PC0 as a timer pin. TimerConfigure(TIMER1_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_B_CAP_COUNT); //Configure the timer to count on timer A. TimerControlEvent(TIMER1_BASE, TIMER_B, TIMER_EVENT_POS_EDGE); //Configure the timer to count positive edges. TimerLoadSet(TIMER1_BASE, TIMER_B, 4); //Count to FFFF (max count available). TimerMatchSet(TIMER1_BASE, TIMER_B, 0); //Set the match constant as 0 (when to fire interrupt). IntEnable(INT_TIMER1B); //Enable TIMER4 interrupt. TimerIntEnable(TIMER1_BASE, TIMER_CAPB_MATCH); //Enable the interrupt to trigger on the match constant. } /********************************************************************************************** void Init_GPIO(): This function is used to configure the GPIO ports to inputs/outputs, sets directions, internal resistors, etc. **********************************************************************************************/ void Init_GPIO (void) { volatile unsigned long delay_clk; //Delay for clock, must have 3 sys clock delay. SYSCTL_RCGC2_R |= 0x00000002; //Enable the system clock to PORT B. delay_clk = SYSCTL_RCGC2; //Dummy read to ensure the clock has stabilized. GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE, GPIO_PIN_3 | GPIO_PIN_4);//Make pin PB3 & PB4 be an output pin. GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_3, 0x08); //Start pin 3 (DA line) high. GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_4, 0x00); //Start pin 4 (OCE_DA line) low. }
/* DM_Comm_Functions is the C file that contains all of the functions neccessary to operate the DM_Comm.c file. This includes initialization functions as well as implementation functions. Written by Barrett Taylor on July 3, 2013. */ #include "DM_Headers.h" uint32_t g_pixel_count; /********************************************************************************************** void RunComms(): Is basically like main() in that this calls all the other functions. Needed to make a separate function to be able ot call it from timeout routines. **********************************************************************************************/ void RunComms(void) { uint16_t OCE_retreived_data[2]; //Variable to hold 16 bit data piece from OCELOT. uint16_t* OCE_retreived_data_ptr; //Pointer to the OCELOT retreived data. uint16_t DM_retreived_data[2]; //Variable to hold 16 bit data from the DM. uint16_t* DM_retreived_data_ptr; //Pointer to data received from the DM. bool go; OCE_retreived_data_ptr = &OCE_retreived_data[0]; //Give the address of the data to the pointer. DM_retreived_data_ptr = &DM_retreived_data[0]; //Give the address of the data to the pointer. g_pixel_count = 0; //Set the pixel count to zero at the start of every communication. go = true; //while (1) //Loop forever, continually checking for inputs. while (go) { //ReceiveCommand(OCE_retreived_data_ptr); //Get a command from the receive FIFO from OCELOT. OCE_retreived_data[0] = 0x0003; if (OCE_retreived_data[0] == 0x0003) //Check if the start command was just sent. { //SendCommand(OCE_retreived_data_ptr); //Send the retreived command to OSIRIS. GetImage(DM_retreived_data_ptr); //Get the image back from the DM and send to OCELOT. go = false; } else { EchoCommand(OCE_retreived_data_ptr); //Send the received command back to OCELOT to confirm. SendCommand(OCE_retreived_data_ptr); //Send the retreived command to OSIRIS. } } } /********************************************************************************************** void ReceiveCommand(): This functions gets the data from the receive FIFO sent from OCELOT. **********************************************************************************************/ void ReceiveCommand(uint16_t* data_ptr) { SSIDataGet(SSI0_BASE, (uint32_t *)(data_ptr)); //Get a command from the receive FIFO if there is one. If there isn't it will wait for one. } /********************************************************************************************** void EchoCommand(): This functions echos the received command from OCELOT back to it. **********************************************************************************************/ void EchoCommand(uint16_t* echo_command) { uint32_t garbage[1]; //Variable to hold the extra data sent over SPI when OCE receives the echo. uint32_t* garbage_ptr; //Pointer to the garbage buffer. garbage_ptr = &garbage[0]; //Assign the pointer the buffer address. SSIDataPut(SSI0_BASE, echo_command[0]); //Put the echo'd command into the FIFO. GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_4, 0x10); //Send a flag high to tell OCELOT to expect data. while (SSIBusy(SSI0_BASE)); //Wait for the echo to be sent back. SSIDataGet(SSI0_BASE, garbage_ptr); //After sending the echo, get the garbage command out of the receive FIFO. } /********************************************************************************************** void SendCommand(): This functions sends the required commands to the DM to set it up to get the appropriate image. It sets the exposure time constant, the exposure exponent and the vector. **********************************************************************************************/ void SendCommand(uint16_t* data) { SSIDataPut(SSI1_BASE, data[0]); //Put the received command into the SSI FIFO to be sent out. TimerEnable(TIMER1_BASE, TIMER_B); //Enable (start) the synchronization timer. while(SSIBusy(SSI1_BASE)); //Wait for the SSI to send out the entire command before doing anything else. } /********************************************************************************************** void GetImage(): This functions receives data from OSIRIS and sends it to OCELOT one word at a time. **********************************************************************************************/ void GetImage(uint16_t* image) { TimerEnable(WTIMER0_BASE, TIMER_B); //Start the timeout timer in case full image is not received. //uDMAChannelEnable(14); //Enable channel 14 DMA to start receiving data. while(g_pixel_count < (144*1369)); //Get data until the expected number of pixels have been received. uDMAChannelDisable(14); TimerDisable(WTIMER0_BASE, TIMER_B); SSIDataPut(SSI0_BASE, 0x8000); GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_4, 0x10); //Send a flag high to tell the master to expect data. while (SSIBusy(SSI0_BASE)); //Wait for the SSI to finish sending. GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_4, 0x00); //Clear the flag sent to the master. SysCtlReset(); } /********************************************************************************************** void __irq TIMER1B_Handler(): This function is the interrupt handler that is called when the synchronization timer triggers. The job of this interrupt is to control the DA line going to the DM by dropping the line low in sync with the 2MHz clock. **********************************************************************************************/ void __irq TIMER1B_Handler(void) { TimerDisable(TIMER1_BASE, TIMER_B); //Disable the synchronization timer. TimerIntClear(TIMER1_BASE, TIMER_CAPB_MATCH); //Clear the interrupt flag for this timer. GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_3, 0x00); //Drop the GPIO pin that is DA to the DM low. TimerEnable(TIMER2_BASE, TIMER_A); //Enable the timer to count 16 edges before putting the line high again to end transmission. } /********************************************************************************************** void __irq TIMER2A_Handler(): This function is the interrupt handler that is called when the counter timer triggers. The job of this interrupt is to control the DA line going to the DM. **********************************************************************************************/ void __irq TIMER2A_Handler(void) { TimerDisable(TIMER2_BASE, TIMER_A); //Disable the timer to allow it to be reset. TimerIntClear(TIMER2_BASE, TIMER_CAPA_MATCH); //Clear the interrupt flag to allow it to trigger again. GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_3, 0x08); //Set the DA line going to OSIRIS high again. } /********************************************************************************************** void __irq TIMER1B_Handler(): This function is the interrupt handler that is called when the counter timer triggers. The job of this interrupt is to control the DA line going to the DM. **********************************************************************************************/ void __irq WTIMER0B_Handler(void) { uDMAChannelDisable(14); TimerDisable(WTIMER0_BASE, TIMER_B); //Disable the timer to allow it to be reset. TimerIntClear(WTIMER0_BASE, TIMER_CAPB_MATCH); //Clear the interrupt flag to allow it to trigger again. SSIDataPut(SSI0_BASE, 0x8100); //Send done transmission with timeout error. GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_4, 0x10); //Send a flag high to tell the master to expect data. while (SSIBusy(SSI0_BASE)); //Wait for the SSI to finish sending. GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_4, 0x00); //Clear the flag sent to the master. SysCtlReset(); } /********************************************************************************************** void uDMA_Error_Interrupt(): This function is the error interrupt handler for the uDMA. **********************************************************************************************/ void __irq UDMAERR_Handler(void) { uint32_t status; status = uDMAErrorStatusGet(); if (status) { uDMAErrorStatusClear(); uDMAChannelDisable(14); TimerDisable(TIMER1_BASE, TIMER_B); SSIDataPut(SSI0_BASE, 0x8200); GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_4, 0x10); SSIBusy(SSI0_BASE); GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_4, 0x00); SysCtlReset(); } } /********************************************************************************************** void __irq SSI3_Handler(): Is the interrupt handler for SSI3 whihc runs DMA Rx. **********************************************************************************************/ void __irq SSI3_Handler(void) { uint32_t status; uint32_t mode; uint16_t i; status = SSIIntStatus(SSI3_BASE, 1); SSIIntClear(SSI3_BASE, status); mode = uDMAChannelModeGet(14 | UDMA_PRI_SELECT); if (mode == UDMA_MODE_STOP) { for (i = 0; i < 8; i++) { SSIDataPut(SSI0_BASE, g_ROERxBufA[i]); g_pixel_count++; //Increment the pixel count. } GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_4, 0x10); //Send a flag high to tell the master to expect data. while (SSIBusy(SSI0_BASE)); //Wait for the SSI to finish sending. GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_4, 0x00); //Clear the flag sent to the master. uDMAChannelTransferSet(14 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *) (SSI3_BASE + SSI_O_DR), g_ROERxBufA, 4); } mode = uDMAChannelModeGet(14 | UDMA_ALT_SELECT); if (mode == UDMA_MODE_STOP) { for (i = 0; i < 8; i++) { SSIDataPut(SSI0_BASE, g_ROERxBufB[i]); g_pixel_count++; //Increment the pixel count. } GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_4, 0x10); //Send a flag high to tell the master to expect data. while (SSIBusy(SSI0_BASE)); //Wait for the SSI to finish sending. GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_4, 0x00); //Clear the flag sent to the master. uDMAChannelTransferSet(14 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *) (SSI3_BASE + SSI_O_DR), g_ROERxBufB, 4); } if(!uDMAChannelIsEnabled(14)) { uDMAChannelEnable(14); } }
For anyone interested, I was able to solve my problem. The posted code works properly. The reason it was not working for me was the order that my initialization functions were being called in. I was calling the Init_GPIO function last in my SystemInit call. Apparently, doing this reset other registers (like DMA initialization) unintentionally. TO solve this problem I called Init_GPIO right after InitializeClock and everything worked a lot better!
Barrett Taylor