5
\$\begingroup\$

I'm confused in using HAL-defined interrupts with FreeRTOS. I'm trying to implement "6.3 Deferred Interrupt Processing" in the guide of FreeRTOS, but I don't know how to do it.

The problem is how to use the peripherals of the STM32 with FreeRTOS. Should I give a semaphore to another task (which read and treat the values) within the irqn "ADC_IRQHandler()" for the ADC or the callback "HAL_ADC_ConvCpltCallback()" for the ADC or just read values with the HAL functions and treat them with FreeRTOS tasks (this one works, but it does not seem to use the RTOS power)?

The manual I talk about: 6.3 Deferred Interrupt Processing (PDF)

\$\endgroup\$
3
  • 1
    \$\begingroup\$ First of all, don't use "nop" loops to create delays. Use vTaskDelay macro. Also you should check where actually your code doesn't run, if the problem is in the freertos or in the ADC commands. \$\endgroup\$
    – olltsu
    Mar 6, 2017 at 5:56
  • \$\begingroup\$ What kind of guide? \$\endgroup\$ Mar 6, 2017 at 7:54
  • \$\begingroup\$ the code of the ADC work alone and create something like the PWM the brightness of the led change by changing the potentiometer. And for the guide: freertos.org/Documentation/… \$\endgroup\$
    – Ramo
    Mar 6, 2017 at 12:42

2 Answers 2

4
\$\begingroup\$

I am aware this is an old question, but I had issues with that as well. The new FreeRTOS for STM32 recommend to use signals as faster and simpler alternative to Semaphores, especially for the interrupt synchronization with a task.

Here is an example code for a button debouncing. It is basically a same thing like dealing with the ADC hardware: in the interrupt routine, send a signal to the task with the osSignalSet(). Signal can be sent even repeatedly, regardless if the task read it already.

/**
 * @brief  EXTI line detection callback.
 * @param GPIO_Pin Specifies the port pin connected to corresponding EXTI line.
 * @retval None
 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == SWITCH_IN_Pin)
    {
        if (button.flag_debouncing == 0)
            osSignalSet(ButtonHandle, USER_BUTTON_DETECTED);
    }
}

Inside the task, just wait forever for the signal with the osSignalWait():

/**
 * @brief Function implementing the Button thread.
 * @param argument: Not used
 * @retval None
 */
void buttonTask(void const *argument)
{
    /*
     * Initialize
     */
    button.value = BUTTON_IS_RELEASED;
    button.flag_debouncing = 0;

    /* Infinite loop */
    for (;;)
    {
        osSignalWait(USER_BUTTON_DETECTED, osWaitForever);
        button.value = BUTTON_IS_PRESSED;
        button.flag_debouncing = 1;

        /*
         * Debounce loop
         * Button was pressed. Wait for its release.
         * Debounce by checking every 10msec until released level is seen unchanged (5 times)
         */
        for (button.debounce_ctr = STABLE_LEVEL_COUNTER; button.debounce_ctr > 0; button.debounce_ctr--)
        {
            osDelay(USER_BUTTON_DEBOUNCE_STEP__MSEC);

            /*
             * Time 10 msec has passed. If the button is still pressed, restart the debouncing
             */
            if (HAL_GPIO_ReadPin(SWITCH_IN_GPIO_Port, SWITCH_IN_Pin) == BUTTON_IS_PRESSED)
            {
                button.debounce_ctr = RELOAD_THE_COUNTER; // not released = not stable... restart the counter and wait more
            }
        }

        /*
         * Button is debounced
         */
        button.value = BUTTON_IS_RELEASED;
        button.flag_debouncing = 0;

        osDelay(1);
    }
}
\$\endgroup\$
2
\$\begingroup\$

First of all, the semaphore can be given in "ADC_IRQHandler()" or "HAL_ADC_ConvCpltCallback()" (ADC) by adding:

BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(xBinarySemaphore, &xHigherPriorityTaskWoken);
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);

At the end of the function it seems that "HAL_ADC_ConvCpltCallback" is also an ISR when I use xSemaphoreGive( xBinarySemaphore);. With it the program crashes.

It is also important to set the priority of the IRQHandler numerically higher than configMAX_SYSCALL_INTERRUPT_PRIORITY, in my case '80', by adding (5 for example). HAL_NVIC_SetPriority(ADC_IRQn, 5, 0);, priority 5 ? 15 in my case. You can check this for more information: http://www.freertos.org/RTOS-Cortex-M3-M4.html.

Anyway this code is working and takes the ADC init from https://visualgdb.com/tutorials/arm/stm32/adc/.

Code

#include "stm32f4xx.h"
#include "stm32f4_discovery.h"


#include <stdlib.h>
#include <string.h>

#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "semphr.h"


SemaphoreHandle_t xBinarySemaphore=NULL;
ADC_HandleTypeDef g_AdcHandle;
uint32_t g_ADCValue=0;

void SystemClock_Config(void);
void ConfigureADC();
static void ledL( void*); // Task to change the brightness of the LED 12 of GPIOD when changing the value of potentiometer
static void vHandlerTask( void*); // Task to toggle GPIOD 13 every time a conversion is achieved and sleep for 300 ms
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef*);
void ADC_IRQHandler();

int main(void){

    HAL_Init();
    SystemClock_Config();
    ConfigureADC();
    HAL_NVIC_SetPriority(ADC_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(ADC_IRQn);

    GPIO_InitTypeDef GPIO_InitStructure;
    __GPIOD_CLK_ENABLE();
    GPIO_InitStructure.Pin = GPIO_PIN_12|GPIO_PIN_13;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
    GPIO_InitStructure.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOD, &GPIO_InitStructure);


    vSemaphoreCreateBinary(xBinarySemaphore);

    if( xBinarySemaphore != NULL ){

        xTaskCreate(vHandlerTask, "Task 1", 1000, NULL, 1, NULL);
        xTaskCreate(ledL, "Task 2", 1000, NULL, 1, NULL);
        vTaskStartScheduler();
    }

    for(;;){}
}

//ISR
void ADC_IRQHandler(){
    HAL_ADC_IRQHandler(&g_AdcHandle);
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    xSemaphoreGiveFromISR( xBinarySemaphore, &xHigherPriorityTaskWoken );
    portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
}

//CALLBACK CALLED WHEN CONVERTION COMPLETE
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle){

    g_ADCValue = HAL_ADC_GetValue(AdcHandle);
}

static void vHandlerTask( void *pvParameters ){

    HAL_ADC_Start_IT(&g_AdcHandle);

    for( ;; ){
        xSemaphoreTake( xBinarySemaphore, portMAX_DELAY );
        HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_13);
        vTaskDelay(pdMS_TO_TICKS(200UL));

    }
}

static void ledL( void *pvParameters ){
    for (;;)
    {
        int onTime = g_ADCValue;
        int offTime = 4096 - onTime;
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET);
        for (int i = 0; i < onTime; i++)
            asm("nop");

        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_RESET);
        for (int i = 0; i < offTime; i++)
            asm("nop");
    }
}

void SystemClock_Config(void)
{
    RCC_ClkInitTypeDef RCC_ClkInitStruct;
    RCC_OscInitTypeDef RCC_OscInitStruct;

    __HAL_RCC_PWR_CLK_ENABLE();
    __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);

    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLM = 8;
    RCC_OscInitStruct.PLL.PLLN = 288;
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
    RCC_OscInitStruct.PLL.PLLQ = 6;
    HAL_RCC_OscConfig(&RCC_OscInitStruct);

    RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
    HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    SystemCoreClockUpdate();

    if (HAL_GetREVID() == 0x1001)
        __HAL_FLASH_PREFETCH_BUFFER_ENABLE();
}

void ConfigureADC()
{
    GPIO_InitTypeDef gpioInit;

    __GPIOC_CLK_ENABLE();
    __ADC1_CLK_ENABLE();

    gpioInit.Pin = GPIO_PIN_1;
    gpioInit.Mode = GPIO_MODE_ANALOG;
    gpioInit.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOC, &gpioInit);

    HAL_NVIC_SetPriority(ADC_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(ADC_IRQn);

    ADC_ChannelConfTypeDef adcChannel;

    g_AdcHandle.Instance = ADC1;

    g_AdcHandle.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV2;
    g_AdcHandle.Init.Resolution = ADC_RESOLUTION_12B;
    g_AdcHandle.Init.ScanConvMode = DISABLE;
    g_AdcHandle.Init.ContinuousConvMode = ENABLE;
    g_AdcHandle.Init.DiscontinuousConvMode = DISABLE;
    g_AdcHandle.Init.NbrOfDiscConversion = 0;
    g_AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
    g_AdcHandle.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
    g_AdcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    g_AdcHandle.Init.NbrOfConversion = 1;
    g_AdcHandle.Init.DMAContinuousRequests = ENABLE;
    g_AdcHandle.Init.EOCSelection = DISABLE;

    HAL_ADC_Init(&g_AdcHandle);

    adcChannel.Channel = ADC_CHANNEL_11;
    adcChannel.Rank = 1;
    adcChannel.SamplingTime = ADC_SAMPLETIME_480CYCLES;
    adcChannel.Offset = 0;

    if (HAL_ADC_ConfigChannel(&g_AdcHandle, &adcChannel) != HAL_OK)
    {
        asm("bkpt 255");
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.