STM32 – HAL_UART TAKILMA SORUNU

STM32 Çalışmalarımda 4-5 aydan bu yana register seviyesinde programlamayı bırakıp HAL kütüphanelerini kullanmaya başladım. STM32 CubeIDE platformunu kullanıyorum.

Bu yeni ortama adapte olmak öyle bir kaç günde olmuyor. El attığım her yeni çevre birimi ve metod ile birlikte mutlaka birkaç sorunla karşılaşıyorum, bazılarının çözümü de epey vakit alıyor.

Burada ele aldığım konu aslında temel programlama tekniklerine dikkat edildiğinde kolaylıkla savuşturulabilecek bir sorun, ama insanın bazan basireti bağlanabiliyor böyle.

Yazının tamamını okumak istemeyenler için sonucu hemen buracıkta yazayım :
Eğer bir Interrupt Handler içinde iken başka Interruptları kullanıyorsanız, içerideki interruptların önceliğini daha yüksek olarak vermek gerekiyor.

Elbette böyle olacak, bunu söylemeye bile gerek yok diyebilirsiniz. Ama, ya farkında olmadan “başka interruptları” kullanmaktaysanız? Örneğin, benim yaptığım gibi, HAL_UART_Receive(), yani interruptla işi olmadığını zannettiğiniz bir fonksiyonu kullanıyorsanız?

Şunu akıldan çıkarmamak lazım; neredeyse tüm kütüphane fonksiyonları en azından SysTick kesmelerini şu ya da bu şekilde kullanıyorlar. Uyanık olmak lazım.

UART_Receive fonksiyonunda timeOut çalışmıyor !

HAL fonksiyonlarını kullandığınızda uzunluğunu önceden bilmediğiniz bir byte paketini UART üzerinden almak biraz sorunlu. İlk akla gelen yöntem, gelecek her bir karakteri tek tek algılayıp yakalamak üzere HAL_UART_Receive_IT() kesme fonksiyonunu tekrarlayarak kullanmak. Ama bu benim sorunumu çözen bir yaklaşım değil, şundan dolayı:

Gelen karakterlerin sonlandığını belirten bir sonlandırma (LF; NL vb) karakteri yok ise, bir süre bekleyip tamam artık bu kadarmış diyerek son yaptığınız HAL_UART_Receive_IT çağrınızı iptal etmeniz gerekiyor. Bunun için de bir bekleme çevrimi kurmak gerekiyor. Bunun için de karmaşık bir kodlama gerekiyor.

Neyse, ben interrupt ve polling metodlarını birlikte kullanan karma bir yöntem geliştirmeye karar verdim. Ne zaman geleceğini önceden bilmediğim ilk karakteri interrupt ile yakalayıp, bunu izleyenleri de polling yöntemi ile alıyorum. Gelen paketin sonlandığını da HAL alış fonksiyonunun timeout özelliğinden yararlanarak anlıyorum.

Ancak, bunu kodlarken bir sorunla karşılaştım, polling fonksiyonunda takılıp kaldım. HAL_UART_Receive() fonksiyonu son karakterden sonra bir türlü TimeOut olarak dönmedi, takılmaya başladı.

İlk karakteri yakalayan Interrupt Call Back Handler fonksiyonunun içinde, aşağıdaki polling satırları var:

Program yukarıdaki HAL_UART_Receive fonksiyonundan dönmüyor, halbuki 2 saniye içinde bir karakter alınmazsa dönmesi gerekiyor. Nerede takıldığını araştırınca aşağıdaki HAL fonksiyon satırlarına ulaştım:

Burada, sorunun HAL_GetTick() değerinin sabit bir değere kilitlenmesinden kaynaklandığı görülüyordu. Yani SysTick durmuş, saymıyordu. Neden durduğunu anlamak için epey uğraştıktan sonra sebebin aslında çok belirgin olduğunu gördüm.

HAL_UART_Receive() fonksiyonunu bir Interrupt Handler içinden çağırıyorum ya; bu durumda sözünü ettiğim Interrupt’ın önceliği SysTick den yüksek ya da aynı olunca SysTick kesmeleri duruyor, dolayısı ile TimeOut fonksiyonu da çalışmıyordu.

Sorunu bir kere bulduktan sonra o kadar basit görünüyor ki, insan kendisine kızıyor, nasıl oldu da bunu bu kadar geç gördüm diye. İşte basiretin bağlanması böyle oluyor.

Çözüm de aşağıdaki gibi:

Görüldüğü gibi Time Base System tick timer’in önceliğini yüksek yani “0” olarak bırakıp, bunu perdelemesi söz konusu olan USART2, DMA ve TIM3 kesmelerinin önceliklerini birer kademe düşürdüm, yani “1” yaptım. Böylece sorun giderildi. Gerçi bunlardan mutlaka gerekli olanı sadece USART2 kesmesiydi, diğerlerini deneyerek tekrar “0” a çevireceğim.

Bu sonuca ulaşmadan önce internette bir sorgulama da yapmıştım, aynı sorunla karşılaşan epey kişi vardı, forumlarda bunlara verilen onlarca yanıt bir çözüme götürmemişti. Bana ilham veren şey, mesaj dizilerinden birinde, yanıt verenlerden birisinin “genelde SysTick kesme önceliğindeki bir sorundan kaynaklanır” şeklideki bir yanıtı oldu.

Ben de burada paylaşıyorum, belki birilerinin daha işine yarar. Bir Interrupt handler içinde iken başka interruptları perdelemekte olduğunu aklından çıkartmayacaksın.

Bu yayının sonu – Selçuk Özbayraktar Haziran 2020

Leave a Reply

Your email address will not be published. Required fields are marked *