Hi,
I am currently using the TIVA device for an audio playback functionality, using a PWM signal, where the audio files are stored in external flash on a booster pack. Up to this point I have configured the SSI module to read data from flash and play it back successfully. However, moving forward I would like to utilize the uDMA to handle the transfer of data from flash to the buffer and then to the timer for PWM output.
The question that this is raising relates to the CS signal. It must be low for the duration of data transfer and on the tm4c123 device this can only be done manually by toggling the GPIO pin. I have included the two files being used below. Again, I am hoping to get some insight, or potentially an example, of how to implement the uDMA for this function.
Thanks,
Garrett
*timers.c*
#define SSI_CLK GPIO_PIN_4 #define SSI_CS GPIO_PIN_5 #define SSI_RX GPIO_PIN_6 #define SSI_TX GPIO_PIN_7 #define endAddr 208065 int d=0, c=10, b=0; int strAddr = 100; uint8_t Buffer[10]; unsigned long dutyCycle; unsigned short sine[32] = { 0x0000, 0x7D39, 0x7FD8, 0x5964, 0x4073, 0x7FFD, 0x0647, 0xBA33, 0x0000, 0x45CD, 0xF9B9, 0x8003, 0xBF8D, 0xA69C, 0x8028, 0x82C7, 0x0000, 0x7D39, 0x7FD8, 0x5964, 0x4073, 0x7FFD, 0x0647, 0xBA33, 0x0000, 0x45CD, 0xF9B9, 0x8003, 0xBF8D, 0xA69C, 0x8028, 0x82C7}; unsigned short sine1[32] = { 0x0000, 0x007A, 0x007D, 0x0057, 0x003F, 0x007D, 0x06, 0x00B6, 0x0000, 0x0044, 0x00F4, 0x007D, 0x00BB, 0x00A3, 0x007D, 0x0080, 0x0000, 0x007A, 0x007D, 0x0057, 0x003F, 0x007D, 0x0006, 0x00B6, 0x0000, 0x0044, 0x00F4, 0x007D, 0x00BB, 0x00A3, 0x007D, 0x0080}; //***************************************************************************** // //! \addtogroup example_list //! <h1>Timer (timers)</h1> //! //! This example application demonstrates the use of the timers to generate //! periodic interrupts. One timer is set up to interrupt once per second and //! the other to interrupt twice per second; each interrupt handler will toggle //! its own indicator on the display. // //***************************************************************************** //uint32_t g_ui32Flags; //***************************************************************************** // // The error routine that is called if the driver library encounters an error. // //***************************************************************************** #ifdef DEBUG void __error__(char *pcFilename, uint32_t ui32Line) { } #endif //***************************************************************************** // // The interrupt handler for Timer 3 & SSI // //***************************************************************************** void Timer3IntHandler(void) { ROM_TimerIntClear(TIMER3_BASE, TIMER_CAPB_EVENT); if(c==10){ SPIFlashRead(SSI2_BASE, strAddr, &Buffer, 10); if (strAddr >= endAddr) strAddr = 100; else strAddr=strAddr+10; c=0; } ROM_TimerMatchSet(TIMER3_BASE, TIMER_A, Buffer[c]); c++; /* ROM_TimerIntClear(TIMER3_BASE, TIMER_CAPB_EVENT); ROM_TimerMatchSet(TIMER3_BASE, TIMER_A, sine1[d]); d++; if(d == 32) d = 0; */ } //***************************************************************************** // // This example application demonstrates the use of the timers to generate // periodic interrupts. // //***************************************************************************** int main(void) { unsigned long ulPeriodA; unsigned long ulPeriodB; unsigned long sysClock; uint8_t manID = 0; uint16_t devID = 0; ulPeriodA = 255; //max 65535 with 16-bit timer sets to 16kHz (80MHz/5000) dutyCycle = 1; //50% duty cycle ulPeriodB = 4080; // (66.66M / 16k) //Enable Peripherals used ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB); ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC); ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER3); ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI2); ///////////////////////////////// Pin Configurations ////////////////////////////////////// //Timer 3A ROM_GPIOPinConfigure(GPIO_PB2_T3CCP0); //configure function of GPIO Pin (_P#_fucntion) ROM_GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_2); //configures pin for use by timer //Audio Amp ROM_GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE, GPIO_PIN_3); //Using AMP CTL* on board ROM_GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_3, 0); //Must move resistor to use AMP CTL (PA4) //SSI ROM_GPIOPinTypeSSI(GPIO_PORTB_BASE, SSI_CLK | SSI_RX | SSI_TX); //configures pins for SSI ROM_GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_3); //manual CS ROM_GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, 255); // manual Chip Select ROM_GPIOPinConfigure(GPIO_PB4_SSI2CLK); // ROM_GPIOPinConfigure(GPIO_PB5_SSI2FSS); ROM_GPIOPinConfigure(GPIO_PB6_SSI2RX); ROM_GPIOPinConfigure(GPIO_PB7_SSI2TX); ////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////// Timer Configurations /////////////////////////////////// //66.66MHz system clock ROM_SysCtlClockSet(SYSCTL_SYSDIV_3 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); // Configure the 16-bit PWM Timer ROM_TimerConfigure(TIMER3_BASE, (TIMER_CFG_SPLIT_PAIR|TIMER_CFG_A_PWM|TIMER_CFG_B_PWM)); //Timer 3A Load, Match, Control ROM_TimerLoadSet(TIMER3_BASE, TIMER_A, ulPeriodA); //count down from 'load value' ulPeriod ROM_TimerMatchSet(TIMER3_BASE, TIMER_A, dutyCycle); //PWM high until counter hits match value ROM_TimerControlLevel(TIMER3_BASE, TIMER_A, true); //Controls output level ==> true = active low output //Timer 3B Load, Match, Control ROM_TimerLoadSet(TIMER3_BASE, TIMER_B, ulPeriodB); //count down from 'load value' ulPeriod ROM_TimerMatchSet(TIMER3_BASE, TIMER_B, dutyCycle); //PWM high until counter hits match value ROM_TimerControlLevel(TIMER3_BASE, TIMER_B, true); //Controls output level ==> true = active low output //Counter Interrupt TimerUpdateMode(TIMER3_BASE, TIMER_B, TIMER_UP_LOAD_TIMEOUT | TIMER_UP_MATCH_TIMEOUT); ROM_TimerIntEnable(TIMER3_BASE, TIMER_CAPB_EVENT); // Enable the timers. ROM_TimerEnable(TIMER3_BASE, TIMER_A); ROM_TimerEnable(TIMER3_BASE, TIMER_B); // Enable processor interrupts. ROM_IntMasterEnable(); // Setup the interrupts for the timer timeouts. ROM_IntEnable(INT_TIMER3B); ///////////////////////////////// SSI Configuration ////////////////////////////////////// sysClock = ROM_SysCtlClockGet(); // SSIConfigSetExpClk(SSI2_BASE, sysClock, SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, 2000000, 8); // SSIAdvModeSet(SSI2_BASE, SSI_ADV_MODE_READ_WRITE); //mode where data is written/read from the slave // SSIAdvFrameHoldEnable(SSI2_BASE); //enables frame hold feature // SSIEnable(SSI2_BASE); //enable SPI module SPIFlashInit(SSI2_BASE, sysClock, 4000000); // SPIFlashReadID(SSI2_BASE, &manID, &devID); ///////////////////////////////////////////////////////////////////////////////////////// //uint32_t data[4]; while(1) // Loop forever while the timers run. {
} }
*spi_flash.c*
void SPIFlashInit(uint32_t ui32Base, uint32_t ui32Clock, uint32_t ui32BitRate) { // // Configure the SPI module. // MAP_SSIConfigSetExpClk(ui32Base, ui32Clock, SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, ui32BitRate, 8); // // Enable the advanced mode of operation, defaulting to read/write mode. // MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE); // // Enable the frame hold feature. // // MAP_SSIAdvFrameHoldEnable(ui32Base); // // Enable the SPI module. // MAP_SSIEnable(ui32Base); } void SPIFlashRead(uint32_t ui32Base, uint32_t ui32Addr, uint8_t *pui8Data, uint32_t ui32Count) { uint32_t ui32Trash; uint8_t c=0; // // Drain any residual data from the receive FIFO. // while(MAP_SSIDataGetNonBlocking(ui32Base, &ui32Trash) != 0) { } ROM_GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, 0); // manual Chip Select//manual C //SysCtlDelay(10); // Set the SSI module into write-only mode. MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_WRITE); // // Send the read command. // MAP_SSIDataPut(ui32Base, CMD_READ); ui32Addr=ui32Addr-4; // // Send the address of the first byte to read // MAP_SSIDataPut(ui32Base, (ui32Addr >> 16) & 0xff); MAP_SSIDataPut(ui32Base, (ui32Addr >> 8) & 0xff); MAP_SSIDataPut(ui32Base, ui32Addr & 0xff); // // Set the SSI module into read/write mode. In this mode, dummy writes are // required in order to make the transfer occur; the SPI flash will ignore // the data. // MAP_SSIAdvModeSet(ui32Base, SSI_ADV_MODE_READ_WRITE); // // See if there is a single byte to be read. // for(c = 0; c <= 4; c++) { MAP_SSIDataPut(ui32Base, 241); MAP_SSIDataGet(ui32Base, &ui32Addr); } if(ui32Count == 1) { // // Perform a single dummy write, marking it as the end of the frame. // MAP_SSIDataPut(ui32Base, 241); } else { // // Perform a dummy write to prime the loop. // MAP_SSIDataPut(ui32Base, 241); // // Loop while there is more than one byte left to be read. // while(--ui32Count != 1) { // // Perform a dummy write to keep the transmit FIFO from going // empty. // MAP_SSIDataPut(ui32Base, 241); //- // Read the next data byte from the receive FIFO and place it into // the data buffer. // MAP_SSIDataGet(ui32Base, &ui32Addr); *pui8Data++ = ui32Addr & 0xff; } // // Perform the final dummy write, marking it as the end of the frame. // MAP_SSIDataPut(ui32Base, 241); // // Read the next data byte from the receive FIFO and place it into the // data buffer. // MAP_SSIDataGet(ui32Base, &ui32Addr); *pui8Data++ = ui32Addr & 0xff; } // // Read the final data byte from the receive FIFO and place it into the // data buffer. // MAP_SSIDataGet(ui32Base, &ui32Addr); *pui8Data++ = ui32Addr & 0xff; while (SSIBusy(SSI2_BASE)){} ROM_GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, 255); // manual Chip Select }