stm32F401 UART DMA Sourunu

Başlatan e-zeki, 17 Şubat 2021, 09:57:54

e-zeki

Merhabalar .
uart dma üzerinden veri alıyorum gelen datanın uzunluğu belli değil o yüzden line idle kesmesi oluşturdum sorunsuz çalışıyor.
Kesme ayarları:
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
HAL_UART_Receive_DMA(&huart1, (uint8_t*)receive_buff, 255);

DMA Ayarları:
    hdma_usart1_rx.Instance = DMA2_Stream2;
    hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4;
    hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_rx.Init.Mode = DMA_CIRCULAR;
    hdma_usart1_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK)

buffer boyutu normalde uint16_t tanımlı olmasına rağmen 255'in üstüne çıktığında çalışmıyor.
gelen tüm datalar 50-100 byte arasında sadece 1 tanesi 300 byte üzerinde bir burst.

buffer size'ı 255 iken  300 btye'lık mesajı almaya çalışırsam startup dosyasında aşağıdaki satıra düşüyorum ve orada kalıyor.
buffer size'ı 400 yapıyorum yine aynı şekilde aynı satıra düşüyorum.

Default_Handler PROC

                EXPORT  WWDG_IRQHandler                   [WEAK]                                        
                EXPORT  PVD_IRQHandler                    [WEAK]                      
                EXPORT  TAMP_STAMP_IRQHandler             [WEAK]         
                EXPORT  RTC_WKUP_IRQHandler               [WEAK]                     
....
....
....
DMA2_Stream7_IRQHandler                                          
USART6_IRQHandler                                                        
I2C3_EV_IRQHandler                                                          
I2C3_ER_IRQHandler                                                          
FPU_IRQHandler
SPI4_IRQHandler
           
                B       .  (BURAYA DÜŞÜYOR)

                ENDP

                ALIGN

DMA buffer size'ı 255'i geçemez mi? yoksa uart kaynaklı bir sorunla mı karşı karşıyayım?
Bilgisi olan beni yönlendirebilir mi?

Tagli

#1
DMA'nın transfer limiti 65535 transfer, ki bu transferler 8 bit olabileceği gibi 16 veya 32 bit de olabilir. Yani donanımsal olarak 255 gibi bir sınır yok, o kesin.

Programda uint8_t receive_buff[400]; gibi yeterince büyük bir buffer tanımladığını varsayıyorum.

HAL'ı hiç bilmiyorum, ancak tahminimce HAL_UART_Receive_DMA() içindeki 255 değeri ilgili DMA'nın NDTR register'ını ayarlıyor olsa gerek. Bu register normalde byte değil transfer sayısını belirliyor ama sen yine de foksiyonun dokümanına bak. Gerçi sende veri genişliği 8 bit, bu sebeple zaten byte sayısı ile transfer sayısı aynı şey senin durumda.

DMA'yı circular olarak tanımlamana gerek yok bence. Bunu yaptığında, senin girdiğin boyut aşıldıktan sonra DMA buffer'ın başına dönüp yazmaya devam edecek ve muhtemelen eski mesajı bozacak.

Ben DMA kullanan Modbus uygulamamda şöyle yapmıştım: Idle kesmesi gelince DMA'yı kapatıp durumu ana programa bir bayrak ile bildiriyordum. Ama bundan önce kesme içinde USART ve DMA kesme bayraklarını temizleyip, DMA'yı yeni bir alım için tekrar kuruyordum. Buffer'ı da ana programda işliyordum.

Daha önce STM32F407 için yazdığım Modbus kodunun ilgili kesme bölümü aşağıda (olduğu gibi kopyala yapıştır yapıyorum, içinde doğrudan ilgili olmayan şeyler de olabilir):

void USARTn_IRQHandler(void) {
    uint32_t dummy;

    // Idle detection - End of Modbus package
    if ((USARTn->SR & USART_SR_IDLE) != 0) {
        DMA_RX->CR &= ~DMA_SxCR_EN; // "Idle detected" means RX frame is completed
        if (rxBuffer[0] != slaveAddress) { // Was it for us?
            state = IDLE;
            reArmRxDMA();
        } 
        else {
            state = FRAME_COMPLETE; // Address matches
        }

        dummy = USARTn->SR; // Dummy read for IDLE clear sequence
        dummy = USARTn->DR; // Dummy read for IDLE clear sequence
    }

    // Transfer completed interrupt (USART TX, for disabling RS485 DE signal)
    if ((USARTn->SR & USART_SR_TC) != 0) {
        USARTn->CR1 &= ~USART_CR1_TCIE; // Disable TC interrupt
        USARTn->SR &= ~USART_SR_TC; // Clear TC flag
        RESET_PIN(RS485_DE);
    }
}

inline void reArmRxDMA() {
    DMA_RX->NDTR = RX_BUFFER_SIZE;
    DMA2->LIFCR |= DMA_LIFCR_CFEIF2 | DMA_LIFCR_CTCIF2 | DMA_LIFCR_CHTIF2; // Clear interrupt flags
    DMA_RX->CR |= DMA_SxCR_EN; // Enable DMA
}

Bu mesaja denk gelen ve başka ailelerden (F0, F1 gibi) işlemciler kullanan arkadaşlara bir uyarı: F4 ile F0 & F1 arasında DMA ayarlarında farklılıklar bulunur. Kodu biraz elden geçirmeden taşımanız mümkün değil.

Bu arada, debug sırasında Fault durumunun oluşup oluşmadığını bir kontrol et. Sanki tanımsız bir kesme koduna sıçrıyor gibi ama bu hatalı bir kesme aktivasyonu olabileceği gibi bir Fault durumu da olabilir.
Gökçe Tağlıoğlu

e-zeki

@Tagli hocam cevabınız için teşekkür ederim.

Alıntı yapılan: Tagli - 17 Şubat 2021, 11:44:47Programda uint8_t receive_buff[400]; gibi yeterince büyük bir buffer tanımladığını varsayıyorum.
Aynen öyle yaptım.
hem uart hem dma handlerda errorcode oluşmuyordu ama fault durumlarına akşam tekrar bakarım.
aslında dma buffer'da mesajın tamamını görüyorum 300 bytelık mesaj geldiğinde dma bufferda mesajı tam olarak görmeme rağmen line idle tetiklenmiyor ve bahsettiğiniz gibi sanırım tanımlı olmayan bir kesmeye sıçramaya çalışıyor.