cancel
Showing results for 
Search instead for 
Did you mean: 

Potential DAC and FreeRTOS conflict?

SKILA
Associate II

Hello,

I am using STM32F417 to control a sounder among many other functions. In the firmware, the DAC is controlling the sounder which works fine when tested alone. However, the moment the FreeRTOS is enabled, the DAC stops working. What I am suspecting is that the Timer I am using to trigger the DAC (TIM6) is somehow deprioritized or disabled as I can hear the sounder clicking and thats because only the first sample of the waveform is actually sent to the sounder.

These are the functions I use to control the sounder

void set_sounder_freq_2khz(uint8_t volume)
{
    // configure DAC to generate amplitude controlled square wave for the buzzer
    HAL_TIM_Base_Stop(&DAC_HTIM);
    HAL_DAC_Stop_DMA(&DAC_HDAC, DAC_CHANNEL);
 
    // DAC sampling frequency is set to 8kHz
    memset(dac_buff, 0, sizeof(dac_buff));
    for(uint32_t i=0; i < (sizeof(dac_buff)/sizeof(uint16_t)); i+=4) {
        dac_buff[i] = (uint16_t)volume;
        dac_buff[i+1] = (uint16_t)volume;
    }
}
 
void sounder_on(bool on)
{
//	DAC_ChannelConfTypeDef sConfig;
    if (on == true) {
        //HAL_TIM_PWM_Start(&SOUNDER_HTIM, SOUNDER_CHANNEL);
        HAL_DAC_Start_DMA(  &DAC_HDAC,
                            DAC_CHANNEL,
                            (uint32_t *)dac_buff,
                            sizeof(dac_buff)/sizeof(uint16_t),
                            DAC_ALIGN_8B_R);
        HAL_TIM_Base_Start(&DAC_HTIM);	
        //SOUNDER_EN_HIGH;
    }
    else {
        //SOUNDER_EN_LOW;
        HAL_TIM_Base_Stop(&DAC_HTIM);
        HAL_DAC_Stop_DMA(&DAC_HDAC, DAC_CHANNEL);
    }
}

This is how they are called in my test.

int main(void)
{
  /* USER CODE BEGIN 1 */
 
  // Set up the IRQ vectors
  extern uint32_t Image$$ER_APPLICATION$$Base;
  SCB->VTOR = (uint32_t) &Image$$ER_APPLICATION$$Base;
  __DSB();
 
 
  /* USER CODE END 1 */
  
 
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* USER CODE BEGIN Init */
 
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  MX_CRC_Init();
  MX_CRYP_Init();
  MX_HASH_Init();
  MX_RNG_Init();
  MX_ADC1_Init();
  MX_I2C1_Init();
  MX_SPI2_Init();
  MX_SPI3_Init();
  MX_TIM2_Init();
  MX_TIM3_Init();
  MX_TIM4_Init();
  MX_TIM8_Init();
  MX_USART2_UART_Init();
  MX_TIM6_Init();
  MX_RTC_Init();
	MX_DAC_Init();
  //MX_ADC3_Init();
	#ifdef HAL_CAN_MODULE_ENABLED
  MX_CAN1_Init();
	#endif
  /* USER CODE BEGIN 2 */
 
  /* USER CODE END 2 */
 
  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */
 
  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */
 
  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */
 
  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
 
  /* USER CODE END RTOS_QUEUES */
 
  /* Create the thread(s) */
  /* definition and creation of defaultTask */
  osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);
  defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);
 
  /* USER CODE BEGIN RTOS_THREADS */
 
	set_sounder_freq_2khz(20);
	sounder_on(true);
	HAL_Delay(1000);
	sounder_on(false);
 
  /* USER CODE END RTOS_THREADS */
 
  /* Start scheduler */
  osKernelStart();
  
  /* We should never get here as control is now taken by the scheduler */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

Note that if I remove the following initiliaization of the FreeRTOS default task, the sounder works fine

osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);
  defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

I even tried to have an empty defaultTask and the sounder still doesn't work.

Any suggestions would be greatly appreciated!

Many thanks

Samer

4 REPLIES 4
alister
Lead

You'd coded this like you might for bare-metal.

	set_sounder_freq_2khz(20);
	sounder_on(true);
	HAL_Delay(1000);
	sounder_on(false);

osKernelStart is the CMSIS-RTOS for FreeRTOS' vTaskStartScheduler.

Before vTaskStartScheduler, interrupts are disabled. So HAL_Delay won't return.

Google "FreeRTOS interrupts before vTaskStartScheduler" without the quotes for pages discussing their rationale. This one describes it well: https://www.freertos.org/FreeRTOS_Support_Forum_Archive/June_2018/freertos_Use_of_FreeRTOS_API_before_call_of_vTaskStartScheduler_2fcc8d8cj.html.

I'd expect your intention is to start and stop the sounder and adjust its volume under external stimulus of your app.

First step, as experiment, wrap this code in your StartDefaultTask:

void StartDefaultTask(void const * argument)
{
  set_sounder_freq_2khz(20);
  while (1)
  {
    sounder_on(true);
    osDelay(1000);
    sounder_on(false);
    osDelay(1000);
  }
}

It initializes your set_sounder_freq_2khz and starts and cycles the sounder every second.

Next step, read lots about FreeRTOS and RTOS apps.

Then, decide how you'll control your sounder per your app's requirements. E.g. using xQueueCreate and define messages to start/stop and control the volume. Don't be afraid to use FreeRTOS API direct. Move it from the default task to one dedicated to the control of your sounder. Does the sounder play sequences? You might like to table those and state-machine the sounder's task so it takes care of starting/sequencing/stopping without burdening the rest of your app with those details. A good practice is to put the queue behind an call-based API of your sounder task's module. So you app can call a function to turn the sound on/off or change its volume and the function constructs and sends the message to its "buried" sounder task.

SKILA
Associate II

Alister,

Thank you for taking the time and replying to my post.

Even though I have presented the sounder code, as you have put it, in bare-metal form. It is not how it is actually called in the firmware. I have multiple FreeRTOS tasks running and the sounder functions is called through these tasks. I just took it out to the main function to be able to debug it independently. It acts in the same way I have described in my original post even when called by a task.

Ok, post the way you're using your functions from FreeRTOS. Otherwise I'm guessing, and work's busy so don't want to do that.

My earlier post is correct for the code you've posted.You should read those links. If you don't care, then add these lines immediately before you call of set_sounder_freq_2khz. BUT THIS ONLY APPLIES THE CODE YOU'VE POSTED, AND YOU REALLY SHOULDN'T EVER DO THIS. It's because osThreadCreate before osKernelStart is raising the interrupt priority. Read those links!

  __enable_interrupt();
  __set_BASEPRI(0);

Check too my code snippet in my earlier post and especially how it replaces HAL_Delay with osDelay. HAL_Delay reads the tick count and then loops until the ticks have lapsed and relies on the tick interrupt to increment its tick count. That time is wasted. An RTOS should give that time to other tasks. BTW same applies bare-metal. No bare-metal code I write uses HAL_Delay. It's really just there for simple throw-away stuff or where your app doesn't have any serious amount of work to do.

It would be more correct and universal to just call:

taskENABLE_INTERRUPTS();