Rtos task

Başlatan mr.engineer, 08 Mayıs 2020, 17:10:38

yamak

Alıntı yapılan: mr.engineer - 09 Mayıs 2020, 00:13:58FreeRtos
Mastering the FreeRTOS™ Real Time Kernel kitap da bu yani ben anladığımı yazdım fakat burada tartıştığımız kısımla ilgili bir şey yazılmamış ama tüm kitabı okumadım.
Hocam µC/OS-III ün User's Manual'ini okumanızı tavsiye ederim.Piyasadaki,Rtos hakkında en iyi kaynaklardan birisi bu kitap bana göre.

mr.engineer

Alıntı yapılan: yamak - 09 Mayıs 2020, 00:29:24Hocam µC/OS-III ün User's Manual'ini okumanızı tavsiye ederim.Piyasadaki,Rtos hakkında en iyi kaynaklardan birisi bu kitap bana göre.

Ben herkes freeRtos u öneriyor diye freertos'a başladım ve kaynak olarak da kendi kaynaklarını kullanıyorum. Bu söylediğiniz kitapta ismi geçen "µC/OS" başka bir rtos çeşidi galiba. Şu an okuduğum kaynakda basit şekilde güzelce anlatıyor aslında ama bu dediğinizi de indireyim.
Ben bu rtos dünyasına biraz yabancıyım ama tüm rtos çeşitleri birbirine benziyor heralde:)

yamak

Hocam Micrium 10 numara RTOS dur.Bana göre FreeRtos ile karşılaştırılamaz.Source kodunun sadeliği,dökümantasyonu çok iyidir.Kod MISRA uyumludur. Ayrıca DO-178B sertifikasyonu vardır.
Bu kadar şeye rağmen bir kaç ay önce Open Source oldu.

Kendine ait bir çok stack'i de mevcut.Tcp/ip,USB,MQTT,FS ...

Github sayfası:
https://github.com/SiliconLabs?q=uC-&type=&language=


Tagli

@mufitsozen'in de dediği gibi, task öncelikleri aynı bile olsa, scheduler çalıştığı zaman mevcut task'ı durdurup ("sen artık yeterince çalıştın" diyerek) aynı öncelikteki bir başkasına zaman vermeli. Zaten kesme, blok çözülmesi vs. gibi bir sebeple daha yüksek öncelikli bir task hazır (ready) durumuna gelirse, scheduler 1 ms süreyi beklemeden devreye girip task'ı değiştirir.

Bu davranış kullanılan RTOS'a ve ayarlara göre değişebilir ama genelde böyledir.

Gökçe Tağlıoğlu

yamak

#19
Alıntı yapılan: Tagli - 09 Mayıs 2020, 08:47:24@mufitsozen'in de dediği gibi, task öncelikleri aynı bile olsa, scheduler çalıştığı zaman mevcut task'ı durdurup ("sen artık yeterince çalıştın" diyerek) aynı öncelikteki bir başkasına zaman vermeli.

  Hocam,Priorty Based Scheduling algoritmasında  böyle olmaz aslında. Eğer iki taskın öncelikleri aynı ise bir task'ın durması için event beklemesi,delay ile bekletme vs gibi bir şey ile suspend edilmesi gerekir. Aksi halde 2. task hiç çalışmaz. Fakat Round Robin gibi bir algoritma kullanılıyorsa, bu durum oluşmaz, scheduler eşit zaman aralıkları ile taskları çalıştırır.

  Yani Priority Based Scheduling kullanılırken bu gibi durumları hesaba katarak tasarım yapmak gerekir.

Edit: Yukarıda bahsettiğim şey, sistemde aynı öncelikte, sadece 2 task varsa geçerli. Eğer bu iki tasktan daha yüksek öncelik başka task(lar) varsa; scheduler ın davranışı bu şekilde olmayabilir.

Tagli

Haklısın @yamak hocam. Sanırım bahsettiğim genelleme hatalı oldu. Ancak FreeRTOS için konuşursak, varsayılan ayar benim dediğim gibi. Bunu configUSE_TIME_SLICING parametresi ile değiştirmek mümkün.

Alıntı YapconfigUSE_TIME_SLICING
By default (if configUSE_TIME_SLICING is not defined, or if configUSE_TIME_SLICING is defined as 1) FreeRTOS uses prioritised preemptive scheduling with time slicing. That means the RTOS scheduler will always run the highest priority task that is in the Ready state, and will switch between tasks of equal priority on every RTOS tick interrupt. If configUSE_TIME_SLICING is set to 0 then the RTOS scheduler will still run the highest priority task that is in the Ready state, but will not switch between tasks of equal priority just because a tick interrupt has occurred.
Gökçe Tağlıoğlu

mr.engineer

#21
Alıntı yapılan: Tagli - 09 Mayıs 2020, 13:26:06Haklısın @yamak hocam. Sanırım bahsettiğim genelleme hatalı oldu. Ancak FreeRTOS için konuşursak, varsayılan ayar benim dediğim gibi. Bunu configUSE_TIME_SLICING parametresi ile değiştirmek mümkün.


Hocam bu dediğiniz kafamı karıştırdı. Aşağıdaki iki değer de set edilmiş.Paylaştığınız açıklamaya göre her tick interrupta task değişmesi gerekiyor ama bende bu şekilde çalışmıyor. Bir task tamamen bittikten sonra diğer task'a geçiyor. Her interruptta değişse aşağıda paylaştığım gibi bir output alamam.

#define configUSE_PREEMPTION  1
#define configUSE_TIME_SLICING 1
#define configTICK_RATE_HZ              ((TickType_t)1000)

Not: UART baud rate 38400

void vTask1( void *pvParameters )
{
const char pcTaskName[] = "Task 1 is running\r\n";
const char pcTaskName2[] = "12345\r\n";
const char pcTaskName3[] = "6789\r\n";
volatile uint32_t ul; /* volatile to ensure ul is not optimized away. */
	/* As per most tasks, this task is implemented in an infinite loop. */
	for( ;; )
	{
	/* Print out the name of this task. */
		HAL_UART_Transmit(&huart2, (uint8_t*)pcTaskName, sizeof(pcTaskName),100);
		HAL_UART_Transmit(&huart2, (uint8_t*)pcTaskName2, sizeof(pcTaskName2),100);
		HAL_UART_Transmit(&huart2, (uint8_t*)pcTaskName3, sizeof(pcTaskName3),100);
		//HAL_UART_Transmit_IT(&huart2,(uint8_t*)pcTaskName, sizeof(pcTaskName)); 
	/* Delay for a period. */
		osDelay(100);
	}
}


void vTask2( void *pvParameters )
{
const char pcTaskName[] = "Task 2 is running\r\n";
volatile uint32_t ul; /* volatile to ensure ul is not optimized away. */
/* As per most tasks, this task is implemented in an infinite loop. */
for( ;; )
{
/* Print out the name of this task. */
HAL_UART_Transmit(&huart2, (uint8_t*)pcTaskName, sizeof(pcTaskName),100);
/* Delay for a period. */
osDelay(100);
}
}


Output aynen şu şekilde:
Task 1 is running
12345
6789
Task 2 is running
Task 1 is running
12345
.
.
.

Tagli

İlginç gerçekten de, ben de çözemedim bu durumu. Bu şekilde olabilmesi için task'ların 1 ms içinde tüm karakterleri yollayabilmeleri lazım ki bu da pek mümkün gözükmüyor.

HAL_UART_Transmit() içinde bir üç kağıt olabilir mi acaba?

Düşük de olsa aklıma bir ihtimal geliyor: Task'lardan biri osDelay(100) içinde bloklanmışken diğeri işini bitirebilir. Task'ların her birinin kabaca 5-10 ms'ye ihtiyacı var gibi. Zamanlama uygun şekilde denk gelirse birbirleri ile çakışmadan çalışabilirler belki.
Gökçe Tağlıoğlu

mr.engineer

Alıntı yapılan: Tagli - 09 Mayıs 2020, 18:26:01İlginç gerçekten de, ben de çözemedim bu durumu. Bu şekilde olabilmesi için task'ların 1 ms içinde tüm karakterleri yollayabilmeleri lazım ki bu da pek mümkün gözükmüyor.

HAL_UART_Transmit() içinde bir üç kağıt olabilir mi acaba?

Düşük de olsa aklıma bir ihtimal geliyor: Task'lardan biri osDelay(100) içinde bloklanmışken diğeri işini bitirebilir. Task'ların her birinin kabaca 5-10 ms'ye ihtiyacı var gibi. Zamanlama uygun şekilde denk gelirse birbirleri ile çakışmadan çalışabilirler belki.

Hocam osDelay içinde bloklanma ihtimali ile demek istediğinizi tam anlayamadım. Bana çakışmadan çalışması pek mümkün gelmedi çünkü osDelay(10) yapınca da düzgün çalışıyor. Tek değişen daha hızlı çalışması.

HAL_UART_Transmit()

Benim aklıma gelen de bu fonksiyon. Sanki bu fonksiyona girince diğer task blocklanıyor gibi duruyor.

mr.engineer

osdelay'i kaldırıp for döngüsü ile delay yaratınca da çalışıyor. Osdelay ile de ilgisi yok görünüyor.

yamak

#25
Hocam siz söyleyince baktım.Stm32 HAL fonksyionları thread safe miş.Ondan dolayı problem olmuyo.Yani HAL_UART_Transmit() _HAL_LOCK adındaki macro ile bir mutexi lock ediyo.O yüzden race condition oluşmuyor.

Edit: _HAL_LOCK un nasıl implement edilidiğine baktığımda aslında buna thread safe denemez. Yani bir mutex kullanımı söz konusu değil. Basit şekilde bi flag set ediliyor reset edilene kadar aynı uartta n başka bir transmit işlemi yapılamıyor.

__HAL_LOCK:
#if (USE_RTOS == 1)
  #error " USE_RTOS should be 0 in the current HAL release "
#else
  #define __HAL_LOCK(__HANDLE__)                                           \
                                do{                                        \
                                    if((__HANDLE__)->Lock == HAL_LOCKED)  \
                                    {                                      \
                                       return HAL_BUSY;                    \
                                    }                                      \
                                    else                                   \
                                    {                                      \
                                       (__HANDLE__)->Lock = HAL_LOCKED;    \
                                    }                                      \
                                  }while (0)

  #define __HAL_UNLOCK(__HANDLE__)                                          \
                                  do{                                       \
                                      (__HANDLE__)->Lock = HAL_UNLOCKED;   \
                                    }while (0)
#endif /* USE_RTOS */

HAL_UART_Transmit:

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
  uint16_t *tmp;
  uint32_t tickstart = 0U;

  /* Check that a Tx process is not already ongoing */
  if (huart->gState == HAL_UART_STATE_READY)
  {
    if ((pData == NULL) || (Size == 0U))
    {
      return  HAL_ERROR;
    }

    /* Process Locked */
    __HAL_LOCK(huart);

    huart->ErrorCode = HAL_UART_ERROR_NONE;
    huart->gState = HAL_UART_STATE_BUSY_TX;

    /* Init tickstart for timeout managment */
    tickstart = HAL_GetTick();

    huart->TxXferSize = Size;
    huart->TxXferCount = Size;

    /* Process Unlocked */
    __HAL_UNLOCK(huart);

    while (huart->TxXferCount > 0U)
    {
      huart->TxXferCount--;
      if (huart->Init.WordLength == UART_WORDLENGTH_9B)
      {
        if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
        {
          return HAL_TIMEOUT;
        }
        tmp = (uint16_t *) pData;
        huart->Instance->DR = (*tmp & (uint16_t)0x01FF);
        if (huart->Init.Parity == UART_PARITY_NONE)
        {
          pData += 2U;
        }
        else
        {
          pData += 1U;
        }
      }
      else
      {
        if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
        {
          return HAL_TIMEOUT;
        }
        huart->Instance->DR = (*pData++ & (uint8_t)0xFF);
      }
    }

    if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK)
    {
      return HAL_TIMEOUT;
    }

    /* At end of Tx process, restore huart->gState to Ready */
    huart->gState = HAL_UART_STATE_READY;

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

__HAL_LOCK, UART_HandleTypeDef structure ı içindeki LOCK elemanını HAL_LOCKED state ine çekiyor.Aynı UART_HandleTypeDef structure ı ile işlem yapmak isteyen başka bir task __HAL_LOCK çağırdığında zaten HAL_LOCKED olduğu için HAL_UART_Transmit fonksiyonu hiçbir şey yapmadan HAL_BUSY ile return ediyor.

MrDarK

Bu hal library derinlerinde amma da iş yapıyor:)

Desenize binary semafor kullanmışlar.

Picproje de daha fazla böyle başlıklar olmalı artık güzel sorular.
Picproje Eğitim Gönüllüleri ~ MrDarK

yamak

Hocam tam binary semaphore değil de ona benzer bi şey yapmaya çalışmışlar.Binary semaphore'da lock edilemediğinde scheduler  çağırılıp context switching yapılıyor.Ama burda sadece transmit fonksiyonundan return ediyo.Yani o taskın içinde transmitten başka bir iş varsa onları yapmaya devam eder.

Ama adamlar
#if (USE_RTOS == 1)
  #error " USE_RTOS should be 0 in the current HAL release "
kısmını yazarak ileride gerçek thread safe e geçiş yapacaklar diye anlıyorum.Şu an USE_RTOS define edilirse compile time'da error alırız.

mr.engineer

@yamak hocam size de diğer uğraşan arkadaşlara da teşekkür ediyorum. Bu sefer tam olarak anladım galiba:)
O zaman @Tagli nın da söylediği gibi her tick interrupta schedular diğer taska geçiyor ama UART'ı çalıştıramadığı için bir şey yapamıyor ve diğer task kaldığı yerden devam ediyor.

mr.engineer

Hocam aslında HAL_UART_Transmit fonksiyonunda HAL_LOCK'dan önce test edilen bir ifade daha var.

UART ilk çalıştığında gstate busy yapılıyor.

huart->gState = HAL_UART_STATE_BUSY_TX;

İkinci task tekrar UART kullanmak isterse karşısına şu test ifadesi çıkıyor ve fonksiyondan çıkmak zorunda kalıyor.

if (huart->gState == HAL_UART_STATE_READY)