Verwenden Sie STM32-Interrupt mit FreeRTOS

Ich bin verwirrt bei der Verwendung von HAL-definierten Interrupts mit FreeRTOS. Ich versuche, "6.3 Deferred Interrupt Processing" in der Anleitung von FreeRTOS zu implementieren, aber ich weiß nicht, wie das geht.

Das Problem ist, wie man die Peripheriegeräte des STM32 mit FreeRTOS verwendet. Soll ich einer anderen Task (die die Werte liest und behandelt) innerhalb des irqn "ADC_IRQHandler()" für den ADC oder dem Callback "HAL_ADC_ConvCpltCallback()" für den ADC ein Semaphor geben oder einfach Werte mit den HAL-Funktionen lesen und behandeln sie mit FreeRTOS-Tasks (dieser funktioniert, aber er scheint die RTOS-Leistung nicht zu nutzen)?

Das Handbuch, über das ich spreche: 6.3 Deferred Interrupt Processing (PDF)

Verwenden Sie zunächst keine "nop"-Schleifen, um Verzögerungen zu erzeugen. Verwenden Sie das vTaskDelay -Makro. Auch sollten Sie überprüfen, wo Ihr Code tatsächlich nicht läuft, ob das Problem in den Freertos oder in den ADC-Befehlen liegt.
Was für eine Anleitung?
Der Code des ADC funktioniert alleine und erzeugt so etwas wie die PWM die Helligkeit der LED-Änderung durch Ändern des Potentiometers. Und für die Anleitung: freertos.org/Documentation/…

Antworten (2)

Mir ist bewusst, dass dies eine alte Frage ist, aber ich hatte auch Probleme damit. Das neue FreeRTOS für STM32 empfiehlt, Signale als schnellere und einfachere Alternative zu Semaphoren zu verwenden, insbesondere für die Interrupt-Synchronisation mit einer Task.

Hier ist ein Beispielcode für eine Tastenentprellung. Es ist im Grunde dasselbe wie beim Umgang mit der ADC-Hardware: Senden Sie in der Interrupt-Routine mit dem osSignalSet() ein Signal an die Task. Das Signal kann sogar wiederholt gesendet werden, unabhängig davon, ob die Aufgabe es bereits gelesen hat.

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

Warten Sie innerhalb der Aufgabe einfach ewig auf das Signal mit 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);
    }
}

Zunächst kann die Semaphore in "ADC_IRQHandler()" oder "HAL_ADC_ConvCpltCallback()" (ADC) angegeben werden, indem hinzugefügt wird:

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

Am Ende der Funktion scheint "HAL_ADC_ConvCpltCallback" auch ein ISR zu sein, wenn ich xSemaphoreGive( xBinarySemaphore);. Damit stürzt das Programm ab.

Wichtig ist auch, die Priorität des IRQHandlers numerisch höher als configMAX_SYSCALL_INTERRUPT_PRIORITY zu setzen, in meinem Fall '80', indem man (zB 5) hinzufügt. HAL_NVIC_SetPriority(ADC_IRQn, 5, 0);, Priorität 5 ? 15 in meinem Fall. Weitere Informationen finden Sie hier: http://www.freertos.org/RTOS-Cortex-M3-M4.html .

Wie auch immer, dieser Code funktioniert und nimmt die ADC-Init von 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");
    }
}