AnsweredAssumed Answered


Question asked by miller.mike.003 on Mar 19, 2014
Latest reply on Apr 1, 2014 by miller.mike.003
Hello everyone.

Thank you for taking the time to read my post. And thank you for any suggestions.

I am trying to play a 20 kHz tone using the DAC of my STM32F0 based micro.

I created an array of 10 data points that give a general shape of a single cycle of a sine wave.  I want to grab this from memory and using the DMA controller place this data into the DAC data register.  Then using the timer (TIM6) clock this at 20kHz so I get the 20kHz tone.  The DMA controller then does this over and over again. I have attempted to do this but it is not working. Something is wrong.

I have broken my section of code into 3 sections that cover the timer, DAC, and DMA setup.  The driver files that I am using are:


I show my source code below and you can see how I have broken this down into the 3 sections.  Timer, DAC, and DMA configuration.

There are some parameters that I was not sure on and I have added these in as comments in the source. Looking over what I have put together does anything look out of place?

What type of interrupt handling should I be doing here? There doesn't seem to be anything that I need to service during an interrupt.

What is the best way to keep debugging this? How do I know I have the 20 kHz coming out of the timer as a standalone test?   How can I test the DMA controller without the other two pieces? And the DAC alone?  There are at least 3 pieces here working together that makes it more complication to test and debug.

    // Section 1 - Configure TIM6 to give me a 20 kHz time base.
     // Clock looks to be 48MHz? Need to check this.
     // Step 1 - Enable TIM clock using  RCC_APBxPeriphClockCmd(RCC_APBxPeriph_TIMx, ENABLE) function.
    // Notes:  I this APB1?   
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
    // Step 2 - Fill the TIM_TimeBaseInitStruct with the desired parameters.
    // Notes:  
    // Calculationf follwing this forumula to try and great a refresh rate of 20 kHz
    // Update_event = TIM_CLK/((PSC + 1)*(ARR + 1)*(RCR + 1))
    // TIM_CLK = timer clock input  
    // PSC = 16-bit prescaler register
    // ARR = 16/32-bit Autoreload register
    // RCR = 16-bit repetition counter
    TIM_TimeBaseInitStructx.TIM_Prescaler = 599;
    TIM_TimeBaseInitStructx.TIM_CounterMode = 1;                        // Not sure what to put here.
    TIM_TimeBaseInitStructx.TIM_Period = 1;                             // ARR value?
    TIM_TimeBaseInitStructx.TIM_ClockDivision = 1;                      // NOt usre on this.                
    TIM_TimeBaseInitStructx.TIM_RepetitionCounter = 1;                  // Not usre on this.
    // Step 3 - Call TIM_TimeBaseInit(TIMx, &TIM_TimeBaseInitStruct) to configure
    //          the Time Base unit with the corresponding configuration.
    // Notes:  
    TIM_TimeBaseInit(TIM6, &TIM_TimeBaseInitStructx);
    // Step 4 -  Enable the NVIC if you need to generate the update interrupt.
    // Notes:  Not sure what to do here.
     // Step 5 - Enable the corresponding interrupt using the function
     //         TIM_ITConfig(TIMx, TIM_IT_Update).
     // Notes:  Not sure if I should be enablign the interrupt here.
      TIM_ITConfig(TIM6,TIM_IT_Update, ENABLE);
     // Step 6 - Call the TIM_Cmd(ENABLE) function to enable the TIM counter.
    // Notes:  Should be running now?
     TIM_Cmd(TIM6, ENABLE);
    //  Section 2 - Configure the DAC  
    /* Enable the clock to get write access for DAC register */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);  
   /* Configure DAC_OUT1 ON PA4 for analog mode.. */
    GPIO_InitStructurex.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructurex.GPIO_Mode = GPIO_Mode_AN;
    GPIO_InitStructurex.GPIO_OType = GPIO_OType_PP;             // QUESTION ON THIS.
    GPIO_InitStructurex.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStructurex.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructurex);
    // GPIO_PORT[Led]->BSRR = GPIO_PIN[Led];
    // GPIOB->BSRR = GPIO_Pin_4;
     /* Conversion is automatic once the DAC1_DHRxxxx register has been loaded, and not by external trigger */
     // DAC_Initstrucx.DAC_Trigger =     DAC_Trigger_None;               // No external trigger the happen auto when DHR updated.   
    /* TIM6 TRGO selected as external conversion trigger for DAC channel */   
      DAC_Initstrucx.DAC_Trigger =     DAC_Trigger_T6_TRGO;
     DAC_Initstrucx.DAC_OutputBuffer = DAC_OutputBuffer_Disable;
     DAC_Init(DAC_Channel_1, &DAC_Initstrucx);
     DAC_Cmd(DAC_Channel_1, ENABLE);
    // Section 3 - Configure the DMA controller
    // Step 1 - Enable the DMA controller clock
    // Notes:  Seems to be correct.
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE);  
    // Step 2 - Enable and configure the peripheral to be connected to the DMA channel
    // Notes:  DAC_Channel1 looks to be connected to Channel 3 of the DMA controller.
    //         Is this the DMA EN1 bit in the DAC control register (DAC_CR)?         
    //         Completed in the "Configure the DAC" section?  
    // Step 3 - For a given Channel, program the Source and Destination addresses,
    //          the transfer Direction, the Buffer Size, the Peripheral and Memory
    //          Incrementation mode and Data Size, the Circular or Normal mode,
    //          the channel transfer Priority and the Memory-to-Memory transfer
    //          mode (if needed) using the DMA_Init() function.
    // Notes:
    //  Need to double check some of the settings.
    DMA_Initstrucx.DMA_MemoryBaseAddr = *wave_data;                     //  should be the starting address of array. Can't view in debugger.
    DMA_Initstrucx.DMA_PeripheralBaseAddr = 0x40007410;                 // Address offset is 0x10. Base address? 0x4000 7400    
    DMA_Initstrucx.DMA_DIR = 1;                                         // data direction read from memory.          
    DMA_Initstrucx.DMA_BufferSize = 10;                                 // buffer size 10 bytes.  
    DMA_Initstrucx.DMA_MemoryInc = 1;                                   // Increment memory address of source wave_data[x]
    DMA_Initstrucx.DMA_PeripheralDataSize = 0;                          // 00: 8-bits
    DMA_Initstrucx.DMA_MemoryDataSize = 0;                              // 00: 8-bits  
    DMA_Initstrucx.DMA_Priority =   2;                                  //  b10 , is 2 (high priority)
    DMA_Initstrucx.DMA_Mode = 1;                                        // Circular mode enabled?
    DMA_Init(DMA1_Channel3, &DMA_Initstrucx);                           // Is this correct? Channel 3?
    // Step 4 - Enable the NVIC and the corresponding interrupt(s) using the function
    //           DMA_ITConfig() if you need to use DMA interrupts.
    // Notes:
    //   Enabling: Transfer complete interrupt.
    DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE);        
    // Step 5 - Enable the DMA channel using the DMA_Cmd() function.
    // Notes:
    DMA_Cmd(DMA1_Channel3,ENABLE );
    // Step 6 -  Activate the needed channel Request using PPP_DMACmd() function for
    //           any PPP peripheral except internal SRAM and FLASH (ie. SPI, USART ...)
    //           The function allowing this operation is provided in each PPP peripheral
    //           driver (ie. SPI_DMACmd for SPI peripheral).
    // Notes:
    // The DAC channel1 is mapped on DMA1 channel3 which must be already configured.
    // Is this happening?

    DAC_DMACmd(DAC_Channel_1, ENABLE);
    // Step 7 - Optionally, you can configure the number of data to be transferred
    //          when the channel is disabled (ie. after each Transfer Complete event
    //          or when a Transfer Error occurs) using the function DMA_SetCurrDataCounter().
    //          And you can get the number of remaining data to be transferred using
    //          the function DMA_GetCurrDataCounter() at run time (when the DMA channel is
    //          enabled and running).
    // Notes:
    //  Nothing need to implement for this step?
    // Step 8 - (#) To control DMA events you can use one of the following two methods:
    //          (##) Check on DMA channel flags using the function DMA_GetFlagStatus().
    //          (##) Use DMA interrupts through the function DMA_ITConfig() at initialization
    //          phase and DMA_GetITStatus() function into interrupt routines in
    //          communication phase.
    //          After checking on a flag you should clear it using DMA_ClearFlag()
    //          function. And after checking on an interrupt event you should
    //          clear it using DMA_ClearITPendingBit() function.
    // Notes:
Thank you.