DMA'de yaşadığım garip bir sorun

Başlatan z, 24 Kasım 2015, 12:36:44

z

Garip olay şöyle gelişiyor.

DMA'yı Timer desteğiyle 100 byte veri taşıması için yapılandırıyorum.

Timer, DMA'yı dürtmeye başlıyor.
DMAdeki counter 0 olduğunda transfer bitmiş oluyor.

Bu aşamada Timer ile bir miktar daha DMA'yi dürtmeye devam ediyorum ve ardından
Timerı durduruyorum.

DMA'yı disable edip tekrar 100 data transferi yapacağımı söylüyorum.
DMA'yı enable eder etmez daha henüz Timerı çalıştırmadığım halde 1. byte derhal transfer ediliyor.
Timerı çalıştırdığımda geri kalan veriler de transfer ediliyor.
Bana e^st de diyebilirsiniz.   www.cncdesigner.com

Mucit23

Belki Timer taşma flagı aktif halde kalıyordur. Yani Siz timeri durdursanız bile en son taşma ile bilikte tetikleme çıkışı 1 olarak kalıyordur. DMA aktif olunca tetik girişi 1 olduğu için bir byte veri taşıyıp tekrar timer'dan tetik bekliyordur.
Ben olsaydım DMA'yı aktif etmeden önce Timerin ilgili Taşma bitlerini yazılımsal olarak temizlerdim.

z

Onları denedim işe yaramadı fakat benzeri bir durum oluyormuş. Biraz önce sorunu çözdüm.

DMA'yı kurmadan önce Timer'ın registerlerindeki  DMA Enable bitini 0 yapıp tekrar Enable edince sorun çözüldü.

Bana e^st de diyebilirsiniz.   www.cncdesigner.com

Mucit23

DMA'da benimde yaşadığım garip bir durum var.

STM32F7'de DCMI ile DMA'yı birlikte kullanıyorum. İkisi birlikte çalışıyorlar. Fakat iki donanımında kesmelerini aynı anda kullanamıyorum. Örneğin Sadece DMA kesmeleri aktif olunca sıkıntı yok. Ama DCMI kesmeleri aktif olunca DMA ayarları yapıldıktan sonra birşey tarafından kapatılıyor. DMA2_Stream1->CR registerinin 0. biti yani EN biti 0 oluyor. Çözemedim bu sorunu. Debug esnasında EN bitini 1 yapıp tekrar çalıştırınca sistem çalışmaya başlıyor. DMA'yı ne kapatıyor ne diye kapatıyor çözemedim.

Çözüm olarak main döngüsü içerisinde aşağıdaki gibi bir satırla DMAyı aktif tutmaya çalışıyorum.

DMA2_Stream1->CR |= 0x00000001;

Doğru bir yöntem değil elbet bu sorunun asıl nedenini bulmam lazım. Fikri olan var mı?

Klein

Hangi kesme gelince DMA kapanıyor?

Mucit23

Bu sorun sadece DCMI kesmelerini aktif edince gerçekleşiyor. DCMI kesmeleri kapalıyken DMA düzgün bir şekilde çalışıp kendi kesmelerini oluşturuyor. Bu sırada kameradan sürekli data alınıp hafızaya yazılıyor.

Şöyle bir sorun var.
Şimdi esasında kameradan bir görüntü alıyorum. Kameranın önüne koyduğum nesnelerin üzerindeki yazıları STM32F7'nin ekranından okuyabiliyorum. Kamera çalışıyor temelde fakat şöyle bir sorun var. Normalde DCMI ve DMA ikilisi sürekli kameradan data alıp benim hafızada tanımladığım bölgeye yazma yapıyor. En son bütün frame aktarıldıktan sonra DMA kesme oluşturuyor. Bende kesme oluşunca main döngü ile hafızadan kamera datalarının bulunduğu kısmı alıp "demosaicing" denilen işlemi yaparak RGB pixellerini ayırıp TFT ekranın hafızasına basıyorum. Fakat ben bu işlemlerle uğraşırken arka planda DCMI ve DMA ikilisi sürekli kameradan yeni datalar alıp hafızadaki verileri güncellemeye devam ediyor. İşte bu sebepten dolayı aldığım görüntü sürekli sola doğru kayıyor. Henüz düzgün bir görüntü elde edebilmiş değilim.

Bu yüzden DCMI kesmelerine ihtiyacım var. (VSYNC, LINE, FRAME vs vs)

Esasında benim DMA TC kesmesine ihtiyacım yok. Sadece DCMI donanımı Görüntünün tamamı gelince Frame, VSYNC gibi kesmeleri düzgün bir şekilde oluştursun istiyorum.

Normalde bu kayma sorununu çözmek içinde Frame frame işlem yapmak gerekiyor. Yani bir frame alıp kamerayı durdurmam lazım. Hafızadaki görüntü işlenip ekrana basılınca tekrardan kamerayı devreye alıp yeni bir frame tamamlanmasını beklemem lazım.

Bunun içinde Kamerayı SnapShot modunda çalıştırmam gerekiyor ama DMA'daki kapanma sorunu yüzünden bu işlemde düzgün gerçekleşmiyor. Öncelikli olarak bu sorunu çözmem gerekir.

kayma sorunu içinde aklıma şu dakika başka bir fikir geldi. Hafızada ikinci bir alan tanımlasam ve DCMI bütün görüntüyü hafızaya attıktan sonra DMA1 ile hızlı bir şekilde hafızadaki kamera datalarını yine RAM'de ikinci bir bölüme taşısam ve görüntü işleme işlemlerini oradaki veriler üzerinde yapsam problem çözülür gibi.  Ama Hafıza ikinci bir alan için yeterli gelirmi hesaplamadım.

@Klein hafıza sorunu için ne tür testler yapabilirim. Sistemi bir türlü oturtturamadım gitti..

Klein

#6
DCMI kesmelerinden hangisi gelince DMA duruyor?  herhangi biri geldiğinde mi?  Belirli bir kesme geldiğinde mi?  yoksa  kesme izni enable iken, kesme gelmese bile mi?
DMA buffer circular mı? normal mi?

Doğru olan  veriyi başka bir bölgeye taşıyıp orada işlem yapmak.   tüm veriyi Taşımak yerine  DMA hedef adresini değiştirip  yeni gelen veriyi başka bir yere kaydetmek daha hızlı olacakır.

Mucit23

Hocam debug konusu çok sıkıntılı. Ama şöyle bir durum var. Bilirsiniz. LCD-SDRAM arasında da DMA2 kullanılıyor.

Debugda  esnasında adım adım çalıştırdığımda LCD init işlemi yapıldıktan birkaç adım sonra DMA devre dışı kalıyor. Fakat ben debug esnasında DMA2_Stream1'in EN bitini aktif ettiğimde sistem çalışmaya başlıyor.
Debug konusu çok sıkıntılı. Çünkü Kamera sürekli data gönderdiği için DCMI kesmelerini aktif ettiğimde adım adım kodu çalıştırırken sürekli kesme oluşuyor. Takip etmekte zorlanıyor. Bazı yerlerde anlamsız takılmalar yaşanıyor.  Yarın sabah tekrardan ayrıntılı bir şekilde bakacağım bu konuya.

Bu arada DCMI için kullandığım DMA CIRCULAR modda çalışıyor.

Ama veriyi başka bölgede taşımak konusunda hafıza adresini değiştirmek daha iyi bir fikir. Bunu kesinlikle deneyeceğim.

Mucit23

#8
Hocam selamlar

Sistem üzerinde çalışma yapıyorum şuan. Kayma sorunu için adres değiştirme olayını yapamadım henüz birkaç sorun var. DMA da adres değiştirme işlemini yapmak için DMA hangi modda çalışmalı. Normal Moddamı çalışacak yoksa circular moddamı?

Circular mod sürekli veri akışı olacağı zaman kullanılması gerekiyor diye biliyorum. Normal modda ise DMA'ya başla dediğimiz zaman boyut kadar transfer yapıp durması gerekiyor diye biliyorum. Sizin dediğiniz adres değiştirme işlemi için DMA'nın Normal modda çalışması lazım değilmi?

Ben deniyorum şuan ama yapamadım bir türlü. DMA DCMI ile birlikte Normal modda çalışmıyor gibi. Debug'da baktığımda DMA'nın enable biti sürekli sıfır olarak görünüyor. Normal modda iken Enable bitine müdahalede edemiyorum. EN bitini elle 1 yaparsam hemen tekrar 0 oluyor.

Acaba DMA, DCMI ile kullanılacaksa sürekli Cırcular Modda olmak zorunda diye bir şart mı var?

Sizin dediğiniz adres değiştirme işlemini Nasıl yapmalıyım?

mesaj birleştirme:: 16 Mart 2016, 12:19:15

Ekleme

DMA'nın kapanması sorunu sadece LCD'yi init ettiğimde oluyor. Bu kesinleşti diyebilirim. Fakat bu haliyle bile Yani DMA kapansa bile Elle sonradan bir kere aktif edersem DMA çalışmaya devam ediyor. LCD'yi init etmesem sıkıntı yok DMA normal bir şekilde DCMI'dan veriyi alıp SDRAM'e basıyor.

Mucit23

DMA'da normal modda neden çalışmaz. Örneğin Memory to Memory kopyalama yapabileceğim bir örnek koda ihtiyacım var.

Ben ilk başta hafıza @Klein'in dediği gibi adres değiştirme olayını yapmak istedim ama çalıştıramadım Sonradan Memory to Memory işlemine baktım ama yine çalıştıramadım.
DMA1'i kopyalama işlemi için ayarlayayım dedim. Bu şekilde DMA'yı init ettim.

__HAL_RCC_DMA1_CLK_ENABLE();
	
	  /* Configure common DMA parameters */
  exdma_handle.Init.Channel             = DMA_CHANNEL_0;
  exdma_handle.Init.Direction           = DMA_MEMORY_TO_MEMORY;
  exdma_handle.Init.PeriphInc           = DMA_PINC_ENABLE;
  exdma_handle.Init.MemInc              = DMA_MINC_ENABLE;
  exdma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  exdma_handle.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;
  exdma_handle.Init.Mode                = DMA_NORMAL;
  exdma_handle.Init.Priority            = DMA_PRIORITY_HIGH;
  exdma_handle.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;         
  exdma_handle.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
  exdma_handle.Init.MemBurst            = DMA_MBURST_SINGLE;
  exdma_handle.Init.PeriphBurst         = DMA_PBURST_SINGLE; 
  
  exdma_handle.Instance = DMA1_Stream0;
  
  /* Configure the DMA stream */
  HAL_DMA_Init(&exdma_handle);


Sonrasında DMA'ya aşağıdaki kodlar start veriyorum.

HAL_DMA_Start(&exdma_handle, (uint32_t)CAMERA_START_ADRES, (uint32_t)CAMERA_BUFFER2, 0x7F80);

Fakat transfer işlemi gerçekleşmiyor. Debus esnasında hafızadan izleme yapıyorum.

Bu konuda referans alabileceğim örnek bir uygulama varmı?

yldzelektronik

@Mucit23 eğer yanlış bilmiyorsam circular mode senin ihtiyacın olan şey.Sen bir frame aldıktan sonra senin işin bitmiyor ki.Sen tekrar başka bir frame alacaksın.Ve bunu sürekli yapacaksın.Yanılıyor muyum?
Kişinin başına gelen hayır Allah'tandır. Kişinin başına gelen şer nefsindendir. Nefislerimizle kendimize zulüm ediyoruz.

Mucit23

Hocam ama şöyle birşey var. Benim DMA2 doanımı DCMI ile birlikte Circular modda çalışıyor. Burada sıkıntı yok sayılır. Benim yapmam gereken şey DMA1 donanımı ile birlikte Memory to memory kopyalama işlemi yapmak. Aslında @Klein söyledi bu iş yavaş olacak ama diğer türlü adres değiştirme işlemini yapamadım.

@Klein hocam doğrudan adres değiştirme işe yaramıyor. Bu işi nasıl yaparım?

Klein

Normal modda çalıştırabilirsin. TC kesmesi gelince adresi değiştirip devam edebilirsin.
Normal modda çalışmama sebebi , veri sayısı ile ilgili olabilir.  DMA işini bitirdiğinde  count 0 olur. Enable etsen de  çalışmaz. Çünkü aktaracağı veri sayısı 0.  Yeniden başlatmak için  aktarılacak veri sayısını ( sanırım CNT registeriydi) yeniden girmen gerek.

Mucit23

@Klein

Yaptıklarımı anlatayım belki hatamı görürsünüz. Şuanda DMA'nın çalışma modunu Normal Yapınca DMA çalışıyor gibi. En azından bir frame aktarılıyor hafızaya. Fakat DCMI ile birlikte uyumlu bir şekilde çalıştıramadım.
DMA ayarlarımı aşağıdaki gibi yapıyorum.

  /*** Configure the DMA ***/
  /* Set the parameters to be configured */
  hdma_handler.Init.Channel             = DMA_CHANNEL_1;
  hdma_handler.Init.Direction           = DMA_PERIPH_TO_MEMORY;
  hdma_handler.Init.PeriphInc           = DMA_PINC_DISABLE;
  hdma_handler.Init.MemInc              = DMA_MINC_ENABLE;
  hdma_handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  hdma_handler.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;
  hdma_handler.Init.Mode                = DMA_CIRCULAR;
  hdma_handler.Init.Priority            = DMA_PRIORITY_HIGH;
  hdma_handler.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;
  hdma_handler.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
  hdma_handler.Init.MemBurst            = DMA_MBURST_SINGLE;
  hdma_handler.Init.PeriphBurst         = DMA_PBURST_SINGLE; 

  hdma_handler.Instance = DMA2_Stream1;
	
  /* Associate the initialized DMA handle to the DCMI handle */
  __HAL_LINKDMA(hdcmi, DMA_Handle, hdma_handler);
	
  /*** Configure the NVIC for DCMI and DMA ***/
  /* NVIC configuration for DCMI transfer complete interrupt */
  HAL_NVIC_SetPriority(DCMI_IRQn, 5, 0);
  HAL_NVIC_EnableIRQ(DCMI_IRQn);  

//  /* NVIC configuration for DMA2 transfer complete interrupt */
  HAL_NVIC_SetPriority(DMA2_Stream1_IRQn, 5, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn);

  /* Configure the DMA stream */
  HAL_DMA_Init(hdcmi->DMA_Handle); 


Hem DCMI hemde DMA kesmeleri aktfi halde. Fakat DMA TC kesmesinin oluştuğunu göremedim.

DCMI ayarlarını vs yaptıktan sonra Programın başında aşağıdaki satırla DCMI ve DMA'ya start veriyorum

  HAL_DCMI_Start_DMA(&hDcmiHandler, DCMI_MODE_CONTINUOUS, (uint32_t)CAMERA_START_ADRES, 0X7F80);

Burada DCMI_Mode_Continuous terimi aklımı karıştırıyor. Normalde DMAnın çalışma modunu normal yaparak sadece 1 frame aktarılmasını sağlamıyormuyuz? Yani DCMI Continuous modunda çalışsa bile DMA 1 frame aktardıktan sonra DMA'nın durması gerekir. İşte bu durumun yarattığı problemler var.

Nedense DCMI donanımını SnapShot modunda çalıştıramıyorum.

Yukarıda vermiş olduğum HAL_DCMI_Start_DMA fonksiyonunu içeriği ise şöyle.

HAL_StatusTypeDef HAL_DCMI_Start_DMA(DCMI_HandleTypeDef* hdcmi, uint32_t DCMI_Mode, uint32_t pData, uint32_t Length)
{  
  /* Initialize the second memory address */
  uint32_t SecondMemAddress = 0;

  /* Check function parameters */
  assert_param(IS_DCMI_CAPTURE_MODE(DCMI_Mode));

  /* Process Locked */
  __HAL_LOCK(hdcmi);

  /* Lock the DCMI peripheral state */
  hdcmi->State = HAL_DCMI_STATE_BUSY;

  /* Check the parameters */
  assert_param(IS_DCMI_CAPTURE_MODE(DCMI_Mode));

  /* Configure the DCMI Mode */
  hdcmi->Instance->CR &= ~(DCMI_CR_CM);
  hdcmi->Instance->CR |=  (uint32_t)(DCMI_Mode);

  /* Set the DMA memory0 conversion complete callback */
  hdcmi->DMA_Handle->XferCpltCallback = DCMI_DMAConvCplt;

  /* Set the DMA error callback */
  hdcmi->DMA_Handle->XferErrorCallback = DCMI_DMAError;

  if(Length <= 0xFFFF)
  {
    /* Enable the DMA Stream */
    HAL_DMA_Start_IT(hdcmi->DMA_Handle, (uint32_t)&hdcmi->Instance->DR, (uint32_t)pData, Length);
  }
  else /* DCMI_DOUBLE_BUFFER Mode */
  {
    /* Set the DMA memory1 conversion complete callback */
    hdcmi->DMA_Handle->XferM1CpltCallback = DCMI_DMAConvCplt; 

    /* Initialize transfer parameters */
    hdcmi->XferCount = 1;
    hdcmi->XferSize = Length;
    hdcmi->pBuffPtr = pData;
      
    /* Get the number of buffer */
    while(hdcmi->XferSize > 0xFFFF)
    {
      hdcmi->XferSize = (hdcmi->XferSize/2);
      hdcmi->XferCount = hdcmi->XferCount*2;
    }

    /* Update DCMI counter  and transfer number*/
    hdcmi->XferCount = (hdcmi->XferCount - 2);
    hdcmi->XferTransferNumber = hdcmi->XferCount;

    /* Update second memory address */
    SecondMemAddress = (uint32_t)(pData + (4*hdcmi->XferSize));

    /* Start DMA multi buffer transfer */
    HAL_DMAEx_MultiBufferStart_IT(hdcmi->DMA_Handle, (uint32_t)&hdcmi->Instance->DR, (uint32_t)pData, SecondMemAddress, hdcmi->XferSize);
  }

  /* Enable Capture */
  DCMI->CR |= DCMI_CR_CAPTURE;

  /* Return function status */
  return HAL_OK;
}


Biraz kod kalabalığı oluyor ama işleyişi anlamaya çalışıyorum. Bu yüzden alt seviye kodlara bakıyorum sürekli. DCMI ayarlandıktan sonra HAL_DMA_Start_IT fonksiyonu ile DMA çalıştırılıyor. Başkada birşey yok.

Hocam hafıza değiştirme işlemini deneyebilmem için Frame interrupt oluşunca yeniden DMA'ya start vermem lazım. Bunun için DMA çalışma modu normal olmalı. HAL_DCMI_Start_DMA fonksiyonunda Hedef adresi değiştirmem yetmiyor. Daha doğrusu debugda DMA'nın S1M0AR registerinde en son vermiş olduğum adresleri göremiyorum.

Sorunun __HAL_UNLOCK(hdma); ve __HAL_LOCK(hdma); makrolarından kaynaklandığını düşünüyorum. Hal libraryde donanım çalışırken ayarların değişmemesi için bu tip önlemler alınmış. Ama tam olarak nasıl çalıştığını ve nasıl kullanacağımı anlayamadım.

Öte yandan HAL_DCMI_Start_DMA fonksiyonu kullanılınca zaten içeride lock ve unlock işlemleri yapılıyor olması lazım. Bu konuda fikrini varmı?

Dediğim gibi yapılmak istenenler sırasıyla şöyle

DCMI çalışınca 1 frame alınsın ve Frame kesmesi oluşsun. Kesme oluşunca ben adresi değiştirip tekrar sisteme start vereyim. Bu şekilde bir döngü kurmak istiyorum.

Klein

Önce DMA'yı kapat.  Adresini ve veri sayısını gir. Tekrar dma yı aç şeklinde bir dizilimle değiştiremiyor musun?.  Halen HAL kütüphanesine geçemediğim  olduğu için kodlarnın sadece belirli bir kısmı hakkında fikir sahibi olabiliyorum.