Перейти к содержанию
    

FreeRTOS проба семафора.

День добрый. Пробую FreeRTOS (свежескачанный с оф. сайта) на GD32F103VCT6 (Cortex-M3) от GigaDevice в Keil. Сделал HelloWorld с мигалкой - всё работает.

Пробую сделать новый проект с использованием семафора. Задача: по нажатию кнопки срабатывает прерывание и выдается семафор. Задача, отслеживающая семафор, принимает его и после этого что-то там делает и снова ждёт семафор. Код такой:

main.c

//---Variables---//
uint8_t UserKey = OFF;

xSemaphoreHandle xSemaphore; // Создание переменной - дескриптора семафора.
//---------------//


//---Моя задача---
void vAntiBounce (void *pvParameters)
{
while(1)
  {
  xSemaphoreTake( xSemaphore, portMAX_DELAY );

  vTaskDelay(100); // Задача помещается в состояние Blocked до истечения периода задержки.

  if (UserKey == ON)
    GPIO_OCTL(GPIOC) |= GPIO_OCTL_OCTL0; // Свети.
  else 
    GPIO_OCTL(GPIOC) &= ~GPIO_OCTL_OCTL0; // Не свети.
  }
}

int main (void)
{
MCU_Init();

vSemaphoreCreateBinary (xSemaphore) // Создание двоичного семафора. В переменную xSemaphore будет возвращён дескриптор созданного семафора.
if (xSemaphore == NULL)
  ERROR_ACTION(TASK_NOT_CREATE,0);

//---Создаем задачи. Если при создании задачи возвращенный параметр не TRUE, то обрабатываем ошибку---//
                                                      // Параметры функции xTaskCreate:
if ( pdTRUE != xTaskCreate (vAntiBounce,              // - имя функции которая будет задачей.
                            "vAntiBounce",            // - текстовая строка для отладки. Может быть любой, но длина ограничина в конфиге ОС. Ну и память она тоже ест немного.
                            configMINIMAL_STACK_SIZE, // - размер стека для задачи. Определяется опытным путем. В данном случае стоит системный минимум,т.к. задачи используют очень мало памяти. Должно хватить.
                            NULL,                     // - передаваемый параметр (pvParameters) в задачу. В данном случае не нужен, потому NULL. Но можно задаче при создании передевать разные данные, для инициализации или чтобы различать две копии задачи между собой.
                            tskIDLE_PRIORITY + 1,     // - приоритет задачи. В данный момент выбран минимально возможный. На 1 пункт выше чем IDLE.
                            NULL) )                   // - тут вместо NULL можно было вписать адрес переменной типа xTaskHandle в которую бы упал дескриптор задачи.
                                                      // Например так: xTaskCreate(vBlinker, "Blinker", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, &xBlinkerTask);
                                                      // где xBlinkerTask это глобальная переменная обьявленная как xTaskHandle xBlinkerTask;
                                                      // глобальная - чтобы ее везде было видно. Отовсюду она была доступна.
                                                      // Но можно и как static обьявить или еще каким образом передать хэндл.
                                                      // И зная эту переменную и, то что там дескриптор этой задачи мы могли бы из другой задачи ее грохнуть,
                                                      // поменять приоритет или засуспендить. В общем, управлять задачей. Но в моем примере это не требуется.
  {                                                   // Остальные аналогично.
  ERROR_ACTION(TASK_NOT_CREATE,0);
  }
//----------------------------------------------------------------------------------------------------//
  
vTaskStartScheduler(); // Запускаем диспетчер и понеслась.

while(1){;}
}

 

В прерывании делаю так:

void EXTI10_15_IRQHandler(void)
{
static portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
  
if (RESET != exti_interrupt_flag_get(EXTI_14))
  {
  if (gd_eval_key_state_get(KEY_USER)) // По схеме, когда кнопка нажата на выводе порта 0.
    UserKey = OFF;
  else
    UserKey = ON;

  exti_interrupt_flag_clear(EXTI_14);
  xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken ); // 'Дать' семафор для разблокировки задачи.
  if( xHigherPriorityTaskWoken == pdTRUE )
    {
    /* Выдача семафора разблокирует задачу, и приоритет разблокированной
    задачи выше, чем у текущей выполняющейся задачи - поэтому контекст
    выполнения переключается принудительно в разблокированную (с более
    высоким приоритетом) задачу.
    ВНИМАНИЕ: макрос, реально используемый для переключения контекста
    из ISR, зависит от конкретного порта FreeRTOS. Здесь указано
    имя макроса, корректное для порта Open Watcom DOS. Другие порты
    FreeRTOS могут использовать другой синтаксис. Для определения
    используемого синтаксиса обратитесь к примерам, предоставленным
    вместе с портом FreeRTOS. */
    taskYIELD();
    }
  }
}

 

Мои ожидания: при запуске выполняется функция main(), задача стоит в ожидании семафора на строчке

  xSemaphoreTake( xSemaphore, portMAX_DELAY );

При нажатии кнопки, формируется прерывание, генерится семафор, задача ловит семафор и выполняется до конца и опять ждёт семафор.

 

Как оно есть в отладке: при запуске выполняется функция main(), затем БЕЗ нажатия кнопки выполняется задача до конца по пути:

  else 
    GPIO_OCTL(GPIOC) &= ~GPIO_OCTL_OCTL0; // Не свети.

При нажатии на кнопку, задача не выполняется, но в отладчике наблюдаю реакцию на нажатие кнопки - прерывание срабатывает корректно.

 

Почему ожидания не совпадают с реальностью? Где я свернул не туда?

 

На всякий случай мой FreeRTOSConfig.h

#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

/* Here is a good place to include header files that are required across
your application. */
//#include "something.h"

#define configUSE_PREEMPTION                    1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
#define configUSE_TICKLESS_IDLE                 0
#define configCPU_CLOCK_HZ                      96000000
//#define configSYSTICK_CLOCK_HZ                  1000000
#define configTICK_RATE_HZ                      1000 // 250
#define configMAX_PRIORITIES                    5
#define configMINIMAL_STACK_SIZE                128
#define configMAX_TASK_NAME_LEN                 16
#define configUSE_16_BIT_TICKS                  0
#define configIDLE_SHOULD_YIELD                 1
#define configUSE_TASK_NOTIFICATIONS            1
#define configTASK_NOTIFICATION_ARRAY_ENTRIES   3
#define configUSE_MUTEXES                       0
#define configUSE_RECURSIVE_MUTEXES             0
#define configUSE_COUNTING_SEMAPHORES           0
#define configUSE_ALTERNATIVE_API               0 /* Deprecated! */
#define configQUEUE_REGISTRY_SIZE               10
#define configUSE_QUEUE_SETS                    0
#define configUSE_TIME_SLICING                  0
#define configUSE_NEWLIB_REENTRANT              0
#define configENABLE_BACKWARD_COMPATIBILITY     1
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5
#define configSTACK_DEPTH_TYPE                  uint16_t
#define configMESSAGE_BUFFER_LENGTH_TYPE        size_t

/* Memory allocation related definitions. */
#define configSUPPORT_STATIC_ALLOCATION             0
#define configSUPPORT_DYNAMIC_ALLOCATION            1
#define configTOTAL_HEAP_SIZE                       (30*1024) // The total amount of RAM available in GD32F103VC: 48 (KB). Set 18 KB for global var, 40 KB for configTOTAL_HEAP_SIZE.

#define configAPPLICATION_ALLOCATED_HEAP            0
#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP   0

/* Hook function related definitions. */
#define configUSE_IDLE_HOOK                     0
#define configUSE_TICK_HOOK                     0
#define configCHECK_FOR_STACK_OVERFLOW          0
#define configUSE_MALLOC_FAILED_HOOK            0
#define configUSE_DAEMON_TASK_STARTUP_HOOK      0

/* Run time and task stats gathering related definitions. */
#define configGENERATE_RUN_TIME_STATS           0
#define configUSE_TRACE_FACILITY                0
#define configUSE_STATS_FORMATTING_FUNCTIONS    0

/* Co-routine related definitions. */
#define configUSE_CO_ROUTINES                   0
#define configMAX_CO_ROUTINE_PRIORITIES         1

/* Software timer related definitions. */
#define configUSE_TIMERS                        1
#define configTIMER_TASK_PRIORITY               3
#define configTIMER_QUEUE_LENGTH                10
#define configTIMER_TASK_STACK_DEPTH            configMINIMAL_STACK_SIZE

/* Interrupt nesting behaviour configuration. */
#define configKERNEL_INTERRUPT_PRIORITY         255 // [dependent of processor]
#define configMAX_SYSCALL_INTERRUPT_PRIORITY    191 // [dependent on processor and application]
#define configMAX_API_CALL_INTERRUPT_PRIORITY   [dependent on processor and application]

/* Define to trap errors during development. */
#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); } // Определение configASSERT() для запрета прерываний и бесконечного зацикливания.

/* FreeRTOS MPU specific definitions. */
#define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0
#define configTOTAL_MPU_REGIONS                                8 /* Default value. */
#define configTEX_S_C_B_FLASH                                  0x07UL /* Default value. */
#define configTEX_S_C_B_SRAM                                   0x07UL /* Default value. */
#define configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY            1
#define configALLOW_UNPRIVILEGED_CRITICAL_SECTIONS             1

/* ARMv8-M secure side port related definitions. */
#define secureconfigMAX_SECURE_CONTEXTS         5

/* Optional functions - most linkers will remove unused functions anyway. */
#define INCLUDE_vTaskPrioritySet                1
#define INCLUDE_uxTaskPriorityGet               1
#define INCLUDE_vTaskDelete                     1
#define INCLUDE_vTaskSuspend                    1
#define INCLUDE_xResumeFromISR                  1
#define INCLUDE_vTaskDelayUntil                 1
#define INCLUDE_vTaskDelay                      1
#define INCLUDE_xTaskGetSchedulerState          1
#define INCLUDE_xTaskGetCurrentTaskHandle       1
#define INCLUDE_uxTaskGetStackHighWaterMark     0
#define INCLUDE_uxTaskGetStackHighWaterMark2    0
#define INCLUDE_xTaskGetIdleTaskHandle          0
#define INCLUDE_eTaskGetState                   0
#define INCLUDE_xEventGroupSetBitFromISR        1
#define INCLUDE_xTimerPendFunctionCall          0
#define INCLUDE_xTaskAbortDelay                 0
#define INCLUDE_xTaskGetHandle                  0
#define INCLUDE_xTaskResumeFromISR              1

/* A header file that defines trace macro can be included here. */
#define xPortSysTickHandler SysTick_Handler
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler

#endif /* FREERTOS_CONFIG_H */

 

Изменено пользователем LAS9891

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

7 minutes ago, LAS9891 said:

Почему ожидания не совпадают с реальностью? Где я свернул не туда?

Обратите внимание на содержимое макроса:

	#define vSemaphoreCreateBinary( xSemaphore )																							\
		{																																	\
			( xSemaphore ) = xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE );	\
			if( ( xSemaphore ) != NULL )																									\
			{																																\
				( void ) xSemaphoreGive( ( xSemaphore ) );																					\
			}																																\
		}

При создании семафор "открыт".

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

40 minutes ago, aaarrr said:

При создании семафор "открыт".

Согласен. Это состояние по умолчанию? В смысле это так и должно быть всегда, или это можно менять в настройках FreeRTOS?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Настроек нет. Но никто не мешает создать собственный макрос без xSemaphoreGive().

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

20 minutes ago, aaarrr said:

Но никто не мешает создать собственный макрос без xSemaphoreGive().

Согласен. Никто не мешает создать и собственную RTOS.

Мне тогда не понятно, почему в примерах это не упоминается: раз, два.

Есть способ не изменяя RTOSных файлов учесть этот момент? Ну кроме переписывания xSemaphoreGive(). Подумал использовать сразу после vSemaphoreCreateBinary функцию xSemaphoreTake. Но тут пишут: Захват семафора осуществляется API - функцией xSemaphoreTake() и может вызываться только из задач.

Изменено пользователем LAS9891

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 minute ago, LAS9891 said:

Есть способ не изменяя RTOSных файлов учесть этот момент?

Не нужно ничего менять: свой макрос и назвать по-своему.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

ОК. Удивляет, что нету упоминаний о том, что

1 hour ago, aaarrr said:

При создании семафор "открыт".

 

и соответственно задача сразу начнёт выполняться.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

55 minutes ago, aaarrr said:

свой макрос и назвать по-своему.

Проблему решил без макроса, в самой задаче.

Изменено пользователем LAS9891

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

я использую приоритет задач. И при старте задачи с наивысшим приоритетом (значит запускается самая первая), до цикла забираю все семафоры, т.к. они уже есть при старте операционки.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

39 minutes ago, KARLSON said:

я использую приоритет

Как раз вопрос про приоритеты, но не задач, а прерываний.

В файле FreeRTOSConfig.h у меня есть макрос:

/* Define configASSERT() to disable interrupts and sit in a loop. */
#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }

Программа не работала согласно ожиданиям, пока не закомментировал этот макрос. Отладка вставала тут:

#if ( configASSERT_DEFINED == 1 )

    void vPortValidateInterruptPriority( void )
    {
        uint32_t ulCurrentInterrupt;
        uint8_t ucCurrentPriority;

        /* Obtain the number of the currently executing interrupt. */
        ulCurrentInterrupt = vPortGetIPSR();

        /* Is the interrupt number a user defined interrupt? */
        if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER )
        {
            /* Look up the interrupt's priority. */
            ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ];

            /* The following assertion will fail if a service routine (ISR) for
             * an interrupt that has been assigned a priority above
             * configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API
             * function.  ISR safe FreeRTOS API functions must *only* be called
             * from interrupts that have been assigned a priority at or below
             * configMAX_SYSCALL_INTERRUPT_PRIORITY.
             *
             * Numerically low interrupt priority numbers represent logically high
             * interrupt priorities, therefore the priority of the interrupt must
             * be set to a value equal to or numerically *higher* than
             * configMAX_SYSCALL_INTERRUPT_PRIORITY.
             *
             * Interrupts that	use the FreeRTOS API must not be left at their
             * default priority of	zero as that is the highest possible priority,
             * which is guaranteed to be above configMAX_SYSCALL_INTERRUPT_PRIORITY,
             * and	therefore also guaranteed to be invalid.
             *
             * FreeRTOS maintains separate thread and ISR API functions to ensure
             * interrupt entry is as fast and simple as possible.
             *
             * The following links provide detailed information:
             * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html
             * https://www.FreeRTOS.org/FAQHelp.html */
            configASSERT( ucCurrentPriority >= ucMaxSysCallPriority );
        }

        /* Priority grouping:  The interrupt controller (NVIC) allows the bits
         * that define each interrupt's priority to be split between bits that
         * define the interrupt's pre-emption priority bits and bits that define
         * the interrupt's sub-priority.  For simplicity all bits must be defined
         * to be pre-emption priority bits.  The following assertion will fail if
         * this is not the case (if some bits represent a sub-priority).
         *
         * If the application only uses CMSIS libraries for interrupt
         * configuration then the correct setting can be achieved on all Cortex-M
         * devices by calling NVIC_SetPriorityGrouping( 0 ); before starting the
         * scheduler.  Note however that some vendor specific peripheral libraries
         * assume a non-zero priority group setting, in which cases using a value
         * of zero will result in unpredictable behaviour. */
        configASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= ulMaxPRIGROUPValue ); //-<----------------------------------------------------ВОТ ТУТА ВИСЕЛО.
    }

Ну ок, косяки с приоритетами прерываний. Почитал как надо делать тут

Контроллер настроил так:

- общее количество приоритетов вытеснения прерывании 16 (0...15);

- общее количество субприоритетов 0.

- используемые прерывания: SysTick (приоритет 15) и EXTI10_15 (приоритет 2).

Далее настраиваем макросы:

/* Interrupt nesting behaviour configuration. */
#define configKERNEL_INTERRUPT_PRIORITY         1
#define configMAX_SYSCALL_INTERRUPT_PRIORITY    3
//#define configMAX_API_CALL_INTERRUPT_PRIORITY   [dependent on processor and application]

Судя по статье (в самом низу зелёная картинка), при таких макросах, и если в обработчике прерывания (ISR) используются функции API FreeRTOS, то приоритет такого прерывания должен быть в диапазоне от 0 до 3.

У меня прерывание SysTick использует только FreeRTOS. Прерывание EXTI10_15 использует функцию xSemaphoreGiveFromISR, приоритет прерывания 2. Следовательно, проблем не должно бы быть, НО

при таких настройках проект при отладке опять висит где и висел.

Где я опять накосячил?

 

Изменено пользователем LAS9891

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

             * Numerically low interrupt priority numbers represent logically high
             * interrupt priorities, therefore the priority of the interrupt must
             * be set to a value equal to or numerically *higher* than
             * configMAX_SYSCALL_INTERRUPT_PRIORITY.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

55 minutes ago, aaarrr said:
             * Numerically low interrupt priority numbers represent logically high
             * interrupt priorities, therefore the priority of the interrupt must
             * be set to a value equal to or numerically *higher* than
             * configMAX_SYSCALL_INTERRUPT_PRIORITY.

1) Как вообще сосуществуют понятия приоритет прерываний и приоритет задач? Вот в STMке назначено 16 приоритетов вытеснения и 0 субприоритетов на прерывания. Эта цифра 16 относится только к приоритетам прерываний или ещё также и к приоритетам задач?

2) Вот в STMке чем меньше число (номер) приоритета, тем выше приоритет. Это утверждение относится только к приоритетам прерываний или и к приоритетам задач?

3) Макрос configKERNEL_INTERRUPT_PRIORITY - это макрос, который определяет приоритет прерывания, используемого для тиков FreeRTOS. Т.е. это приоритет прерывания SysTick микроконтроллера. Так или не так?

4) Макрос configMAX_SYSCALL_INTERRUPT_PRIORITY - это макрос, который устанавливает наивысший приоритет ЧЕГО?, из которого могут быть вызваны защищенные для прерываний функции FreeRTOS API, т.е. функции в названии которых есть FromISR (например xSemaphoreGiveFromISR). Так или не так?

5) Для макросов из пунктов 3 и 4 приоритеты должны быть выбраны из диапазона 0...15. Так или не так? Т.е. контроллер тут получается законодатель мод?

6) Везде пишут, что если SysTick используется для шедулера, то приоритет SysTickа нужно сделать самым низким, я сделал его 15 при настройке контроллера. Значит ли это, что и configKERNEL_INTERRUPT_PRIORITY должен быть равен 15 ?

Изменено пользователем LAS9891

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

6 minutes ago, LAS9891 said:

Как вообще сосуществуют понятия приоритет прерываний и приоритет задач?

Эти понятия никак не связаны между собой.

 

6 minutes ago, LAS9891 said:

Это утверждение относится только к приоритетам прерываний или и к приоритетам задач?

У задач всё наоборот: больше число - выше приоритет.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

18 минут назад, LAS9891 сказал:

6) Везде пишут, что если SysTick используется для шедулера, то приоритет SysTickа нужно сделать самым низким, я сделал его 15 при настройке контроллера. Значит ли это, что и configKERNEL_INTERRUPT_PRIORITY должен быть равен 15 ?

Самым низким должен быть приоритет PendSV, а приоритет SysTick должен быть на единицу выше, чем у PendSV.

PendSV - должен быть самым низким обязательно;

SysTick - ниже всех аппаратных ISR, но выше PendSV - очень желательно, но не обязательно (по логике работы - он просто не нужен высоким).

Т.е. - если у вас 16 уровней приоритета прерываний, то:

PendSV = 15

SysTick = 14

... аппаратные прерывания

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...