merhabalar arkadaşlar stm32 ile hal lib uarttan karakter uzunluğunu bilmeden nasıl alım yaparız yardımcı olurmusunuz
@k0rkunc https://programming.vip/docs/stm32-hal-library-learning-serial-idle-interrupt.html
Usart1 Global Interrupt aktif bir şekilde 1 byte olarak okunabilir;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
// User Code, SetFlag vb.
HAL_UART_Receive_IT(&huart1, &rxData, 1);
}
int main()
{
HAL_UART_Receive_IT(&huart1, &rxData,1);
while(1)
{
// Flag control, user code vb.
}
}
İşinizi görecektir.
@sımışka , ben denemedim ama bu yöntemin hızlı ve sürekli veri akışı olduğunda byte kaçırdığını okuduğumu hatırlıyorum. HAL'ın bu seri veri alımı olayı biraz sorunlu. Genel olarak söylenen, en temiz ve güvenilir yolun DMA kullanmak olduğu.
STM32'yi ilk öğrenmeye başladığım günlerde (kabaca 2017'nin başlarına denk gelir) HAL'ı araştırmış ve ilk olarak da seri port işini çözmeye çalışmıştım. Daha işin başında karşıma bu sorun çıktı, gıcık oldum, soğudum. Bir daha da HAL'a elimi sürmedim. İyi ki de öyle yapmışım.
@sımışka hocam benim de 10 gün öncesine kadar bugüne kadar kullandığım yöntem buydu fakat
@Tagli hocamın dediği gibi sürekli veri akışında aksaklıklar çıkardı. elimde bir proje var USB FAT_FS + 2 UART + I2C olunca Uartlar byte kaçırmaya başladı ben de yukarıda linkini attığım yönteme geçtim.
Dairesel tampon kullanabilirsiniz. Uart kesmesi içerisinde bir buffer doldurup ve kesme içerisinde yine buffer'dan ne kadar dolduğunu bir değişkene yazarsanız. Gelen bayt sayısından bağımsız veri almış olursunuz. Tabi buffer'ı okuduğunuzda sayacı okuduğunuz kadar azaltmanız lazım.
Bu uart kullanımının en uç noktası aslında. Ben DMA,timer ve iki buffer kullanıyorum. Literatürdeki adı "head and tail" diye geçiyor,aratırsan daha detaylı anlatımlar mevcut.
ilk buffer beklenen veriden 2-3 kat büyük seçilir. Tam değeri bilmeseniz de yaklaşık olarak biliyorsunuzdur.
DMA Circular olarak ayarlanır, USART IDLE interruptı tetiklendiğinde DMA CNDTR registerından ne kadar veri geldiğini görürsün. Buffer'da verinin başlangıç ve bitiş adreslerini hesaplarsın.
Ben bu aşamada DMA bufferından bağımsız olsun ve veriler düzenli olsun diye ilk bufferdaki başlangıç ve bitiş konumları bilinen veriyi ikinci bir buffer'a düzenli şekilde alıyorum, ondan sonraki işlemleri ikinci buffer üzerinde hallediyorum.
Timer'ı ise şu yüzden kullanıyorum, diyelim ki gönderen cihaz veri aktarımı bitmeden "stop" durumu yani hatalı USART IDLE interruptı oluşturacak kadar ufak bir boşluk verdi, bunu algılamak için kullanıyorum. Eğer ayarladığım süre içinde UART'ta bir veri trafiği görürsem veri aktarımının tamamlanmadığını anlayıp tekrar bir idle interrupt tetiklenmesini bekliyorum.
Aynı zamanda veri sonlarındaki "CR LF" gibi özel karakterleri takip ederek de işini iyice sağlama alabilirsin.
HAL kütüphanesinden vazgeçmek istemiyorsanız LL ve HAL kütüphanesini birlikte kullanabilirsiniz. Standart Peripheral kütüphanesinden HAL'e geçtiğimde bu durum benimde canımı sıkmıştı. Şuan HAL ile devam ettiğim projelerimde bende 1 byte lık veri alımı ile çalışıyorum ama işlem gücü israfı canımı sıkmıyor değil. Uart gibi Basit seri çevre birimlerim için LL kütüphanesi USB,Ethernet gibi yazılım katmanına daha çok ihtiyaç duyduğum çevre birimleri için ise HAL kütüphanesini birlikte kullanmak düşüncesindeyim.
Alıntı yapılan: Tagli - 17 Şubat 2021, 14:32:56STM32'yi ilk öğrenmeye başladığım günlerde (kabaca 2017'nin başlarına denk gelir) HAL'ı araştırmış ve ilk olarak da seri port işini çözmeye çalışmıştım. Daha işin başında karşıma bu sorun çıktı, gıcık oldum, soğudum.
Hocam duygularıma tercüman olmuşsunuz! Şu bayram tatilinde evde kalınca, STM32F103 geliştirme kitini PC ye takıp ufak ufak STM örneklerini kurcalamaya başladım. W5500 ile server uygulamasında nextion ekran ilave edeyim istedim. ADC GPIO işeri iyi gitti :) ekrana veri göndermekte problem yok fakat; USART kesmesi ile verileri alamadım. >:( Kesmeye gidiyor verileri okumuyor. Tekrar kesmeler çalışmıyor. İnternette bakmadığım denemediğim örnek kalmadı. Yaptığım hata muhtemelen basittir. IDE ye ve işlemciye olan yabancılığımı atamadığım için beni uğraştırmaya devam edecek gibi görünüyor. Bu yollardan daha önceleri de birkaç kez geçtiğim için ne olacağını biliyorum. İnatlaşacağız. Başka yolu yok. Zaman buldukça ,oluncaya kadar denemeye ,araştırmaya devam...
Yapmayın arkadaşlar, bu kadar da kötü bir kütüphane olamaz sanırım!
Uzun zamandır oldukça başarılı bir şekilde kullanıyorum HAL ve LL Library. Ayrıca bir sürü buffer, timer vs gerçekten gerekli mi? Verimli yol bu mu?
Elbette ihtiyaca özel durumlar oluşabilir.
Benim izlediğim yol, RXNEI IRQ 1 byte için aktifleştiriyorum. ISR içinde bu bir byte veriyi bir yere yazıyorum (Ring buffer) ve bir sayacı arttırıyorum.
Veriyi buffer içinde buffer size kadar alana koyuyorum. Buffer sonuna gittiğimde (az evvel bahsettiğim sayaç ile bunu takip ediyorum) buffer başına geri dönüp işleme devam ediyorum.
Bu veriye her nerede ihtiyacım varsa onu bir döngü ile kontrol ediyorum. Bir tür poller ile buffer durumuna bakıp içeride veri var mı diye bakıyorum.
Eğer veri geldiyse bunu byte olarak alıp işliyorum.
Veriyi RXNEI ile almak yerine DMA ile de alabilirsiniz elbette. DMA 1 byte set edilir ve transfer completed isr içinde tekrar kurarsınız. Ayrıca yukarıda bahsettiğim sayaç işlerini de yaparsınız.
PIC için kod yazarken ben de öyle yapıyordum. Sonuçta DMA falan yok, mecbur kesme ile okumak gerekiyor. Aslında çok yüksek veri akışı olmadığı durumlarda gayet makul bir yöntem.
HAL'daki sorun her byte için kesme gelmesi değil. HAL USART'ı kesme modunda kurarsan, önceden belirlenen miktarda veri geldikten sonra (senin durumunda 1 byte) HAL kütüphanesi kesmeleri kapatıyor. İşte bu çok ciddi bir sorun. Sen callback fonksiyonuna düşüp tekrar 1 byte alacak şekilde fonksiyonu çağırana kadar gelen verileri kaçırabilirsin.
Galiba doğrudan kesme koduna erişip, callback'e düşmeden HAL'ın bazı katmanlarını atlamak mümkün olabilir. Ama neden adamların kütüphanesinin içini eşeleyip iç yapısını öğrenmeye çalışayım ki? O zaman zaten HAL kullanmanın ne anlamı kalıyor? İşte o yüzden HAL'ı sevmiyorum. Çevremde duyduklarıma göre LL kütüphaneleri eski SPL'ye benziyormuş ve kulağa daha makul geliyorlar.
Bu arada, STM32 için bir kütüphane kullanıyor olsam, muhtemelen LibOpenCM3'ü tercih ederdim.
Karakter timeout ile,
gelen her karakteri bir katara diz, iki karakter arasında boşluk uzun ise değerlendir.
Örneğin 100ms boşluk varsa tüm katarı değerlendir be yeni katar dizisi için, dizmeyi başa al, karakter timeoutu bekle.
UART Interrupt+ timer0 Interrupt+ main while.
Ben
@ercan_t 'nin dediğini DMA ile yapıyorum. USART'tan gelen verileri DMA bir buffer'a aktarıyor. USART Idle Detect kesmesi, bir karakterlik bir boşluk olduğu zaman tetikleniyor. Bu kesme içinde DMA'yı kapatıyorum ve bir bayrağı kaldırıyorum. Main içinde de buffer'ı işliyorum. RTOS olsa sondaki bayrak kısmı değişir sadece.
Idle kesmesi tetiklenme süresinin bir ayarı yok maalesef, sabit 1 karakter kadar. Ancak bazı modellerde Modbus desteği adı altında, süresi ayarlanabilir bir hat sessizliği kesmesi var.