STM32F103 FreeRtos PB12 Interrupt hk

M_B

Aktif Üye
Katılım
16 Şubat 2023
Mesajlar
126
Merhabalar,
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(1));:
koydugumda ise kitlenme meydana geldi.

Kesme CallBack fonksiyon icerisi:
Kod:
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:
Kod:
 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));       
  }
}

Hardware olarak:
Exti_Interrupt_Schema.png
 
Bir butonun basılmasının doğrudan kesme üretmesi, sadece o butonları işleyen, debounce eden ayrı bir devre varsa mantıklı. Çünkü bu durumda bir buton aksiyonu meydana gelir, bir kesme alırsın ve yapacağın işi yaparsın. Ama debounce edilmemiş bir butonu kesmeye bağlarsan, tamamen belirsiz zamanlama ile kesmeler alacaksın demektir. İki kesme 200 ns aralıkla gelebilir, veya kesme aktif iken ikinci kesme gelir. Bunun dışında dijital bir inputa gri alanda kalan sinyaller uygulayacaksın. Belki bu işlemci için sorun değil, ama bu kadar fazla belirsizlik varken, iyi bir kod yazıp "bu her durumda çalışır" demek çok zor.

Onun yerine bir FreeRTOS timer tamımla, mesela 1 ms de bir çağrılsın. Timer fonksiyonu da hardware'de bulunan bütün butonları işlesin, debounce etsin ve bir buton basış algıladı ise, buton bilgisini bir FreeRTOS queue içerisine koysun. Main program task da bu queue yu beklesin, veri gelince okuyup işlesin.

Benim şu anda üzerinde çalıştığım üründe bu yapıyı kullanıyorum. 15 tane buton var, 1 ms sıklıkla hepsini işliyorum, debounce ediyorum, çift tıklama, uzun basma, repeat, acceleration gibi herşeyi yapıyorum. Bütün işlem de en kötü durumda 20 μs de tamamlanıyor.
 
@taydin hocam,
Detaylı acıklama için teşekkür ederim. FreeRtos ta yeniyim. Videolarla buraya kadar gelebildim. Mantıgını kavramak icin çebelleşiyorum. Yönlendirme acısından vaktiniz doğrultusunda örnek verebilirmisiniz. Stm32f103 te FreeRtos icin TIM4 kullanıyorum.

Teşekkürler.
Mehmet
 
FreeRTOS timer'ları hardware timer'lardan bağımsız. FreeRTOS'a TIM4 ü kullan demiyorsun.

Aşağıdaki örneği inceleyebilirsin. Kod temsilidir, derlenmez, sadece konsepti anlaman icin. 1 ms de bir çalışacak "button_task" fonksiyonu tanımlıyoruz, FreeRTOS'a tanıtıyoruz ve timer'i başlatıyoruz. Fonksiyon da sırayla bütün butonların GPIO larını okuyor, debounce ediyor, durum değişim zamanlarını takip ediyor ve buradan da single press, double press, press-hold, repeat, acceleration, hepsini yapıyor. Bütün ayrıntıları koymadım çünkü dikkat dağıtır.

C:
static TimerHandle_t button_timer;

/**********************************************************************
 **********************************************************************/
static void button_task(TimerHandle_t xTimer)
{
    for (unsigned int i = 0; i < num_buttons; ++i)
    {
        BUTTON_INFO* b = &buttons[i];
        
        /* buton okuma, debounce ve diger butun islemler */
    }
}

*
*
*

    button_timer = xTimerCreate("Button Timer",
                                pdMS_TO_TICKS(1),
                                pdTRUE,
                                0,
                                button_task);
    if (button_timer == 0)
    {
        debug_log(LOGMASK_INFO, 0, "xTimerCreate failed\n");
    }

    if (xTimerStart(button_timer, 0) == pdFAIL)
    {
        debug_log(LOGMASK_INFO, 0, "xTimerStart failed\n");
    }
 
FreeRTOS'un çok ayrıntılı yazılmış bir online kitabı da var. Öncelikle onu okuman, sonra değişik videoları takip etmende yarar var.

 
  • Beğen
Reactions: M_B
Teşekkür ederim hocam.
Linkteki mesajınızı kendi buton grubuma eklemeye calısıyorum.
sanırım
Kod:
  b->curr = read_button_status(b->port);
kısımdaki read_button_status(b->port); kısmını kendi işlemcime göre ayarlamam gerekecek.
Kod:
typedef struct
{
   int port;
   int pin;
   int count;
   int prev;
   int curr;
   int status;

} BUTTON;

HAL_GPIO_ReadPin(b->port,b->pin);  // Fonsiyonun donus degeri True veya false.
Sanırım BUTTON yapısına port dısında bir de pin eklemem gerekiyor. Yukardaki ornekte kullandığım gibi.
Tabi birde
Program basında port ve pin tanımlamalarını yapmam gerekiyor.
static BUTTON buttons[NUM_BUTTONS];
Kod:
buttons[0].port=EMG_GPIO_Port;
buttons[0].pin=EMG_pin ;
buttons[0].count=3;
buttons[0].prev=0;
buttons[0].curr=0;
buttons[0].status=0;
Hocam doğru yoldamıyım.
 
Doğrudan pin değerini okuyan bir STM API vardır diye düşünüyorum. Bu durumda sadece ilgili GPIO yu temsil eden bir değer olacak struct içerisinde. Önceki (bir önceki milisaniyedeki) buton değeri ile güncel buton değeri aynı ise counteri arttıracaksın, farklı ise counteri sıfırlayacaksın. Eğer counter eşiğe ulaştı ise artık buton stabildir, o değeri kullanıp daha üst seviyeli değerlendirmeleri yaparsın (double click vs).
 
@taydin hocam yonlendirmeler icin teşekkür ederim.
Timer kısmını kurup çalıştırdım. butonları tarıyor. Debug aşamasında ise
buttons[2].status iceriklerini görebiliyorum. button yapısını Global yaptıgım ıcın diğer tasklarda erişim sağlayabiliyorum. ama ben Global degil button_task icinde tanımlayıp kuyruk mantıgını kullanarak tum butonların değerini göndermek istiyorum. Sanırım yapımda enum tanımlamam gerekecek degil mi ?
button_task fonksiyonunda count degerini yorumlayamadım tuşa kac kere veya uzun basıldıgını anlayamdım.
Yeniden ufak bir yönlendirme yapabilirmisiniz.

Teşekkürler.


Kod:
#define NUM_BUTTONS 4
#define DEBOUNCE_THR 4

typedef struct
{
   int pin;
   int count;
   int prev;
   int curr;
   int status;
} BUTTON;

   buttons[0].pin = INPUT_1_Pin;
   buttons[1].pin = INPUT_2_Pin;
   buttons[2].pin = INPUT_3_Pin;
   buttons[3].pin = EMG_Pin;
    
   buttons[3].status = 1;
   buttons[2].status = 1;
   buttons[1].status = 1;
  buttons[0].status = 1;

static void button_task(TimerHandle_t xTimer)
{
    for (unsigned int i = 0; i < NUM_BUTTONS; ++i)
   {
      BUTTON* b = &buttons[i];

      b->prev = b->curr;
            b->curr = HAL_GPIO_ReadPin(GPIOB,b->pin);
      if (b->curr == b->prev)
      {
         ++b->count;

         if (b->count >= DEBOUNCE_THR)
         {
            b->count = 0;
            b->status = b->curr;
         }
      }
      else
                    {
                         b->count = 0;
                    }               
   }
}
 
Orada count'un amacı, kaç kere aynı değeri gördüğünü saymak. Eğer çok bounce varsa (yaylı mekanik buton), eşiği yüksek tutmak ve count'u da o eşiğe kadar saydırmak lazım. Ben eşiği 10 olarak kullanıyorum. Muhtemelen abartıyorum ama şansını zorlamaya gerek yok.

Butonları bir queue ya koymak gerekiyor zaten. Tuş kaybolmaması için bu şart. Ama sorduğun soruyu anlamadım. b->status değişkeni butonun debounce edilmiş durumunu yansıtıyor. Bunun değiştiği zamanları kaydedersen sonra da bu zamanları işlersen her türlü değerlendirmeyi yapabilirsin. Tek tık, çift tık, repeat, basılı tut, basılı tutuldukça hızlandırma vs.
 
Hocam sormak istedigim , anlatmaya çalıştığım şey.
Tüm butonlar
Kod:
/*Buton sayımız */
#define NUM_BUTTONS 4
static BUTTON buttons[NUM_BUTTONS];
Şu an benim
Kod:
buttons[0].status
buttons[1].status
buttons[2].status
buttons[3].status
4 butonun durumları bu yapının içinde. Butona basılmadığı sürece 1 'deler.
Butona basınca 0 oluyor. Buraya kadar sorun yok.

Butonlar okunduğu an 4 butonun da değerini bir kerede kuyruğa koymak veya basılı olanları.

buttons[4] elamanlı bir dizi olsaydı
Kod:
    xQueueSendToBack(Queue,&buttons,pdMS_TO_TICKS(1));
şeklinde gönderebilir diye düşünüyorum denemedim.

Hocam olayın basit halini kendi içimde iyice karmaşıklığa ve kafa karıştırmaya çalışıyorum.

Özet olarak hocam okunan 4 adet butonun değerlerini nasıl kuyruğa koyabilirim.

Şöyle birşey denedim ama mantıklı gelmedi.
Kod:
    if(buttons[2].status == 0){
                            xQueueSendToBack(Queue,&buttons[2].status,pdMS_TO_TICKS(1));                           
                    }



Değerli yorum ve önerilerinizi bekliyorum.
 
Her buton aksiyonunu ayrı ayrı göndermek daha mantıklı bence. Bir BUTTON_EVENT tanımlarsın, içerisinde de basılan butonun ID'si, aksiyonu ve zamanı olabilir. Ana task bir BUTTON_EVENT çeker queue içerisinden, gerekeni yapar, sonra tekrar queue'yu beklemeye başlar.
 
Merhaba Hocam,
Tekrardan yeni bir konu açmadan buradan devam etmek istedim.
modülüm de ( test devremde ) Bir buton bir role cıkış pinim var. STM32F103 'i Sanal com port olarak ayarladım com dan gelen veriye göre role kontrolünü yapıyorum.
Butona kısa basmalı durumlarda role kontrolü sağlanıyor. Benim burada yapmak istediğim butona uzun süreli basmalar da işlem görmemesi. Bu problemi de netten bulduğum buton okuma fonksiyonu ile de çözdüm.

Kısaca program yapım :Buton fonksiyonu ana task ta çağırıyorum. Buton fonksiyon içerisin de kısa basılma durumu varsa veriyi kuyruğa atıyorum yoksa bir işlem yapmıyorum. Ana task içinde ise kuyruk sorgulaması ile veriyi alıyorum.

Buraya kadar herhangi bir buton okuma kuyruk yönetimi ile ilgili sorunum yok.
Şu an yapmak istediğim sizin yukarıdaki BUTTON yapınızla uzun süreli basılma kaç kere basılma gibi aksiyonları kavrayıp öğrenmek.
Şu an bir buton olayı ile bu işi hallede bilirsem başka uygulamalarda sizin Button yapınızı kullanmak isterim.

Şimdi kullandığım bir buton okuma fonksiyonum:
Kod:
void ButonRead(void)
{
    currentState = HAL_GPIO_ReadPin(RST_BNT_GPIO_Port,RST_BNT_Pin);

  if(lastState == true && currentState == false)
        {       
          pressedTime = xTaskGetTickCount();
            isPressing = true;
            SendValue =2;
            isLongDetected = false;
            xQueueSendToBack(Queue,&SendValue,pdMS_TO_TICKS(100)); // Kisa sureli basmada veri gönderiliyor.
        } else if(lastState == false && currentState == true)
                                    {
                                        isPressing = false;
                                      releasedTime = xTaskGetTickCount();
                                    }       
  if(isPressing == true && isLongDetected == false)
            {
                    long pressDuration = xTaskGetTickCount() - pressedTime;
                  if( pressDuration > LONG_PRESS_TIME )                     
                            {
                                isLongDetected = true;
                            }
            }
  lastState = currentState;
}

Teşekkürler.
 
İki tane sorun var burada. Birincisi, buton okuma işini ana task'ta yapıyorsun, yani butonların durumlarının okunma zamanlaması, ana task'taki diğer kodun işleme süresine bağlı olarak sürekli değişkenlik gösterecek. Buton işleme işini bir timer rutininde yapman daha mantıklı olur. Her rutine girdiğinde zamanın ne olduğunu biliyor olacaksın.

İkinci sorun, fonksiyonda herhangi bir debounce yok, doğrudan porttan okuduğun değeri kullanıyorsun. Doğrusu, buton işleme iki aşamadan oluşacak:

1) Debounce
2) Bounce'dan arındırılmış buton verisi üzerinde diğer işlemler

İkinci aşamayı bir FSM ile yaparsan daha iyi olur, ileride daha farklı atraksiyonlar istenirse, fazla bir kod değişikliği yapmadan ekleyebilirsin.
 
Hocam size ( foruma ) msj yazdıktan sonra aklıma geldi ve buton okuma kısmını daha önce önerdiğiniz gibi timer'la yaptım calıştı.

Hocam ikinci kısım yol gösterici olarak birşeyler karalaya bilirmisiniz. ( Kalıp oluşturmak acısından )
 
Eğer bir FSM ile bir butondaki bir önceki değişim durumunu (NO_CHANGE, OFF -> ON, ON -> OFF) ve bu değişimin meydana geldiği zamanı kaydedersen, güncel değişim durumunu ve güncel zamanı da biliyorsun, buradan olabilecek bütün buton kullanım kalıplarını gerçekleştirebilirsin. Bununla ilgili bir sürü değişik kod yazılabilir, hepsi de doğru olabilir. Sen bir kodu yaz, o kod üzerinden değerlendirelim.
 

Çevrimiçi üyeler

Forum istatistikleri

Konular
6,625
Mesajlar
112,564
Üyeler
2,720
Son üye
Muhenned

Son kaynaklar

Son profil mesajları

hakan8470 wrote on Dede's profile.
1717172721760.png
Dedecim bu gul mu karanfil mi? Gerci ne farkeder onu da anlamam. Gerci bunun anlamini da bilmem :gulus2:
Lyewor_ wrote on hakan8470's profile.
Takip edilmeye başlanmışım :D ❤️
Merhaba elektronik tutsakları...
Lyewor_ wrote on taydin's profile.
Merhabalar. Elektrik laboratuvarınız varsa bunun hakkında bir konunuz var mı acaba? Sizin laboratuvarınızı merak ettim de :)
Lyewor_ wrote on taydin's profile.
Merhabalar forumda yeniyim! Bir sorum olacaktı lcr meterler hakkında. Hem bobini ölçen hemde bobin direnci ölçen bir lcr meter var mı acaba?
Back
Top