0

FreeRTOS Notları #3: Queue(Kuyruk)

    Kuyruk(Queue) veri yapılarından da hatırlayacağımız üzere doğrusal bir veri saklama yapısıdır. Bu konuyu basit bir örnekle somutlaştırabiliriz. Bir bankamatikten para çekmek istedik ve bankamatiğin önüne gittiğimizde bankamatiğin önünde uzunca bir sıra olduğunu gördük. Bu sıra veya kuyruk nasıl işler? Kuyruğun bir başı vardır yani bankamatikten o anda para çeken bir kişi vardır ve muhtemelen kuyruğa ilk olarak o girmiştir. Ondan sonra gelenler onun arkasına sıralanır ve sıranın başındaki kişi bankamatikte işini hallettikten sonra kuyruktan ayrılır ve bir arkasındaki kişi kuyruğun başına geçer. Bu işlem sürekli tekrar edilerek en sonunda kuyrukta kimse kalmayana kadar devam eder. RTOS daki kuyruk kullanımı da buna benzer. Bu örnekte kuyruktaki insanlar RTOS’da veriler veya mesajlardır. Bu verileri tasklar sırası ile okuyarak belli işlemler yaparlar ve kuyruk boş iken tasklar bloklanabilir. Kuyruk kullanımının güzelliği de burada başlar. Kuyruğa bir veri geldiğinde istenilen task harekete geçirilebilir.

    Bir kuyruk(queue) sabit boyutlu değişkenler(8 bit,16 bit vb) tutabilir. Kuyrukta bulunan bu değişkenlerin maksimum sayısı ise “length” olarak isimlendirilir. Bu iki parametre kuyruk ilk oluşturulduğunda tanımlanır.
    Kuyruklar genelde FIFO(First In First Out) buffer kullanır. Yani kuyruğa ilk yazılan veri ilk önce okunur. Bir veri kuyruğa yazılırken genellikle sondan eklenir,veri okunacağı zaman kuyruğun başından okunur.
    Kuyruklar kendi başlarına bir nesnedir aslında. Herhangi bir task veya ISR(Interrupt Service Routine) içerisinden erişilebilir. Birden fazla task kuyruğa veri yazabilir ve aynı kuyruktan veri okuyabilir. Genelde birden fazla taskın kuyruğa veri yazması birden fazla taskın kuyruktan veri okumasından daha yaygındır.
    Tasklar kuyruk ile bloklanabilir. Eğer kuyrukta veri yok ise kuyruğa veri gelene kadar task bloklanabilir. Bu bloklama kuyruğa veri gelene kadar yapılabileceği gibi opsiyonel olarak belli bir süre ile de yapılabilir. Yani task belli bir süre kuyruğa veri gelmesini bekleyip bu süre içerisinde veri gelmez ise işlemlerine kaldığı yerden devam edebilir. Kuyruktan veri okuyan birden fazla task varsa bu task’lar aynı anda bloklanabilir fakat kuyruğa gelen veriyi ilk olarak en yüksek öncelikli task alır. Eğer kuyruktan veri bekleyen taskların öncelikleri eşit ise veriyi en uzun süredir bekleyen task kuyruktan okur.
    Bir task kuyruğa veri yazarken de bloklanabilir. Eğer kuyruk dolu ise kuyruğa veri yazma işlemi yapan task kuyruktan veri silinene kadar bloklanabilir. Yine bu durumda da birden fazla task kuyruğa veri yazma işlemi yaparken bloklanabilir. En yüksek öncelikli task kuyrukta yer açıldığında veriyi kuyruğa yazar. Eğer öncelikler eşit ise kuyruğa veri yazmak için en uzun süre bekleyen task kuyruğa veriyi yazar.

Kuyruk Oluşturma

    Kuyruk(Queue) da aslında bir RTOS nesnesidir. FreeRTOS‘da kuyruk oluşturmak için xQueueCreate() fonksiyonu kullanılır. Bu fonksiyon QueueHandle_t tipinde bir değer döner. Kuyruk oluşturulduğunda heap içerisinde dolayısı ile RAM de alan ayrılır. Eğer bu alan ayırma ile ilgili bir sorun oluşursa xQueueCreate() fonksiyonu NULL değeri döndürür. Eğer kuyruk sorunsuz bir şekilde oluşturuldu ise bir handle döndürür.
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );
  • uxQueueLength: Kuyruğun uzunluğunu yani kuyruğa maksimum kaç eleman girileceğinin belirlendiği parametredir.
  • uxItemSize: Kuyrukta depolanacak her verinin kaç bayt olacağı bu parametre ile girilir.
  • Return Değeri: Kuyruk sorunsuz oluşturulduysa handle, oluşturulamadı ise NULL değeri döndürür.

Kuyruğa Veri Yazma

    Bir kuyruğa veri yazmak için iki tane fonksiyon kullanılabilir. Bunlar; xQueueSendToBack() ve  xQueueSendToFront() API fonksiyonlarıdır. Biri kuyruğun başına yazarken, diğeri kuyruğun sonuna yazar. Bunlardan hariç bir de xQueueSend()  fonksiyonu vardır. Bu fonksiyon xQueueSendToBack() fonksiyonu ile aynı işlemi yapar yani kuyruğun sonuna yazar.

    Eğer bir interrupt fonksiyonu içerisinden kuyruğa veri yazılacaksa bu fonksiyonları kullanamayız. Bunun için ayrı tanımlanmış xQueueSendToBackFromISR() veya xQueueSendToFrontFromISR() fonksiyonunu kullanmamız gerekir. Bunlar için daha detaylı bilgi interrupt ile ilgili notta verilecektir.

BaseType_t xQueueSendToFront( QueueHandle_t xQueue,
                              const void * pvItemToQueue,
                              TickType_t xTicksToWait );
BaseType_t xQueueSendToBack( QueueHandle_t xQueue,
                             const void * pvItemToQueue,
                             TickType_t xTicksToWait );
  • xQueue: Kuyruk oluşturulurken kullanılan xQueueCreate() fonksiyonunun döndürdüğü handle parametresi.
  • pvItemToQueue: Kuyruğa eklenecek olan veri. Bu verinin boyutu kuyruk oluşturulurken tanımlanmıştır.
  • xTicksToWait: Kuyruk dolu iken ne kadar süre bekleneceğinin girildiği parametre. Blok süresi burada tick periyoduna göre belirlenir. Yani bekleme süresi tick frekansına göre değişiklik gösterir. Bu parametre yerine portMAXDELAY girilebilir. Bu parametre girildiğinde eğer FreeRTOSConfig.h dosyası içerisindeki INCLUDE_vTaskSuspend  1 olarak ayarlanmış ise portMAXDELAY süresiz olarak beklemek anlamına gelir.
  • Return Değeri: Eğer veri kuyruğa yazılmış ise pdPASS , kuyruk dolu ise errQUEUE_FULL değeri döndürülür.

Kuyruktan Veri Okuma

    Kuyruktan veri okumak için xQueueReceive() fonksiyonu kullanılabilir. Bu fonksiyon kuyruktan veri okuduktan sonra okuduğu veriyi kuyruktan siler. Eğer interrupt fonksiyonu içerisinden kuyruktan veri okunacak ise xQueueReceiveFromISR() fonksiyonunu kullanmak daha iyidir.

BaseType_t xQueueReceive( QueueHandle_t xQueue,
                          void * const pvBuffer,
                          TickType_t xTicksToWait );
  • xQueue: Kuyruk oluşturulurken kullanılan xQueueCreate() fonksiyonunun döndürdüğü handle parametresi.
  • pvBuffer: Okunan verinin tutulacağı değişken.
  • xTicksToWait: Kuyruk dolu iken ne kadar süre bekleneceğinin girildiği parametre.
  • Return Değeri: Eğer veri kuyruğa yazılmış ise pdPASS , kuyruk boş ise errQUEUE_EMPTY değeri döndürülür.
    Burada eğer xTicksToWait sıfırdan farklı ise kuyruk boş olduğunda task ,xTickToWait süresi kadar bloklanır. Başka bir task bu kuyruğa veri gönderince kuyruktan veri okuyan ve blocked state de bulunan task running state e geçer ve kuyruktaki veriyi çeker.
    Not: xQueueReceive() fonksiyonu kuyruktan veri okuduktan sonra okuduğu değeri kuyruktan siler. Eğer kuyruktaki verileri silinmeden okumak istersek  xQueuePeek() fonksiyonu kullanılabilir.

uxQueueMessagesWaiting() API Fonksiyonu

    Bu fonksiyon kuyrukta kaç tane veri olduğunu sorgulamak için kullanılır.
UBaseType_t uxQueueMessagesWaiting( QueueHandle_t xQueue );
  • xQueue: Kuyruk oluşturulurken kullanılan xQueueCreate() fonksiyonunun döndürdüğü handle parametresi.
  • Return Değeri: Kuyrukta o anda bulunan verilerin sayısını döndürür. Eğer return değeri sıfır ise kuyruk boştur.
    FreeRTOS daki Queue yapısı ile ilgili diğer fonksiyonlara buraya tıklayarak ulaşabilirsiniz.

Kuyruk Kullanımını İle İlgili Örnek

    Amaç: İki adet task ve bir adet queue oluştur. Tasklardan biri bu kuyruğa veri yazarken diğeri kuyruktan veri okusun. Kuyruktan veri okuyan task aynı zamanda okuduğu verileri UART üzerinden göndersin.

    İlk olarak FreeRTOS ile ilgili olarak aşağıdaki kütüphaneleri include edelim.

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
    Bir Queue Handle oluşturduk.
QueueHandle_t Queue;                // queue handle
    xQueueCreate() fonksiyonu ile 5 tane 16 bitlik sayı tutabilen bir kuyruk oluşturduk.
Queue = xQueueCreate(5,sizeof(uint16_t));
    Burada “if” şartı ile kuyruğun hatasız oluşturulup oluşturulmadığını sorguladık. Eğer kuyruk oluşturmada hata oluşmuş ise scheduler başlamayacaktır.
if(Queue != NULL)
  {
     xTaskCreate(SenderTask,                        // Task function
                 "Sender",                          // Task name
                 configMINIMAL_STACK_SIZE,          // Stack depth
                 NULL,                              // Task parameter
                 0,                                 // Priority
                 NULL);                             // Task handle
                            
    xTaskCreate(ReceiverTask,"Receiver",configMINIMAL_STACK_SIZE,NULL,0,NULL);
    vTaskStartScheduler();
   }
else
   {
    Uart_Print("Queue Not Created!\n");
   }
    Oluşturulan task fonksiyonları aşağıdaki gibidir. SenderTask başlangıçta 2 saniye bloklanmıştır. Buradaki amaç ReceiverTask fonksiyonun gerçekten bloklanıp bloklanmadığını görmektir. ReceiverTask fonksiyonunu incelersek, xQueueReceive(Queue,&received_value,pdMS_TO_TICKS(5000)) fonksiyonu ile kuyrukta veri yok iken taskın maksimum 5 sn süre ile bloklandığını görebiliriz. Eğer 5 sn içerisinde kuyruğa veri gelmez ise task kaldığı yerden çalışmaya devam eder ve UART üzerinden “Queue is empty!” mesajını gönderir. Eğer burada pdMS_TO_TICKS(5000) yerine portMAX_DELAY kullanılsaydı task süresiz olarak bloklanacaktı (FreeRTOSConfig.h dosyasındaki INCLUDE_vTaskSuspend  1 olduğu zaman).
void SenderTask(void * argument)
{
    
    uint16_t SendValue;
    vTaskDelay(2000);
    for(int i=1;i<=10;i++)
    {
        SendValue = i*10;
        xQueueSendToBack(Queue,&SendValue,0);
    }
  /* Infinite loop */
  for(;;)
  {
    
  }
}
void ReceiverTask(void * argument)
{
    BaseType_t status;
    uint16_t received_value;
    char string[50];
  /* Infinite loop */
  for(;;)
  {
    status = xQueueReceive(Queue,&received_value,pdMS_TO_TICKS(5000));
        if(status == pdPASS)
        {
            sprintf(string,"Recived Value: %d\n",received_value);
            Uart_Print(string);
        }
        else
        {
            Uart_Print("Queue is empty!\n");
        }
  }
}
    SenderTask içerisinde bir for döngüsü ile bir kereye mahsus kuyruğa 10 adet veri yazılmaya çalışılmıştır. Kuyruk uzunluğu 5 olduğu için sadece 50 ye kadar olan sayıları tutabilecektir. Bu yüzden 50 den sonra xQueueSendToBack(Queue,&SendValue,0) fonksiyonu errQUEUE_FULL değerini döndürecektir ve xTicksToWait parametresi sıfır olarak girildiği için task kuyruktan veri eksilmesini beklemeden işlemlerine devam edecektir. Bu yüzden for döngüsünün içerisindeki kodlar 10 defa çalışsa bile kuyruğa sadece 50 ye kadar olan sayılar yazılacaktır. Kuyruktaki bütün veriler ReceiverTask tarafından okunduktan sonra tekrar status = xQueueReceive(Queue,&received_value,pdMS_TO_TICKS(5000)); satırına geldiğinde task 5 saniye boyunca bloklanacaktır. Bu 5 saniye sonunda ReceiverTask kaldığı yerden çalışmaya devam edecektir. Buradaki status değişkeni kuyruk boş olduğu için errQUEUE_EMPTY olacaktır ve task fonksiyonu içerisindeki Uart_Print(“Queue is empty!\n”) kodu yürütülecektir.

    Eğer SenderTask içerisindeki xQueueSendToBack() fonksiyonu aşağıdaki gibi düzenleseydik nasıl bir sonuç alırdık?
xQueueSendToBack(Queue,&SendValue,pdMS_TO_TICKS(100));
    Bu satırda yapılan değişiklik ile SenderTask kuyruk dolduğunda maksimum 100 milisaniye bloklanacaktır. Bu 100 milisaniye dolmadan ReceiverTask kuyruktan veri çekeceği için kuyrukta yer açılıp SenderTask tarafından sonraki veri gönderilecektir. Bu örneği çalıştırdığımız zaman aşağıdaki gibi bir çıktı alırız.

    Bu örnekte görüldüğü üzere kuyruk uzunluğu 5 olsa bile 10 adet sayıyı kuyruk ile başka bir task’a gönderdik.

Kodların Tümü(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 <string.h>
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

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


/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;
QueueHandle_t Queue;                // queue handle

/* 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 SenderTask(void * argument);
void ReceiverTask(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 */


  /* USER CODE END 2 */


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

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

  /* We should never get here as control is now taken by the scheduler */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
    
    Queue = xQueueCreate(5,sizeof(uint16_t));
    
    if(Queue != NULL)
    {
    xTaskCreate(SenderTask,                        // Task function
                            "Sender",                                 // Task name
                            configMINIMAL_STACK_SIZE,      // Stack depth
                            NULL,                                     // Task parameter
                            0,                                        // Priority
                            NULL);                                 // Task handle
                            
    xTaskCreate(ReceiverTask,"Receiver",configMINIMAL_STACK_SIZE,NULL,0,NULL);
    vTaskStartScheduler();
    }
    else
    {
        Uart_Print("Queue Not Created!\n");
    }
  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 */
void SenderTask(void * argument)
{
    
    uint16_t SendValue;
    vTaskDelay(2000);
    for(int i=1;i<=10;i++)
    {
        SendValue = i*10;
        xQueueSendToBack(Queue,&SendValue,pdMS_TO_TICKS(100));
    }
  /* Infinite loop */
  for(;;)
  {
    
  }


}
void ReceiverTask(void * argument)
{
    BaseType_t status;
    uint16_t received_value;
    char string[50];
  /* Infinite loop */
  for(;;)
  {
    status = xQueueReceive(Queue,&received_value,pdMS_TO_TICKS(5000));


        if(status == pdPASS)
        {
            sprintf(string,"Recived Value: %d\n",received_value);
            Uart_Print(string);
        }
        else
        {
            Uart_Print("Queue is empty!\n");
        }
        
  }
}
/* USER CODE END 4 */


/* StartDefaultTask function */

/**
  * @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 Queue Kullanımı

    CubeMX ile kuyruk oluşturmak istediğimizde aşağıdaki gibi bir pencere açılır. Bu pencerede bizim için önemli olan ilk üç parametredir. Queue Name kısmına istenilen isim verilebilir. Burada verdiğimiz isim handle oluşturmada kullanılır. İsmin sonuna “Handle” eklenerek CubeMX tarafından oluşturulur. Örneğin; Queue Name = queue1 ise Queue handle   queue1Handle şeklinde olur. CubeMx teki diğer ayarlar bir önceki yazıdaki ayarlar ile aynıdır. O yüzden yazıyı fazla uzatmadan o kısımları atlıyorum.

    CMSIS-RTOS da queue handle oluşturmak için osMessageQId kullanılır.

osMessageQId myQueueHandle;
    osMessageQDef fonksiyonu ile kuyruğun boyutu ve değişken tipi tanımlanır.
osMessageQDef(name, queue_sz, type)
    Burada name yerine kuyruğa verilecek isim yazılır. CubeMX ile bunu myQueue yaptık. Kuyruk ve kuyrukta bulunan her değişkenin boyutu FreeRTOS ile olan örnekteki gibi tanımlanmıştır. Kuyruk yapısı ile sadece integer değil char tipinde değişkenlerde gönderilebilir. Hatta string bile gönderilebilir.
osMessageQDef(myQueue, 5, uint16_t);
    osMessageCreate fonksiyonu ile kuyruk oluşturulur. Bu fonksiyonun aldığı parametreler aşağıdaki gibidir.
osMessageQId osMessageCreate (const osMessageQDef_t *queue_def, osThreadId thread_id)
    Bu fonksiyon bir handle döndürür ve aşağıdaki gibi kullanılabilir. Ayrıca bu fonksiyonun ilk parametresi osMessageQ(myQueue) şeklinde girilmelidir. Buradaki thread_id parametresi ile ilgili bir örnek bulamadım. Tahminime göre bu parametre yerine osThreadCreate fonksiyonu kullanılarak bir task oluşturulabilir. Bu parametrenin genellikle NULL olarak girildiğini gördüm.
myQueueHandle = osMessageCreate(osMessageQ(myQueue), NULL);

     Kuyruğa veri yazan ve veri okuyan tasklar aşağıdaki tanımlanmıştır.

void Sender(void const * argument)
{
  /* USER CODE BEGIN 5 */
  /* Infinite loop */
    uint16_t SendValue;
    osDelay(2000);
    for(int i=1;i<=10;i++)
    {
        SendValue = i*10;
        osMessagePut(myQueueHandle,SendValue,0);
    
    }
  for(;;)
  {
    
  }
  /* USER CODE END 5 */
}

/* Receiver function */
void Receiver(void const * argument)
{
  /* USER CODE BEGIN Receiver */
  /* Infinite loop */
    osEvent message;
    char string[50];
  for(;;)
  {
    message = osMessageGet(myQueueHandle,5000);
        if(message.status == osEventMessage)
        {
            sprintf(string,"Recived Value: %d\n",message.value.v);
            Uart_Print(string);
        }
        else
        {
            Uart_Print("Queue is empty!\n");
        }
  }
  /* USER CODE END Receiver */
}
    CMSIS-RTOS da kuyruğa veri yazmak için osMessagePut() fonksiyonu kullanılabilir. Bu fonksiyonun tanımlaması cmsis_os.c dosyası içerisinde aşağıdaki gibidir.
osStatus osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec)
    Burada info parametresi 32 bitlik olsa da tanımladığımız kuyruk 16 bitlik veriler tutar. Bekleme süresi olarak doğrudan milisaniye cinsinden sayılar girebiliriz. Eğer süresiz olarak bekletmek istiyorsak “osWaitForever” yazabiliriz.

    Kuyruktan veri okumak için osMessageGet() fonksiyonu kullanılabilir. Bu fonksiyonda yine cmsis_os.c dosyası içerisinde aşağıdaki tanımıdır.

osEvent osMessageGet (osMessageQId queue_id, uint32_t millisec)
    Burada karşımıza FreeRTOS dan farklı bir yapı çıkmaktadır. osMessageGet() fonksiyonu osEvent tipinde bir değer döndürmektedir. osEvent in tanımlı olduğu yere gidersek aşağıdaki bir struct olduğunu görürüz.
typedef struct  {
  osStatus                 status;     ///< status code: event or error information
  union  {
    uint32_t                    v;     ///< message as 32-bit value
    void                       *p;     ///< message or mail as void pointer
    int32_t               signals;     ///< signal flags
  } value;                             ///< event value
  union  {
    osMailQId             mail_id;     ///< mail id obtained by \ref osMailCreate
    osMessageQId       message_id;     ///< message id obtained by \ref osMessageCreate
  } def;                               ///< event definition
} osEvent;
    osEvent CMSIS-RTOS da sadece kuyruk için kullanılmaz. Kuyruk ile kullanırken sadece “status, v,  message_id” değişkenlerini kullanacağız.
    Burada status değişkeni kuyruktan veri çekildiğinde “osEventMessage” değerini alır. Bu değeri kontrol ederek kuyruğun boş olup olmadığını anlayabiliriz. Kuyruktaki verinin değerine ulaşmak istersek “message.value.v” şeklinde çekebiliriz.
    Bu örnekte de kuyruk kullanımı bir öncekine göre aynıdır. CMSIS-RTOS da şöyle bir şey farkettim. osMessagePut() fonksiyonuna “millisec” parametresini sıfır girmeme rağmen kuyruğa 100 e kadar olan sayıları yazdı. FreeRTOS da kuyruk dolduğu için 50 ye kadar olan sayıları kuyruğa ekleyebilmiştik. CMSIS-RTOS da her nedense bu böyle olmadı.
    CMSIS-RTOS daki Queue ile ilgili diğer fonksiyonlar hakkında daha fazla bilgi için buraya tıklayabilirsiniz.

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),1000)
/* USER CODE END Includes */


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


osThreadId SenderTaskHandle;
osThreadId ReceiverTaskHandle;
osMessageQId myQueueHandle;


/* 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 Sender(void const * argument);
void Receiver(void const * argument);


/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
void SenderTask(void * argument);
void ReceiverTask(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 */


  /* USER CODE END 2 */


  /* 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 SenderTask */
  osThreadDef(SenderTask, Sender, osPriorityNormal, 0, 128);
  SenderTaskHandle = osThreadCreate(osThread(SenderTask), NULL);


  /* definition and creation of ReceiverTask */
  osThreadDef(ReceiverTask, Receiver, osPriorityNormal, 0, 128);
  ReceiverTaskHandle = osThreadCreate(osThread(ReceiverTask), NULL);


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


  /* Create the queue(s) */
  /* definition and creation of myQueue */
  osMessageQDef(myQueue, 5, uint16_t);
  myQueueHandle = osMessageCreate(osMessageQ(myQueue), NULL);
    
  /* 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 */


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


  /* USER CODE BEGIN 5 */
  /* Infinite loop */
    uint16_t SendValue;
    osDelay(2000);
    for(int i=1;i<=10;i++)
    {
        SendValue = i*10;
        osMessagePut(myQueueHandle,SendValue,0);
    
    }
  for(;;)
  {
    
  }
  /* USER CODE END 5 */
}


/* Receiver function */
void Receiver(void const * argument)
{
  /* USER CODE BEGIN Receiver */
  /* Infinite loop */
    osEvent message;
    char string[50];
  for(;;)
  {
    message = osMessageGet(myQueueHandle,5000);
        if(message.status == osEventMessage)
        {
            sprintf(string,"Recived Value: %d\n",message.value.v);
      Uart_Print(string);
        }
        else
        {
            Uart_Print("Queue is empty!\n");
        }
  }
  /* USER CODE END Receiver */
}


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