Usar interrupción STM32 con FreeRTOS

Estoy confundido al usar interrupciones definidas por HAL con FreeRTOS. Estoy tratando de implementar "6.3 Procesamiento de interrupciones diferidas" en la guía de FreeRTOS, pero no sé cómo hacerlo.

El problema es cómo usar los periféricos del STM32 con FreeRTOS. ¿Debería darle un semáforo a otra tarea (que lea y trate los valores) dentro del irqn "ADC_IRQHandler()" para el ADC o la devolución de llamada "HAL_ADC_ConvCpltCallback()" para el ADC o simplemente lea los valores con las funciones HAL y los trate con ¿Tareas de FreeRTOS (esta funciona, pero no parece usar el poder de RTOS)?

El manual del que hablo: 6.3 Procesamiento de interrupciones diferidas (PDF)

En primer lugar, no utilice bucles "nop" para crear retrasos. Utilice la macro vTaskDelay . También debe verificar dónde realmente no se ejecuta su código, si el problema está en los comandos freertos o ADC.
¿Qué tipo de guía?
el código del ADC funciona solo y crea algo así como el PWM, el brillo del cambio de led cambiando el potenciómetro. Y para la guía: freertos.org/Documentation/…

Respuestas (2)

Soy consciente de que esta es una pregunta antigua, pero también tuve problemas con eso. El nuevo FreeRTOS para STM32 recomienda utilizar señales como una alternativa más rápida y sencilla a los semáforos, especialmente para la sincronización de interrupciones con una tarea.

Aquí hay un código de ejemplo para un botón que elimina el rebote. Es básicamente lo mismo que tratar con el hardware ADC: en la rutina de interrupción, envíe una señal a la tarea con osSignalSet(). La señal se puede enviar incluso repetidamente, independientemente de si la tarea ya la leyó.

/**
 * @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);
    }
}

Dentro de la tarea, simplemente espera por siempre la señal con 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);
    }
}

En primer lugar, el semáforo se puede dar en "ADC_IRQHandler()" o "HAL_ADC_ConvCpltCallback()" (ADC) agregando:

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

Al final de la función, parece que "HAL_ADC_ConvCpltCallback" también es un ISR cuando uso xSemaphoreGive( xBinarySemaphore);. Con él, el programa se bloquea.

También es importante establecer la prioridad del IRQHandler numéricamente más alto que configMAX_SYSCALL_INTERRUPT_PRIORITY, en mi caso '80', agregando (5 por ejemplo). HAL_NVIC_SetPriority(ADC_IRQn, 5, 0);, prioridad 5 ? 15 en mi caso. Puede consultar esto para obtener más información: http://www.freertos.org/RTOS-Cortex-M3-M4.html .

De todos modos, este código funciona y toma el inicio de ADC de https://visualgdb.com/tutorials/arm/stm32/adc/ .

Código

#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");
    }
}