0

FreeRTOS Notları #6: Mutex

    Bir multitasking yapan sistemde birden fazla taskın aynı kaynağa erişmesinde sorunlar çıkabilir. Örneğin; 2×16 LCD ekrana yazı yazan iki task düşünelim. Bunlardan biri “Hello world” yazsın. Diğer task ise “Mehmet” yazsın. Şimdi task1 in çalıştığı durumu düşünelim. En temelde ekrana bu yazılar karakter karakter gönderilecektir. Task1 çalışmaya başladı ve “Hello w” kısmına kadar yazdı. Tam burada “o” karakterini yazacak iken bir context switching oluştu ve Task2 yürütülmeye başlandı. Bu durumda ekrana Task2 nin yazdırmak istedikleri yazılacaktır değil mi? Diyelim ki Task2 yazmak istediğinin hepsini yazdı. Ekranda artık “Hello wMehmet” yazacaktır. Tekrar context switching olduğunda Task1 kaldığı yerden çalışmaya devam edecek ve LCD ekranda yazan cümlenin son hali “Hello wMehmetorld” şeklinde olacaktır. Bu sorun bir taskın aynı kaynak ile işini bitirmeden diğer taskların o kaynağa erişmek istemesinden kaynaklanır. RTOS’da bu sorunun önüne geçmek için birkaç yöntem vardır. Bunlardan biride mutex(mutual exclusion) dir.
    Veri tutarlılığını sağlamak ve tasklar arasında paylaşılmış kaynakların korunması için mutex tekniği kullanılır. Böylelikle task bir kaynak ile işini bitirmeden diğer tasklar o kaynağa erişemez.

Kritik Alanlar ve Scheduler’ı Askıya Alma(Suspend)

    Kod içerisindeki basit kırıtik alanlar için taskENTERCRITICAL() ve taskEXİTCRITICAL()  makroları kullanılabilir.
void vPrintString( const char *pcString )
{
/* Write the string to stdout, using a critical section as a crude method of
mutual exclusion. */
taskENTER_CRITICAL();
{
printf( "%s", pcString );
fflush( stdout );
}
taskEXIT_CRITICAL();
}
    Bu makroları kullanmak mutex’in çok ilkel bir versiyonudur. Bu makrolar interruptları kapatır. Context switching ise systick timer kesmesine göre yapıldığı için taskEXİTCRITICAL() makrosu çağrılmadan diğer tasklar çalışmaz.
    Krıtik alanlar olabildiğince kısa tutulmalıdır. Aksi takdirde interrupt cevap süresini kötü yönde etkiler.

    Krıtik alanlar için scheduler’ın askıya alınması (suspend) işlemi de  kullanılabilir. Suspend etmek sadece taskların aynı kaynağa erişmesini önler. İnterruptlar o kaynağa hala erişebilir.

void vTaskSuspendAll( void );
BaseType_t xTaskResumeAll( void );

Mutex(Mutual Exclusion)

FreeRTOS-mutex

    Mutex tasklar arasında paylaşılmış bir kaynağa erişmek için kullanılan özel tipte bir binary semaphore‘dur. Burada token üzerinden gidecek olursak; Bir task korunmak istenen bir kaynağa ulaşmak istediğinde token’ı ilk önce elde etmelidir(take). Kaynak ile işini bitirdikten sonra ise token’ı diğer taskların aynı kaynağa erişebilmesi için geri bırakmalıdır(give). Mutex’ler aynı semaphore API fonksiyonlarını kullanır. Bu yüzden bir blok süresinin tanımlanmasına izin verir. Binary semaphore’un aksine mutex’lerde öncelik kalıtım mekanizması(priority inheritance) vardır. Eğer bir task mutex’i elde etmiş ise daha yüksek öncelikli bir task aynı mutex’i elde edemeyecek ve bloklanacaktır. Bu mekanizma yüksek öncelikli taskın mümkün olan en kısa süre bloklanmasını sağlamak için tasarlanmıştır.
Mutex priority inheritance
    Priority Inheritance kavramını daha iyi anlamak için yukarıdaki grafik üzerinden gidelim.
  1. LP task mutex’i elde etmiş ve işlemlerini yaparken bir context switching isteği gelmiş.
  2. HP task aynı mutex’i elde etmek istediğinde mutex hali hazırda başka bir task tarafından tutulduğu için elde edememiş ve bloklanmış.
  3. HP task bloklandığında sıradaki task olan MP task çalışması gerekirken LP task çalışmaya devam etmiş. Burada HP task önceliğini miras olarak LP task’a vermiş diyebiliriz. Buradaki amaç HP taskı olabildiğince az bloklanmış durumda tutmaktır.
  4. LP task mutex’i bıraktığında HP task mutex’i elde edebilmiş ve blocked stateden çıkıp işlemlerini yapmış.
    Mutex’ler interrupt içerisinden kullanılmamalıdır çünkü;
  • Tasklar arasında geçerli bir öncelik kalıtım mekanizması içerirler, interruptlar arasında değil.
  • Bir interrupt mutex tarafından korunan bir kaynağın ulaşılabilir olmasını beklemek için engellenemez.
    FreeRTOS da mutex kullanabilmek için FreeRTOSConfig.h dosyası içindeki configUSE_MUTEXES makrosu 1 olarak ayarlanmalıdır.

xSemaphoreCreateMutex() Fonksiyonu

    FreeRTOS da mutex oluşturabilmek için xSemaphoreCreateMutex() fonksiyonu kullanılır.
SemaphoreHandle_t xSemaphoreCreateMutex( void );
  • Return Değeri: Eğer mutex oluşturulamadı ise NULL, oluşturuldu ise handle döndürür.

    Mutex için semaphore fonksiyonları aşağıdaki gibi kullanılır.

...
...
xSemaphoreTake( xMutex, portMAX_DELAY );
{
/* The following line will only execute once the mutex has been successfully
obtained. Standard out can be accessed freely now as only one task can have
the mutex at any one time. */
printf( "%s", pcString );
fflush( stdout );
/* The mutex MUST be given back! */
}
xSemaphoreGive( xMutex );
...
...
    Binary semaphore’un çalışma mantığını hatırlayalım. Normalde xSemaphoreTake() satırında task eğer semaphore mevcut değil ise süresiz olarak bloklanıyordu ta ki başka bir task veya interrupt tarafından xSemaphoreGive() fonksiyonu ile verilene kadar. Mutex te ise xSemaphoreTake() fonksiyonu kullanıldığında task mutex’e sahip olur ve task bloklanmadan işlemlerine devam eder. Daha sonra aynı task içerisinde xSemaphoreGive() fonksiyonu kullanılana kadar yapılan işlemlere başka bir task erişemez. Bu binary semaphore ile mutex arasındaki farktır. Burada önemli bir nokta var. Örneğin bir kaynak iki task tarafından kullanılıyor ise iki task ta mutex’i elde etmek istemelidir. Mutex’i ilk elde eden task işlemlerini yapar. Diğer task ise mutex’i elde edemediği için blocked state e girer. Böylece iki taskın aynı kaynağa erişmesi engellenmiş olur.

Recursive Mutex

    Recursive mutex counting semaphore gibi çalışır. Eğer bir task mutex’i beş defa elde ederse mutex’i elde etmek isteyen bir başka task mutexin beş defa verilemesini(give) beklemek zorundadır. Recursive Mutex için başlıca API fonksiyonları şunlardır; xSemaphoreCreateRecursiveMutex(), xSemaphoreTakeRecursive(), xSemaphoreGiveRecursive().

FreeRTOS Mutex Kullanımına Ait Örnek

    Amaç; Uart üzerinden string gönderen iki task oluştur. Task fonksiyonları birebir aynı olsun(Göndereceği stringler hariç). Mutex kullanmadan verilerin nasıl gideceği ve mutex ile verilerin nasıl gideceğini gözlemle. Burada ortak kullanılan kaynak UART1 dir.
    Öncelikle mutex’i kullanabilmek için semaphore kütüphanesini projeye dahil ediyoruz. Mutexin aynı semaphore API fonksiyonlarını kullandığını hatırlayalım.
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
    Mutex için kullanılacak olan handle’ı main fonksiyonunun üzerinde tanımlıyoruz çünkü farklı task fonksiyonları içerisinden bu handle’ı çağırmamız gerekecek.
SemaphoreHandle_t UartMutex;

    main fonksiyon içerisinde Mutex oluşturma işlemini yapıyoruz.

UartMutex = xSemaphoreCreateMutex();
    İki adet task oluşturuyoruz.
xTaskCreate(Task1Function,"Task1",configMINIMAL_STACK_SIZE,NULL,0,NULL);
xTaskCreate(Task2Function,"Task2",configMINIMAL_STACK_SIZE,NULL,0,NULL);
    
vTaskStartScheduler();
    Burada önce mutex kullanmadığımız durumu görebilmek için aşağıdaki gibi task fonksiyonlarını oluşturduk.
void Task1Function(void * argument)
{
    char message[50];
    int i=0;
  for(;;)
  {
        sprintf(message,"Task1 is running: %d\n",i);
        Uart_Print(message);
        i++;
        vTaskDelay(pdMS_TO_TICKS(1000));
  }
}

void Task2Function(void * argument)
{
  char message[50];
    int i=0;
    
  for(;;)
  {
        sprintf(message,"Task2 is running: %d\n",i);
        Uart_Print(message);
        i++;
        vTaskDelay(pdMS_TO_TICKS(1000));
  }
}
    Bu fonksiyonları bir inceleyelim. Burada task fonksiyonları gönderdikleri mesajlar hariç birebir aynıdır değil mi? Yani burada aynı UART üzerinden farklı mesajlar aynı anda gönderilmeye çalışılabilir. Burada HAL kütüphanesinin UART fonksiyonlarını kullandığımız için UART a iki taskın ayna anda erişmesi aslında HAL kütüphanesi tarafından engellenir çünkü HAL kütüphanesi transmit fonksiyonunu kullanırken __HAL_LOCK(huart) ile UART’ı kilitler. Yani Tasklardan biri UART’ı kullanırken bir context switching oluşsa bile diğer task UART kilitli olduğu için erişemeyecek ve transmit fonksiyonu HAL_BUSY değerini döndürecektir. Fakat HAL kütüphanesi kullanmadığımız durumda yani transmit fonksiyonunu register seviyesinde veya SPL ile kendimiz yazdığımızda(HAL’daki gibi bir lock mekanizması yok ise) UART’tan veri gönderirken bir context switching oluştuğunda gönderilen stringin yarısı Task1 in göndermek istediği, diğer yarısı da Task2 nin göndermek istediği string olarak karşı tarafa iletilecektir. Yukarıdaki task fonksiyonlarında UART başka bir task tarafından kullanılırken diğer taskın  UART’a erişip erişemeyeceğini görebilmek için integer değeri her seferinde arttırılarak UART üzerinden gönderilmiştir. Yukarıdaki task’ların çıktısı aşağıdaki gibi olur.
    Yukarıda görüldüğü gibi normalde  her taskın sayacı doğrusal olarak artması gerekirken, birer atlayarak sayac artmış. Aslında sayac birer birer artıyor ama UART’a sadece tek bir task erişebildiği için diğer task’ın gönderdiği string te sayacın bir fazla olduğunu görüyoruz. Yani normalde göndermek istediğimiz stringler ;
Task1 is running: 0
Task2 is running: 0
Task1 is running: 1
Task2 is running: 1
 şeklinde olması gerekirken yukarıdaki resimdeki gibi olmuştur. Burada HAL kütüphanesi UART’ı kilitlemeseydi iki fonksiyonda aynı UART ile string göndermeye çalışacak ve yazının başında verdiğim örneğe benzer bir durum olacaktı. Şimdi bu sorunu mutex kullanarak giderelim.
void Task1Function(void * argument)
{
    char message[50];
    int i=0;
  for(;;)
  {
        
        sprintf(message,"Task1 is running: %d\n",i);
        xSemaphoreTake(UartMutex,portMAX_DELAY);
        Uart_Print(message);
        xSemaphoreGive(UartMutex);
        i++;
        vTaskDelay(pdMS_TO_TICKS(1000));
  }
}
void Task2Function(void * argument)
{
  char message[50];
    int i=0;
    
  for(;;)
  {
        sprintf(message,"Task2 is running: %d\n",i);
        xSemaphoreTake(UartMutex,portMAX_DELAY);
        Uart_Print(message);
        xSemaphoreGive(UartMutex);
        i++;
        vTaskDelay(pdMS_TO_TICKS(1000));
  }
}
    Yukarıda görüldüğü gibi mutex’i sadece Uart_Print() fonskiyonundan önce(take) ve sonra(give) kullanarak bu sorunu çözebiliriz. Yukarıda önce Task1 çalışacak ve mutex’i veya token’ı elde edecektir. Task1 UART üzerinden string gönderirken bir context switching oluşsa bile Task2 mutex’i elde edemeyeceği için UART’ı kullanamayacak ve blocked state’e girecektir. Task1 tekrardan kaldığı yerden çalışıp mutex’i bıraktığında Task2 mutex’i elde edecek ve UART üzerinden string gönderecektir. Mutex kullanılmış halinin çıktısı aşağıdaki gibi olur.  FreeRTOS da mutexin kullanım amacı basitçe böyledir.

Tüm Kodlar (FreeRTOS ile)

/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * This notice applies to any and all portions of this file
  * that are not between comment pairs USER CODE BEGIN and
  * USER CODE END. Other portions of this file, whether
  * inserted by the user or by software development tools
  * are owned by their respective copyright owners.
  *
  * Copyright (c) 2020 STMicroelectronics International N.V.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted, provided that the following conditions are met:
  *
  * 1. Redistribution of source code must retain the above copyright notice,
  *    this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
  * 3. Neither the name of STMicroelectronics nor the names of other
  *    contributors to this software may be used to endorse or promote products
  *    derived from this software without specific written permission.
  * 4. This software, including modifications and/or derivative works of this
  *    software, must execute solely and exclusively on microcontroller or
  *    microprocessor devices manufactured by or for STMicroelectronics.
  * 5. Redistribution and use of this software other than as permitted under
  *    this license is void and will automatically terminate your rights under
  *    this license.
  *
  * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
  * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
  * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f1xx_hal.h"


/* USER CODE BEGIN Includes */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "string.h"


#define Uart_Print(__message)                    HAL_UART_Transmit(&huart1,(uint8_t *)__message,strlen(__message),HAL_MAX_DELAY)
/* USER CODE END Includes */


/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;
SemaphoreHandle_t UartMutex;


/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/


/* USER CODE END PV */


/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);


/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
void Task1Function(void * argument);
void Task2Function(void * argument);


/* USER CODE END PFP */


/* USER CODE BEGIN 0 */


/* USER CODE END 0 */


/**
  * @brief  The application entry point.
  *
  * @retval None
  */
int main(void)
{
  /* USER CODE BEGIN 1 */


  /* 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_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
    
    UartMutex = xSemaphoreCreateMutex();                                                                                
    xTaskCreate(Task1Function,"Task1",configMINIMAL_STACK_SIZE,NULL,0,NULL);
    xTaskCreate(Task2Function,"Task2",configMINIMAL_STACK_SIZE,NULL,0,NULL);
    
    vTaskStartScheduler();


  /* USER CODE END 2 */


  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {


  /* USER CODE END WHILE */


  /* USER CODE BEGIN 3 */


  }
  /* USER CODE END 3 */


}


/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{


  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;


    /**Initializes the CPU, AHB and APB busses clocks
    */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }


    /**Initializes the CPU, AHB and APB busses clocks
    */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;


  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }


    /**Configure the Systick interrupt time
    */
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);


    /**Configure the Systick
    */
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);


  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 15, 0);
}


/* USART1 init function */
static void MX_USART1_UART_Init(void)
{


  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }


}


/** Configure pins as
        * Analog
        * Input
        * Output
        * EVENT_OUT
        * EXTI
*/
static void MX_GPIO_Init(void)
{


  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();


}


/* USER CODE BEGIN 4 */


/* USER CODE END 4 */


/* Task1Function function */
void Task1Function(void * argument)
{
    char message[50];
    int i=0;
  for(;;)
  {
        
        sprintf(message,"Task1 is running: %d\n",i);
        xSemaphoreTake(UartMutex,portMAX_DELAY);
        Uart_Print(message);
        xSemaphoreGive(UartMutex);
        i++;
        vTaskDelay(pdMS_TO_TICKS(1000));
  }
}


/* Task2Function function */
void Task2Function(void * argument)
{
  char message[50];
    int i=0;
    
  for(;;)
  {
        sprintf(message,"Task2 is running: %d\n",i);
        xSemaphoreTake(UartMutex,portMAX_DELAY);
        Uart_Print(message);
        xSemaphoreGive(UartMutex);
        i++;
        vTaskDelay(pdMS_TO_TICKS(1000));
  }
}


/**
  * @brief  Period elapsed callback in non blocking mode
  * @note   This function is called  when TIM1 interrupt took place, inside
  * HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
  * a global variable "uwTick" used as application time base.
  * @param  htim : TIM handle
  * @retval None
  */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* USER CODE BEGIN Callback 0 */


  /* USER CODE END Callback 0 */
  if (htim->Instance == TIM1) {
    HAL_IncTick();
  }
  /* USER CODE BEGIN Callback 1 */


  /* USER CODE END Callback 1 */
}


/**
  * @brief  This function is executed in case of error occurrence.
  * @param  file: The file name as string.
  * @param  line: The line in file as a number.
  * @retval None
  */
void _Error_Handler(char *file, int line)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  while(1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}


#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */


/**
  * @}
  */


/**
  * @}
  */


/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

CMSIS-RTOS ile Mutex Kullanımı

    Mutex için kullanılan handle tipi osMutexId dir ve aşağıdaki gibi tanımlanır.

osMutexId UartMutexHandle;

    Mutex tanımlayabilmek için osMutexDef() makrosu kullanılır.

#define osMutexDef(name)   const osMutexDef_t os_mutex_def_##name = { 0 }

    Mutex oluşturmak için osMutexCreate() fonksiyonu kullanılır.

osMutexId osMutexCreate (const osMutexDef_t *mutex_def)
    osMutexCreate() fonksiyonuna parametre osMutex makrosu referansı ile girilir. osMutex() ise parametre olarak osMutexDef() makrosuna girilen mutex ismini alır.

    CMSIS-RTOS da mutex aşağıdaki gibi oluşturulabilir.

osMutexDef(UartMutex);
UartMutexHandle = osMutexCreate(osMutex(UartMutex));
    CMSIS-RTOS da xSemaphoreTake() fonksiyonu yerine osMutexWait() fonksiyonu kullanılır.
osStatus osMutexWait (osMutexId mutex_id, uint32_t millisec)

    Bu fonksiyon osStatus tipinde bir değer döndürür. osMutexWait fonksiyonu bu değeri osErrorOS veya osOK şeklinde döndürür. Bu fonksiyon aşağıdaki gibi kullanılabilir.

osMutexWait(UartMutexHandle,osWaitForever);

    Mutex’i bırakmak için CMSIS-RTOS da osMutexRelease() fonksiyonu kullanılır. Aldığı parametre ise mutex handle’ı dır.

osMutexRelease(UartMutexHandle);

    Bu bilgiler ışığında task fonksiyonları aşağıdaki gibi düzenlenebilir.

/* Task1Function function */
void Task1Function(void const * argument)
{
  /* USER CODE BEGIN 5 */
    char message[50];
  int i=0;
  /* Infinite loop */
  for(;;)
  {
    sprintf(message,"Task1 is running: %d\n",i);
    osMutexWait(UartMutexHandle,osWaitForever);
    Uart_Print(message);
    osMutexRelease(UartMutexHandle);
    i++;
    osDelay(1000);
  }
  /* USER CODE END 5 */
}
/* Task2Function function */
void Task2Function(void const * argument)
{
  /* USER CODE BEGIN Task2Function */
    char message[50];
  int i=0;
  /* Infinite loop */
  for(;;)
  {
    sprintf(message,"Task2 is running: %d\n",i);
    osMutexWait(UartMutexHandle,osWaitForever);
    Uart_Print(message);
    osMutexRelease(UartMutexHandle);
    i++;
    osDelay(1000);
  }
  /* USER CODE END Task2Function */
}

CubeMX Ayarları

    “FreeRTOS Configuration” penceresinde “Config parameters” sekmesindeki “USE_MUTEXES” kutucuğu “Enabled” olarak aşağıdaki gibi ayarlanmalıdır.

    CubeMX’de Mutex “Timers and Semaphores” sekmesinde aşağıdaki gibi oluşturulur.

Tüm Kodlar (CMSIS-RTOS ile)

/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * This notice applies to any and all portions of this file
  * that are not between comment pairs USER CODE BEGIN and
  * USER CODE END. Other portions of this file, whether
  * inserted by the user or by software development tools
  * are owned by their respective copyright owners.
  *
  * Copyright (c) 2020 STMicroelectronics International N.V.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted, provided that the following conditions are met:
  *
  * 1. Redistribution of source code must retain the above copyright notice,
  *    this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
  * 3. Neither the name of STMicroelectronics nor the names of other
  *    contributors to this software may be used to endorse or promote products
  *    derived from this software without specific written permission.
  * 4. This software, including modifications and/or derivative works of this
  *    software, must execute solely and exclusively on microcontroller or
  *    microprocessor devices manufactured by or for STMicroelectronics.
  * 5. Redistribution and use of this software other than as permitted under
  *    this license is void and will automatically terminate your rights under
  *    this license.
  *
  * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
  * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
  * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f1xx_hal.h"
#include "cmsis_os.h"


/* USER CODE BEGIN Includes */
#include "string.h"


#define Uart_Print(__message)                    HAL_UART_Transmit(&huart1,(uint8_t *)__message,strlen(__message),HAL_MAX_DELAY)
/* USER CODE END Includes */


/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;


osThreadId Task1Handle;
osThreadId Task2Handle;
osMutexId UartMutexHandle;


/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/


/* USER CODE END PV */


/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
void Task1Function(void const * argument);
void Task2Function(void const * argument);


/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/


/* USER CODE END PFP */


/* USER CODE BEGIN 0 */


/* USER CODE END 0 */


/**
  * @brief  The application entry point.
  *
  * @retval None
  */
int main(void)
{
  /* USER CODE BEGIN 1 */


  /* 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_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
    
  /* USER CODE END 2 */


  /* Create the mutex(es) */
  /* definition and creation of UartMutex */
  osMutexDef(UartMutex);
  UartMutexHandle = osMutexCreate(osMutex(UartMutex));


  /* 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 */


  /* Create the thread(s) */
  /* definition and creation of Task1 */
  osThreadDef(Task1, Task1Function, osPriorityNormal, 0, 128);
  Task1Handle = osThreadCreate(osThread(Task1), NULL);


  /* definition and creation of Task2 */
  osThreadDef(Task2, Task2Function, osPriorityNormal, 0, 128);
  Task2Handle = osThreadCreate(osThread(Task2), NULL);


  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */


  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */


  /* 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 */


}


/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{


  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;


    /**Initializes the CPU, AHB and APB busses clocks
    */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }


    /**Initializes the CPU, AHB and APB busses clocks
    */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;


  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }


    /**Configure the Systick interrupt time
    */
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);


    /**Configure the Systick
    */
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);


  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 15, 0);
}


/* USART1 init function */
static void MX_USART1_UART_Init(void)
{


  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }


}


/** Configure pins as
        * Analog
        * Input
        * Output
        * EVENT_OUT
        * EXTI
*/
static void MX_GPIO_Init(void)
{


  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();


}


/* USER CODE BEGIN 4 */


/* USER CODE END 4 */


/* Task1Function function */
void Task1Function(void const * argument)
{


  /* USER CODE BEGIN 5 */
    char message[50];
  int i=0;
  /* Infinite loop */
  for(;;)
  {
        sprintf(message,"Task1 is running: %d\n",i);
        osMutexWait(UartMutexHandle,osWaitForever);
        Uart_Print(message);
    osMutexRelease(UartMutexHandle);
    i++;
    osDelay(1000);
  }
  /* USER CODE END 5 */
}


/* Task2Function function */
void Task2Function(void const * argument)
{
  /* USER CODE BEGIN Task2Function */
    char message[50];
  int i=0;
  /* Infinite loop */
  for(;;)
  {
    sprintf(message,"Task2 is running: %d\n",i);
        osMutexWait(UartMutexHandle,osWaitForever);
        Uart_Print(message);
    osMutexRelease(UartMutexHandle);
    i++;
    osDelay(1000);
  }
  /* USER CODE END Task2Function */
}


/**
  * @brief  Period elapsed callback in non blocking mode
  * @note   This function is called  when TIM1 interrupt took place, inside
  * HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
  * a global variable "uwTick" used as application time base.
  * @param  htim : TIM handle
  * @retval None
  */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* USER CODE BEGIN Callback 0 */


  /* USER CODE END Callback 0 */
  if (htim->Instance == TIM1) {
    HAL_IncTick();
  }
  /* USER CODE BEGIN Callback 1 */


  /* USER CODE END Callback 1 */
}


/**
  * @brief  This function is executed in case of error occurrence.
  * @param  file: The file name as string.
  * @param  line: The line in file as a number.
  * @retval None
  */
void _Error_Handler(char *file, int line)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  while(1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}


#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */


/**
  * @}
  */


/**
  * @}
  */


/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

Kaynaklar

Mehmet Topuz

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir