İşletim Sistemi ve MCU İnterrupt

Başlatan mr.engineer, 28 Ekim 2020, 17:05:11

mr.engineer

MCU'daki interrupt handler ile işletim sistemindeki interrupt handler arasındaki fark nedir? Biri sadece yazılımsal diğeri donanımsal mı?
Mesela normal kullandığımız PC'ler deki işlemciler de bir interrupt handler'a sahip mi? Ya da bilgisayarda interrupt sadece işletim sistemi ile mi ilgili?

Bir FreeRTOS projesinde MCU'nun kendi interruptlarını kullanıyor musunuz? RTOS'a ait tasklar ve işlemcinin interrupt fonksiyonları arasında karmaşıklık olma ihtimali yüksek değil mi?  RTOS kullanırkern MCU'nun interrupt işlemlerinden uzak durmamız mı gerekir?

mufitsozen

#1
Alıntı yapılan: mr.engineer - 28 Ekim 2020, 17:05:11MCU'daki interrupt handler ile işletim sistemindeki interrupt handler arasındaki fark nedir? Biri sadece yazılımsal diğeri donanımsal mı?
Mesela normal kullandığımız PC'ler deki işlemciler de bir interrupt handler'a sahip mi? Ya da bilgisayarda interrupt sadece işletim sistemi ile mi ilgili?

Bir FreeRTOS projesinde MCU'nun kendi interruptlarını kullanıyor musunuz? RTOS'a ait tasklar ve işlemcinin interrupt fonksiyonları arasında karmaşıklık olma ihtimali yüksek değil mi?  RTOS kullanırkern MCU'nun interrupt işlemlerinden uzak durmamız mı gerekir?

Biraz kafa karisikligi olmus zannimca.
MCU daki int.handler ile isletim sistemi int.handler derken bunlarin
nasil calistigini dusunuyordunuz?
kavramsal olarak bir cizimle vb aciklayabilirmisiniz? 
Aptalca bir soru yoktur ve hiç kimse soru sormayı bırakana kadar aptal olmaz.

Tagli

İşlemci içindeki donanımsal kesmelerin de kullanılması gerekir çoğu projede. Ben donanımsal kesmeleri de bir çeşit task olarak görüyorum. Ama FreeRTOS için konuşacak olursak (belki diğerlerinde de mantık aynıdır), donanımsal kesmelerin önceliği tüm RTOS task'lerinden daha yüksektir.

Genelde tavsiye edilen şey, donanımsal kesme içinde pek oyalanmayıp, o işi halledecek bir RTOS taskına sinyal gönderip hemen kesmeyi terk etmek. Bu sinyal mesaj, semaphore veya direct task notification ile gönderilebilir. Hatta çok düşük öncelikli bir iş ise, sanırım idle task altında çalışması için bir fonksiyon pointer'ı da sıraya eklenebiliyordu.

Benim bazen bu kuralı ihlal edip tüm işi donanımsal kesmede yapıp çıktığım oluyor. Aslında kesin bir kural yok, seçeneklerin farkında olmak ve duruma göre uygun olanını seçmek gerekli.
Gökçe Tağlıoğlu

mr.engineer

Alıntı yapılan: Tagli - 29 Ekim 2020, 10:12:47İşlemci içindeki donanımsal kesmelerin de kullanılması gerekir çoğu projede. Ben donanımsal kesmeleri de bir çeşit task olarak görüyorum. Ama FreeRTOS için konuşacak olursak (belki diğerlerinde de mantık aynıdır), donanımsal kesmelerin önceliği tüm RTOS task'lerinden daha yüksektir.

Genelde tavsiye edilen şey, donanımsal kesme içinde pek oyalanmayıp, o işi halledecek bir RTOS taskına sinyal gönderip hemen kesmeyi terk etmek. Bu sinyal mesaj, semaphore veya direct task notification ile gönderilebilir. Hatta çok düşük öncelikli bir iş ise, sanırım idle task altında çalışması için bir fonksiyon pointer'ı da sıraya eklenebiliyordu.

Benim bazen bu kuralı ihlal edip tüm işi donanımsal kesmede yapıp çıktığım oluyor. Aslında kesin bir kural yok, seçeneklerin farkında olmak ve duruma göre uygun olanını seçmek gerekli.

Teşekkür ederim, kesme öncelikleri (priority) kısmında kafam karışmıştı ama anladım şimdi.

Tagli

#4
Bu arada belirtmekte fayda var: FreeRTOS ve ARM'ın kesme numaralandırma mantığı birbirlerine ters ve bu durum kafa karışıklığına sebep olabiliyor. Unutulmaması gereken birkaç nokta var.

1) FreeRTOS'ta task'ler için en düşük öncelik 0, en yüksek öncelik (configMAX_PRIORITIES - 1), ki bu değer FreeRTOSConfig.h içinde tanımlanıyor.

2) ARM Cortex M'lerin bakış açılarına göre en yüksek öncelik 0. En düşük öncelik işlemciye göre değişiyor. STM32F0'larda 3 olabilirken STM32F4'lerde 15 olabilir. Yani donanımın kesme öncelikleri için kaç bit kullandığı ile ilgili.

3) Tüm donanımsal kesmeler, tüm RTOS task'lerinden daha yüksek öncelikli.

4) Öncelik numaralarını FreeRTOS task'leri ve donanımsal kesmeler için ayrı düşünmek lazım. Yani iki ayrı grup var, numaralar grup içindeki sıralamayı belirliyor. Grup dışında 3. madde geçerli.

5) Donanımsal kesmelerin FreeRTOS fonksiyonlarını çağırabilmeleri için sahip olabilecekleri maksimum öncelik FreeRTOSConfig.h içinde configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY ile tanımlanmış. Mesela bu değer 5 ise, ben NVIC_SetPriority() ile 5 veya daha büyük bir değer (5, 6, 7 vs.) ayarlamam lazım ki ISR içinde FreeRTOS fonksiyonlarını kullanabileyim. Eğer daha yüksek öncelik verirsem (0, 1, 2, 3, 4) o zaman kesme kodunun içinde FreeRTOS fonksiyonlarını çağıramam.

6) Yukarıdaki örnek üzerinden gidersek, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY değerinden daha düşük öncelikli ISR'ler (5, 6, 7 vs.) FreeRTOS kernel'i tarafından kesilebilir. Ama daha önce de dediğim gibi, araya task falan giremez.
Gökçe Tağlıoğlu

M_B

Merhabalar,
Yeni konu acmadan sorunumu burdan sormak istedim.
STM32F103C8T6 mcu FreeRtos kullanarak PB12 Interrupt kesmesiyle BtnCnt değerimi bir artırarak işlem yapmaya çalışıyorum. Program kesmeye giriyor ama debounce nedeniyle BtnCnt değeri bir bir artmıyor. Bunun onune nasıl gecebilirim.
FreeRtos kullanmasaydım HAL_GetTick() fonksiyonu ile gecikme saglayabilirdim.

FreeRrosta nasıl yapabilirim bilmiyorum. Kesme icine denemek icin

vTaskDelay(pdMS_TO_TICKS(20));:

koydugumda ise kitlenme meydana geldi.

Kesme CallBack fonksiyon icerisi:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{   
 BaseType_t xHigherPriorityTaskWoken = pdFALSE;     
    
        if(GPIO_Pin == EMG_Pin) 
        {           
                BtnCnt++;
                if(BtnCnt>=3){BtnCnt=0;}
                xSemaphoreGiveFromISR(BinarySem,&xHigherPriorityTaskWoken);    // Give semaphore from ISR     
                __HAL_GPIO_EXTI_CLEAR_IT(EMG_Pin);     
                portYIELD_FROM_ISR(xHigherPriorityTaskWoken); 
        }   
}

FreeRtos kurulum yapım:

BinarySem = xSemaphoreCreateBinary();   
    
    xTaskCreate(TaskLedFlash, "Led Flash",configMINIMAL_STACK_SIZE,NULL,0,NULL);  // priority 0
    xTaskCreate(HandlerTask,"Task 2",configMINIMAL_STACK_SIZE,NULL,1,NULL);      // priority 1
    xTaskCreate(TaskDsply,"Oled disply",configMINIMAL_STACK_SIZE,NULL,2,NULL);  // priority 2
    
    vTaskStartScheduler();

    void TaskLedFlash(void * argument)
{
  for(;;)
            {
                led_toggle();           
                vTaskDelay(pdMS_TO_TICKS(500));         
            }
}

void HandlerTask(void * argument)
{   
    for(;;)
    {
            xSemaphoreTake(BinarySem,portMAX_DELAY);        // If semaphore is not available, the task waits for the maximum time in the blocked state.
    }
}

void TaskDsply(void * argument)
{ 
 uint16_t sayi;
  for(;;)
  {
                if(BtnCnt==1){ BtnCnt++; sayi=0;}         // Saymaya baslangic adimi.   
                if(BtnCnt==2)
                             {
                                sayi++;        // saymaya basladi.                                                                                                                               
                            }             
                if(BtnCnt==3){
                                                
                                BtnCnt=0;   // Sayma durduruldu. BtnCnt =0 ilk deger
                             }                                                                                   
                sprintf(txt, "%d ",(uint16_t) sayi);       
                ssd1306_SetCursor(10, 20);       
                ssd1306_WriteString(txt, Font_11x18, White);   
                ssd1306_UpdateScreen();           
                vTaskDelay(pdMS_TO_TICKS(1));       
  }
}
İmkanın sınırlarını görmek için imkansızı denemek lazım.                                                             Fatih Sultan Mehmet

Tagli

Yakın zamanda bu mesajımda da dile getirdiğim üzere normal şartlarda butonları kesmeye bağlamak doğru bir tasarım değil. Bir task, bir soft timer veya bir donanımsal timer kesmesi butonları periyodik olarak okumalı ve sonra okuduğu değerler arasında oy birliğine bakmalı. FreeRTOS söz konusu olduğunda task veya soft timer ile okumak belki yavaş kalabilir, çünkü genelde FreeRTOS'ta 100 Hz'den yüksek tick frekansı önerilmiyor ve bunun sağlayacağı 10 ms çözünürlük belki yetersiz kalabilir. Denemek lazım. 1 ms'de bir okuma yapmak için donanımsal timer kesmesi kullanılmalı. Elde edilecek sonuçlar bir atomic değişkenle veya bir FreeRTOS queue ile ana programa aktarılabilir.

İstisnai durum olarak butona basılarak derin uykudan uyanma verilebilir. Böyle bir durumda butonun kesmeye bağlanması gerekecektir.

Bu arada donanımsal debouncing de eğer mümkünse uygulanmalı. Ancak bu butonların kesmelere bağlanmaması gerektiği konusundaki genel fikrimi değiştirmiyor. Bu durumda da yine periyodik okuma yapılmalı ama belki oy birliği şartına gerek kalmayabilir veya oylama daha kısa tutulabilir.
Gökçe Tağlıoğlu

X-Fi

#7
Alıntı yapılan: Tagli - 20 Haziran 2024, 12:10:08Ancak bu butonların kesmelere bağlanmaması gerektiği konusundaki genel fikrimi değiştirmiyor. Bu durumda da yine periyodik okuma yapılmalı ama belki oy birliği şartına gerek kalmayabilir veya oylama daha kısa tutulabilir.

Hocam tek bir çözümü yok dediğiniz gibi farklı yöntemler var task içerisinde döngüsel kontrol ve araya TaskDelay koyulabilir.

Peki bu çözüm bizden neler götürecek bir bakalım;

1) sekronizasyon ve tepki süresi, butonlarınızı hızlı okumak istediğinizde TaskDelay fonksiyonunu tick süresine kadar düşürmeniz gerekecek ki bu da kodda TaskYield gibi gereksiz/kötü çözümlere götürür.

2) Döngüsel bir task oluşturduğunuzda bu task CPU IDLE süresini kısaltarak sisteme sonsuz tekrarlı bir switching context eklendiği için gereksiz bir CPU kullanımı getirir.

3) Bu task yüksek öncelikte olmak zorundadır. Aksi durumda butonlar yakalanmayabilir. Birkaç ms de halledilecek bir buton-input işlemi için scheduler yazılmış daha az öncelikli tasklarınızın hepsi blocklanır.

4) Freertos için running de tutulan her task heap alanında en az 256 byte harcar ki bu fazladan ve gereksiz bir kaynak demek.

--------------------

Benim çözüm önerime gelecek olursak bence en doğru uygulama yöntemi binary-semaphore kulanmak olacaktır,
interrupt içerisinde bir semaphore gönderilerek block halindeki task tetiklenmelidir.
 
Semaphore tetiklendikten sonra bir TaskDelayUntil() fonksiyonu ile buton yeniden kontrol edilir. Eğer ki hardware tarafı low-pass filtre desteklemiyorsa birbirini blocklamayacak şekilde her bir buton için ayrı ayrı timer doğrulaması kurulmalı periyodik kontrol eklenmelidir.

Böyle senaryolar için daha güzel bir diğer çözümde EventGroup ların TaskNotification olarak kullanılmasıdır. Toplam input sayısı 32 adedi geçmiyorsa EventGrouplar kullanılabilir.
 
Bu fonksiyonlar semaphore aksine 32 bitlik bir değişken üzerinden maskeleme yöntemi ile kontrol ediliyor ve sistemin performans maliyetini önemli derecede azaltıyor.

https://www.freertos.org/RTOS_Task_Notification_As_Event_Group.html

Sözün özü birşey HW ile yapılabiliyorsa yapılmalı özellikle Uart, I2C, gibi donanımların interrupt okumaları counting-semaphore ile yazılmalı.

Bu bir kural değildir ancak RTOS ile donanımları uyumlu ve kesintisiz bir şekilde çalıştırmak istiyorsanız gereklidir.

Gömülü sistemlerde buton okuma basit bir if kontrolü değildir mimariden donanıma kadar uzanır yanlış başlarsa yanlış gider.


http://www.coskunergan.dev/    (Yürümekle varılmaz, lakin varanlar yürüyenlerdir.)