STM32 UART DMA İLE UZUNLUĞU BİLİNMEYEN VERİ PAKETLERİNİ ALMAK

STM32 ile USART üzerinden veri alış verişi yapmanın çeşitli yöntemleri var. En basitinden yoklama (polling) yöntemi, biraz daha geliştirilmiş bir yöntem tercih ediyorsak kesme (interrupt) yöntemi kullanılabiliyor. Daha önce bu yöntemleri ve ring buffer kullanarak veri alış verişini anlattığım yayınlarım olmuştu.


Bu sefer, HAL kütüphanesinden özel bir fonksiyonu kullanarak DMA modunda alış için uyarladığım ring buffer tekniğini anlatacağım. Bu yeni algoritma UART DMA kesmelerinden yararlanarak CPU yu neredeyse hiç meşgul etmeden çalışan basit ve verimli bir çözüm. Bundan sonraki projelerimde bu kütüphaneyi kullanmayı düşünüyorum.

GÜNCELLEME NOTU:

18.8.2021 :
NOT-1 : DMA Alışlarda tıkanma sorununa çözüm : DMA ile alış yapmakta olan mikrodenetleyici, gönderen taraftaki bir resetleme, parazit vb. sorundan dolayı kesmelerin etkin olmadığı bir durumda kalabiliyor. Bunun neden olduğunu çözemedim. Bu durumda kesme gelmiyor, dolayısı ile IRQ callback fonksiyonu da çağırılmıyor, sonuç olarak kesmeler de tekrar etkinleşmeden kalıyor. Ancak sistemin yeniden başlatılması halinde etkin hale geliyorlar.
Çözüm olarak sysTick ile 10ms de bir eksiltilen bir sayaç “Timer3” kullanarak, son veri gelişinden bu yana belirli bir süre geçti ise kesmeleri etkinleştiren bir kod satırı ekledim.

NOT-2 : Alışlarda kullanılan port değişti. Güncelleme öncesi USART1 den alış yaparken şimdi USART2 den alış yapıyoruz. Bu nedenle proje kurulumundaki açıklamalarda Usart1 olarak görünen ayarlar Usart2 olarak dikkate alınmalıdır.

22.8.2021:
NOT-1 : Ana program içindeki test programında, alınmakta olan bir satır tamamlanmadan, yani sonlandırma karakteri gelmeden yarıda kesilirse beklemeye devam edip takılıyordu. Bunu aşmak için rxTimout adlı bir zaman aşımı kontrolu daha ekledim.

NOT-2 : Test programına periyodik olarak veri gönderen bir bölüm ekledim. Böylece hem gönderip, aynı zamanda alış yapan bir test programı oldu.

25.8.2021:

Main.c içinde Timer’ların başlangıç değer atamaları eklendi. Başka hatalar düzeltildi.

KULLANILAN TEKNİK HAKKINDA BİLGİ

Bugüne kadar kullandığım UART alış algoritmalarında kesmelerden yararlanıyordum. UART kesmelerini etkinleştirdikten sonra gelen her bir karakter yeni bir kesme oluşturuyor, paketin bittiğini ‘\n’ gibi bir sonlandırma karakterinin gelişiyle anlıyordum. Eğer sonlandırma karakteri gecikirse bunu da bir zaman aşımı kontrolu ile farkedip paketin sonlandığına karar veriyordum.
Bu yöntem, yoklama yöntemi kadar olmasa da, CPU’yu önemli ölçüde meşgul ediyor. Zira, her gelen karakterin oluşturduğu yeni bir kesme ile en azından bu karakteri bir diziye yerleştirmek gerekiyor. Bu arada yapılması gereken zaman aşımı kontrolları da CPU’yu meşgul ediyor.
Halbuki DMA (Direct memory access) yöntemini kullanarak bu işleri tamamiyle STM32 nin USART çevre aracına yaptırmak, böylece değerli CPU zamanını bu yükten kurtarmak mümkün.
Bunun için HAL kütüphanesinin HAL_UARTEx_ReceiveToIdle_DMA(…) fonksiyonundan yararlanacağız. Bu fonksiyon HAL kütühanelerine ne zaman eklendi bilmiyorum, ama ben yeni farkettim, farkettiğime çok da sevindim.
Bu çalışmamda daha önce kullanmakta olduğum ring buffer tekniğini bu fonksiyona uyarladım. Bu algoritma ile her bir karakterde gelen kesme yerine her bir paket sonladığında gelen bir kesme nedeniyle CPU çok daha az meşgul oluyor.

HAL_UARTEx_ReceiveToIdle_DMA fonksiyonu uart üzerinden gelen verinin genelde aralıksız bir seri karakter akışından oluşan paketler halinde olmasından yararlanarak paketler arasındaki boşlukları algılıyor. Bu boşluklarda uart alış hattı kısa ya da uzun sürelerle “idle” konumda yani boşta kalıyor. Bu şekilde paketin sonlandığı anlaşılarak bir alış bitti kesmesi yaratılıyor. O ana kadar gelen veri, DMA sayesinde CPU meşgul edilmeden doğrudan belleğe yazılıyor.
Kesmenin oluşumuyla CPU’yu devreye sokarak gelen paketi bir başka diziye aktarıyoruz, zira gelecek olan bir sonraki paket, bellekteki bir öncekinin üzerine yazılıyor.

Burada anlattığım uygulamanın kapsamı sadece UART alış ile sınırlı. UART gönderme için istenen HAL fonksiyonu -DMA da dahil olmak üzere- kullanılabilir. Ben genelde UART üzerinden gönderme işlemleri için kesme kullanmıyorum. 8-10 karakterden uzun paketler için yine DMA kullanmakta yarar olabilir.

PROJE KURULUMU

Bu uygulamayı geliştirmek ve test etmek için iki adet STM32F103 kiti kullanıyorum. Bunlardan birisinin görevi sadece saniyede bir 20-25 karakter uzunluğunda bir karakter dizisini UART üzerinden göndermekten ibaret. Uygulamanın kendisi alıcı tarafındaki STM32 de, bu yayında anlattıklarım da bu STM32 üzerindeki kodlara yönelik.
Geliştirme platformu olarak STM32 CubeIDE kullanıyorum. Şu anki sürümü 1.7.0. Bununla entegre olan cubeMX yapılandırıcısının sürümü ise 6.3.0.

STM32 CubeIDE PROJE YAPILANDIRMA AYARLARI

Sadece önemli ekran görüntülerini vermekle yetineceğim.

GENEL PROJE YAPISI

Bu uygulamaya yönelik kodlar uart_dma.c dosyası içinde yer alıyor. main.c içinde sadece bir test döngüsü var.

STM32 PİN ATAMALARI

Test düzeneğimde, alıcı STM32 ye bağlı bir SPI arayüzlü 320×240 ILI9341 TFT var. Pinlerden birisini ADC giriş olarak tanımladım ama bu uygulamada kullanılmıyor. Uygulamanın alış portu USART1, Pin A10 üzerinden alış yapıyoruz.

USART1 DMA AYARLARI

Usart_rx için aşağıdaki gibi bir DMA kanalı oluşturuyoruz.

NVIC AYARLARI

Dikkat : Sadece DMA kesmesi değil, USART kesmeleri de etkinleştiriliyor.

KODLAR

Her gelen paketin sonunda, yani bir seri karakter akışından sonra gelen duraksama ile bir kesme oluşuyor. Bu kesmeyi kotardığımız “call back” fonksiyonu :
HAL_UARTEx_RxEventCallback(…)
Bu fonksiyon ile gelen paketi, usart1 ya da usart2 den gelmiş olmasına göre ring_buffer1 ya da ring_buffer2 adlı dizilere kaydediyoruz. Ring buffer tekniği hakkındaki yayınıma burayı tıklayarak ulaşabilirsiniz.

Kesme fonksiyonumu USART1 ve USART2 den gelecek veriler için hazırlıklı olarak geliştirmiş de olsam her iki portun aynı anda etkin olması durumunu henüz test etmedim. Şimdilik sadece usart1 için çalıştığını, usart2 tarafının eksik olduğunu varsayabiliriz.

uart_dma.c içindeki ring buffer ve diğer değişken tanımları aşağıdaki gibi:

 #include "string.h"
 #include "inttypes.h"
 

 #ifdef STM32F103xB
 #include "stm32f1xx_hal.h"
 #elif STM32F302xC
 #include "stm32f3xx_hal.h"
 #endif
 

 #ifdef STM32F205xx
 #include "stm32f2xx_hal.h"
 #elif STM32F401XE
 #include "stm32f4xx_hal.h"
 #endif
 

 #define rxBuf_SIZE   64
 

 uint8_t rxBuf1[rxBuf_SIZE];
 uint8_t rxBuf2[rxBuf_SIZE];
 

 #define ring_buffer_SIZE 128
 

 typedef struct
 {
   unsigned char buffer[ring_buffer_SIZE];
   volatile unsigned int head;
   volatile unsigned int tail;
   volatile unsigned int bufferSize;
 } ring_buffer;
 

 /* USART1 ve USART2 için birer buffer takımı tanımlayalım */
 

 ring_buffer ring_buffer1 = { { 0 }, 0, 0,ring_buffer_SIZE};
 ring_buffer ring_buffer2 = { { 0 }, 0, 0,ring_buffer_SIZE};
 

 /* bufferlara pointerler */
 

 ring_buffer *p_ring_buffer;
 

 UART_HandleTypeDef huart1;
 UART_HandleTypeDef huart2;
 

 DMA_HandleTypeDef hdma_usart1_rx;
 DMA_HandleTypeDef hdma_usart2_rx;
 

 DMA_HandleTypeDef *hdma_usart_rx;
 

 unsigned char c;
 uint8_t usart1_flg, usart2_flg;
 

Interrupt handler fonksiyonumuz uart_dma.c içindeki Callback fonksiyonu:

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
 {   unsigned char *rxBuf;
 if (huart->Instance == USART1)
 { p_ring_buffer = &ring_buffer1;
 hdma_usart_rx = &hdma_usart1_rx;
 rxBuf = rxBuf1;
 usart1_flg = 1;
 }
 else { p_ring_buffer = &ring_buffer2;
 hdma_usart_rx = &hdma_usart2_rx;
 rxBuf = rxBuf2;
 usart2_flg = 1;
 }
 
 if (p_ring_buffer->head+Size > ring_buffer_SIZE)  // gelen veri buffer'da kalan yere sığmıyor ise
 {
 uint16_t datatocopy=ring_buffer_SIZE-p_ring_buffer->head;//kalan yeri hesapla
// verinin sığan kısmını kalan yere koyalım
memcpy ((uint8_t *)p_ring_buffer->buffer+p_ring_buffer->head, rxBuf, datatocopy);  
// Verinin kalanını buffer'ın başından itibaren kaydet
memcpy ((uint8_t *)p_ring_buffer->buffer, (uint8_t *)rxBuf+datatocopy, (Size-datatocopy));
 p_ring_buffer->head = (Size-datatocopy);  // indeksi güncelle
 }
 
 /*
  * Gelen veri buffer'daki boş alana sığıyor ise doğrudan kaydet
  */
 else
 {
 memcpy ((uint8_t *)p_ring_buffer->buffer+p_ring_buffer->head, rxBuf, Size);
 p_ring_buffer->head += Size;
 }
 
 /* DMA yı tekrar başlat */
 HAL_UARTEx_ReceiveToIdle_DMA(huart, (uint8_t *) rxBuf, rxBuf_SIZE);
/* Yarı alış kesmesi ile ilgilenmiyoruz, onu kapatalım */
 __HAL_DMA_DISABLE_IT(hdma_usart_rx, DMA_IT_HT);
 }
 
RING BUFFER’a KAYDEDİLEN VERİLERİN OKUNMASI

Kesme rutini tarafından alınan veriler ring_buffer1 ve ring_buffer2 içine kaydedilerek saklanıyor. Bunları yoklamak ve okumak için iki fonksiyonumuz var:

int Uart_read(ring_buffer *ringBuffer)
 {
   if(ringBuffer->head == ringBuffer->tail)
   {
     return -1;
   }
   else
   {
     c = ringBuffer->buffer[ringBuffer->tail];
     ringBuffer->tail = (unsigned int)(ringBuffer->tail + 1) % (unsigned int)(ringBuffer->bufferSize);
     return c;
   }
 }
 
 int IsDataAvailable(ring_buffer *ringBuffer)
 {
   return (uint16_t)(ringBuffer->bufferSize + ringBuffer->head - ringBuffer->tail) % (unsigned int)(ringBuffer->bufferSize);
 }

uint8_t check_usart_flag(uint8_t usartNo){
 if (usartNo == 1)return usart1_flg;
 else if (usartNo == 2)return usart2_flg;
 else return 0;
 }
 

Başlangıçta HAL DMA kesmelerini etkinleştirmek için aşağıdaki fonksiyonu kullanıyoruz:

void startUartDma(UART_HandleTypeDef *huart){
 unsigned char *rxBuf;
 

 if(huart->Instance==USART1)
 {hdma_usart_rx = &hdma_usart1_rx;
 rxBuf = rxBuf1;
 usart1_flg = 0;}
 else
 {hdma_usart_rx = &hdma_usart2_rx;
 rxBuf = rxBuf2;
 usart2_flg = 0;}
 
HAL_UARTEx_ReceiveToIdle_DMA(huart, rxBuf, rxBuf_SIZE);
//  ** Yarı alış kesmesini kullanmıyoruz, onu kapatalım. ** /
__HAL_DMA_DISABLE_IT(hdma_usart_rx, DMA_IT_HT);
 }

TEST FONKSİYONU

Uygulama örneği olarak tilde ‘~’ ile başlayıp yeni satır ‘\n’ karakteri ile sonlanan paketleri algılayıp görüntüleyen bir test fonksiyonumuz var. Değişik uygulamalar için daha farklı fonksiyonlar kullanıyorum. Örneğin en çok kullandığım bir alış fonksiyonu, check sum kontrolu yapanı hakkında bilgi için burayı tıklayabilirsiniz.

/*****************************************************************************
  ** uart_rx_line_IT, Interrupt ile UART uzerinden gelen bir satırı 
  ** alarak goruntuler
  ** Satır başlangıcı'~'ile,sonu da endMarker karakteri ile işaretlenmiş    
  ** olmalıdır.
  ** endMarker '\n' olmak zorunda değildir.
  **
  ** ana programda okunacak her yeni satirdan önce  *pNewLineFlag sıfırlanmalı
  ** Ana programda okunacak her yeni sattırdan önce *pUartIndex sıfırlanmalı,
  **
  ** Ayrıca CHECK SUM Byte okumaz
  *****************************************************************************/
 uint8_t uart_rx_line_IT(UART_HandleTypeDef *huart, uint8_t *buffer,
 uint8_t *pUartIndex, uint8_t bufferSize, uint8_t *pNewLineFlag, uint8_t endMarker) 
{
 uint8_t received;
 uint8_t success = 0;
 ring_buffer *ringBuffer;
 
 if(huart->Instance==USART1)
 {ringBuffer = &ring_buffer1;
  usart1_flg = 0;  // Bir sonraki kesme için bayrağı indir
}
 else
 {ringBuffer = &ring_buffer2;
  usart2_flg = 0;  // Bir sonraki kesme için bayrağı indir
}
 
 while (IsDataAvailable(ringBuffer) && (*pNewLineFlag == 0)) { // Bu while ile tilde gelmesini bekliyoruz
 received = Uart_read(ringBuffer);
 if (received == '~') {
 *pNewLineFlag = 1;
 buffer[0] = received;
 *pUartIndex = 1; // fonskiyonu çağırırken index = 0 yerine 1 koyarsak bu satıra gerek kalmaz
 break; // tilde gelince bu bloktan çık
 }
 }
 
 while (IsDataAvailable(ringBuffer) && (*pNewLineFlag == 1)) { // Tilde den sonraki karakterleri alalım
 received = Uart_read(ringBuffer);
 #ifdef displayIsLCD
  // LCD_printf(0x80, "RxC:%c           ", received);
  #else if displayIsTFT
  TFT_Printf(10, 120, 24, "RxC: %c  ", received);
  #endif
 buffer[*pUartIndex] = received;
 (*pUartIndex)++;
 
 if (*pUartIndex < bufferSize) { // Buffer taşması yok ise dizi sonunu işaretle
 buffer[*pUartIndex] = 0x00;
 if (received == endMarker) {
 #ifdef displayIsLCD
  LCD_printf(0xC0, "RxS:%s*  ", buffer);
  #else if displayIsTFT
  TFT_Printf(10, 144, 24, "RxS:%s", buffer);
  #endif
 success = 1;
 break;
 }
 } else { // '\n' gelmeden maksimum buffer boyutu aşılmış hata mesajı ile dön
 success = 2;
 break;
 }
 }
 return success;
 }

ANA PROGRAM

Ana programımızda çok ilginç birşey yok. Test çevrimini vermekle yetineceğim. Kodlar içinde epeyi ayrıntılı bilgi verdiğimi düşünüyorum.

/* USER CODE BEGIN PV */
 uint8_t result;
 uint8_t Index;
 uint8_t satirbasi;

char masterCmd[24];
uint8_t size;
uint16_t masterTxSayac;// Gönderdiğimiz mesajlara verdiğimiz sıra nosu

 /* Veri gelişinin kesildiğini algılamak için zaman aşımı sayaçları
  * Bunlar stm32f1xx_it.c içinde Systick ile 10ms de bir eksiltilen
  * uint16_t sayaçlar. Gereken kodlar stm32f1xx_it.c içinde eklenmelidir.
*/
extern volatile uint16_t Timer0, Timer1, Timer2, Timer3, txTimeout;

char UartTx[] = "~test message\n";
 
 /* USER CODE END PV */

  /* USER CODE BEGIN 0 */

#define lineBuffer_size 64
#define usart1_timeout 1000
#define usart2_timeout 1000
#define usart_IT_checkPeriod 2000
#define rxTIMEOUT 100

uint8_t lineBuffer1[lineBuffer_size];
uint8_t lineBuffer2[lineBuffer_size];

/* USER CODE END 0 */
/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  MX_USART1_UART_Init();
  MX_SPI1_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */

  TFT_Init();

    Index1 = 0;
    result1 = 0;
    satirbasi1 = 0;
 
    Index2 = 0;
    result2 = 0;
    satirbasi2 = 0;

    Timer1 = usart1_timeout;
    Timer2 = usart2_timeout;
    Timer3 = usart_IT_checkPeriod;
    txCount = 0;
    Timer0 = 100;

 #ifdef use_USART1_RX
      startUartDma(&huart1);
#endif
#ifdef use_USART2_RX
      startUartDma(&huart2);
#endif
#ifdef use_USART1_RXandUSART2_RX
      startUartDma(&huart1);
      startUartDma(&huart2);
#endif

   /* USER CODE END 2 */
 
   /* Infinite loop */
   /* USER CODE BEGIN WHILE */
 
    /* Bu çevrim içinde usart alışlarından gelen DMA kesmesi olup olmadığı 
     * sürekli olarak yoklanıyor.
     * 
     * Kesme yok ise, yani gelen veri yoksa istenen başka işlemler yapılır.
     *
     * Kesme oluştuğu takdirde bu olay, usart bayrakları check_usart_flag
     * fonksiyonu yardımı ile yoklanarak anlaşılır.
     * 
     * Kesme durumunda uart ring buffer okunmak üzere uart_rx_line_IT 
     * fonksiyonu çağrılır. Result == 0, sürecin devam ettiğini gösterir.
     * Result == 1 ise paket başarı ile okunarak lineBuffer'a aktarılmıştır.
     * 
     * Result'ın 0 ve 1 dışındaki sonuçlar hata mesajıdır. Bu durumda verinin
     * okunduğu kadarı gözardı edilir, sayaçlar ve bayraklar sıfırlanarak
     * bir sonraki paket beklenir.
     */
 
   while (1)
   {
	  if(Timer0 ==0){ // Periyodik olarak USART1 den bir komut gönder
		  sizeOfText = strlen(UartTx1);
 HAL_UART_Transmit(&huart1, (uint8_t *)UartTx1,  sizeOfText, 1000);

/* aşağıdakiler gönderilen satırı Master'ın kendi ekranında görüntülemek için.
* Gönderilen mesajın sonuna bir sıra numarası ekleyerek görüntülüyoruz.
* Aksi halde ekranda sürekli olarak aynı satır duruyor.
*/
		 strcpy(txString,UartTx1);
		 txCount++;
		 itoa(txCount,charBuffer,10);
		 TFT_ShowString(0, 0, 24, UartTx1, YELLOW, 0);
		 cursorPos = strlen(UartTx1)*12;
		 TFT_ShowString(cursorPos,0, 24, charBuffer, YELLOW, 0);

		 Timer0= 100;  // Saniyede bir göndersin
	  }


 #ifdef use_USART1_RX
   if((check_usart_flag(1) == 1)&&(rxTimeout !=0)){//Usart1 den interrupt gelmiş, birşeyler var
rxTimeout = rxTIMEOUT; // Satırın tamamlanmasını sonsuza kadar bekleme
    while(result1 == 0){
    result1 = uart_rx_line_IT(&huart1, lineBuffer1, &Index1, lineBuffer_size, &satirbasi1, '\n');
    }
   }
 #endif

 #ifdef use_USART2_RX
   if(check_usart_flag(2) == 1){//Usart2 den interrupt gelmiş, birşeyler var
rxTimeout = rxTIMEOUT; // Satırın tamamlanmasını sonsuza kadar bekleme
    while((result2 == 0)&&(rxTimeout !=0)){
    result2 = uart_rx_line_IT(&huart2, lineBuffer2, &Index2, lineBuffer_size, &satirbasi2, '\n');
    }
   }
 #endif

 #ifdef use_USART1_RXandUSART2_RX
   if((check_usart_flag(1) == 1)||(check_usart_flag(2) == 1)){//Usart1 ya da usart2 den interrupt gelmiş, birşeyler var
rxTimeout = rxTIMEOUT; // Satırın tamamlanmasını sonsuza kadar bekleme
    while((result1 == 0)&&(result2 ==0)&&(rxTimeout !=0)){ // Her iki portu da 
    result1 = uart_rx_line_IT(&huart1, lineBuffer1, &Index1, lineBuffer_size, &satirbasi1, '\n');
    result2 = uart_rx_line_IT(&huart2, lineBuffer2, &Index2, lineBuffer_size, &satirbasi2, '\n');
    }
 #endif

    if(result1 == 1){ // Usart1 den gelen paket
 
	  	/* Burada usart1 den gelen satır ile ne isteniyorsa onu yap */
	  		TFT_Printf(0,200,24,"Uart1: %s   ",lineBuffer1);

	  	/* yeni bir satır almak üzere döngüye devam et */

		Index1 = 0;
	  	result1 = 0;
	  	satirbasi1 = 0;
	  	Timer1 = usart1_timeout;
	  	Timer3 = usart_IT_checkPeriod;
    }

    if(result2 == 1){ // Usart2 den gelen paket

	  	/* Burada usart2 den gelen satır ile ne isteniyorsa onu yap */
	  		TFT_Printf(0,200,24,"Uart2: %s   ",lineBuffer2);
	  	/* yeni bir satır almak üzere döngüye devam et */

		Index2 = 0;
	  	result2 = 0;
	  	satirbasi2 = 0;
	  	Timer2 = usart2_timeout;
	  	Timer3 = usart_IT_checkPeriod;
    }

    /* Burada usart portlardan veri beklenirken ne isteniyorsa onu yap.
     * Aşağıdaki kod ile portlardan uzun süre birşey gelmemesi durumunda
     * iki işlem yapılıyor. 
     * 1- Gönderen tarafı uyarmak üzere bir komut dizisi gönderiliyor.
     * 2- Kesmeler bir şekilde kapatılmış olabilir. Kesmeleri yeniden 
     *    etkinleştiriyoruz.
     **/

if(Timer1 == 0){ // Son veri gelişinden bu yana zaman aşımı, yeniden veri iste
	  		Timer1 = usart1_timeout;
/*			masterTxSayac ++; //Gönderilen mesajın sıra nosu
	  		sprintf(masterCmd, "~wakeUp %d\n", masterTxSayac);
	  		size = sizeof(masterCmd);
	  		HAL_UART_Transmit(&huart1, masterCmd, size, 100);
	  		TFT_ShowString(0, 0, 24, masterCmd, YELLOW, 0);
*/	  	}
if(Timer2 == 0){ // Son veri gelişinden bu yana zaman aşımı, yeniden veri iste
	  		Timer2 = usart2_timeout;
/*			masterTxSayac ++; //Gönderilen mesajın sıra nosu
	  		sprintf(masterCmd, "~wakeUp %d\n", masterTxSayac);
	  		size = sizeof(masterCmd);
	  		HAL_UART_Transmit(&huart1, masterCmd, size, 100);
*/	  	}

if (Timer3 == 0) { // Uzun süredir veri gelmiyor, kesmeler etkin olmayabilir
			Timer3 = usart_IT_checkPeriod;
#ifdef use_USART1_RX
      startUartDma(&huart1);
#endif
#ifdef use_USART2_RX
      startUartDma(&huart2);
#endif
#ifdef use_USART1_RXandUSART2_RX
      startUartDma(&huart1);
      startUartDma(&huart2);
#endif
		}

	  	/* usart kesmesi olup olmadığını kontrol etmek üzere döngüye devam et */


        //  ***  farklı teknikler ***
	  	//	HAL_UART_Receive_IT(&huart1, rxBuffer, 28);
	  	//	HAL_UART_Receive(&huart1, rxBuffer, 28, 1000);
	  	__NOP();

 

     /* USER CODE END WHILE */
 

SONUÇ

Çok verimli çalışan bir alış algoritması oldu. Bir birine USART1 den veri gönderip USART2 den alacak şekilde bağlanmış iki STM32F103 modülü ile test edildi.
Modüllerden birisi RESET lendiğinde diğerinin USART ve DMA kesmeleri kapanıyor, nedenini anlayamadım. Bu nedenle bir zaman aşımı kontrolu ile uzun süre veri gelmezse DMA ve kesmeleri yeniden etkinleştiriyorum.

Bundan sonraki çalışmalarımda bunu kullanacağım.

Usart1 ve Usart2 den aynı anda veri alış veriş uygulamasını test etmem gerekiyor. Çatışma olduğundaki davranışın kontrol edilmesi gerekli.

Bu YAYININ SONU – Selçuk Özbayraktar Temmuz 2021

3 Replies to “STM32 UART DMA İLE UZUNLUĞU BİLİNMEYEN VERİ PAKETLERİNİ ALMAK”

  1. Selçuk bey merhaba
    DMA ile uzunluğu bilinmeyen uart verileri almada sorun yaşadığım için DMA gibi bir nimetten hep uzak durdum. DMA yı sadece Ekran işlemlerinde TX olarak kullandım. Bu bilgiler çok işime yarayacak. Teşekkür ederim. İyi çalışmalar.

  2. Selçuk Bey merhaba,
    ” Modüllerden birisi RESET lendiğinde diğerinin USART ve DMA kesmeleri kapanıyor, nedenini anlayamadım. ” stm32 ler resetlendiğinde ve ya enerjisi kesildiğinde uarttan \xFF veya \FE karakterlerini gönderiyor. Karşıdaki bu karakterleri algılayınca iletişimi sonlandırıyor olabilir. Bunu doğrulayacak kaynak bulamadım.
    Soruma gelecek olursak, ring buffer yazınızda bahsetmiş olduğunuz overrun sorununa ” HAL_UARTEx_ReceiveToIdle_DMA ” fonksiyonunu kullanarak çözüm bulabiliyor muyuz? GPS ten IT ile verileri alıyorum ancak bazen overrun hatası oluşuyor.

    1. Fatih Bey merhaba, yayının baş tarafında sıraladığım güncelleme ve hata ayıklamalarından sonra bu sorunlar ortadan kalktığı için nedenleri üzerinde daha fazla kafa yormadım. Zira bazı değişkenlerin başlangıç değerlerinin tanımsız bırakılması gibi önemli hatalar vardı, bunlar her türlü şaçmalamaya yol açabilirdi.
      Bu son sürümlerde “Overrun Error” da kayboldu. O nedenle üzerinde daha fazla çalışmadım, maalesef bir yorum yapamayacağım.

      Selamlarımla,

Comments are closed.