stm32f4 VCP DMA ile buffer göndermek

Başlatan Gökhan BEKEN, 10 Eylül 2015, 17:10:31

Gökhan BEKEN

Arkadaşlar proje bu: https://github.com/gokhanBeken/stm32_VirtualComPort_coocox
Bu projedeki gönderme fonksiyonunu DMA ile yapmak istiyorum.

Gönderme fonksiyonunda, sadece buffer dolduruluyor,
bu buffer'ın adını arattığım zaman, en son kullanıldığı yer, şu fonksiyon:
uint32_t  DCD_EP_Tx ( USB_OTG_CORE_HANDLE *pdev,
                     uint8_t   ep_addr,
                     uint8_t   *pbuf,
                     uint32_t   buf_len)
{
  USB_OTG_EP *ep;
  
  ep = &pdev->dev.in_ep[ep_addr & 0x7F];
  
  /* Setup and start the Transfer */
  ep->is_in = 1;
  ep->num = ep_addr & 0x7F;  
  ep->xfer_buff = pbuf;
  ep->dma_addr = (uint32_t)pbuf;  
  ep->xfer_count = 0;
  ep->xfer_len  = buf_len;
  
  if ( ep->num == 0 )
  {
    USB_OTG_EP0StartXfer(pdev , ep);
  }
  else
  {
    USB_OTG_EPStartXfer(pdev, ep );
  }
  return 0;
}


Sanırım bu fonksiyonda değişiklik yapmam lazım, ama emin değilim, yardım bekliyorum.
Özel mesaj okumuyorum, lütfen göndermeyin.

Klein

Bu fonksiyon hangi dosyada?
Kodlarda DMA Adresinin verildiğini görüyorum. Yapmak istediğin nedir?

Gökhan BEKEN

#2
Bu dosyada hocam: https://github.com/gokhanBeken/stm32_VirtualComPort_coocox/blob/master/usb_lib/otg/usb_dcd.c
satır no: 257
bence DMA çalışmıyor, çünkü normalde döngü içinde DMA ve USART ile gönderebildiğim bir veriyi, VCP ile gönderdiğimde döngü kilitleniyor, çünkü döngüyü zamanında bitiremediği için diğer kısımlarda sorun çıkıyor.
Bu arada benim buffer boyutum 4800 ama bu maksimum 64 byte gönderiyor sanırım. 255byte gönderiyor.
Parçalı göndermek de benim için sorun olacak.

düzeltme: buffer size değeri
Özel mesaj okumuyorum, lütfen göndermeyin.

Klein

Döngü " USB_OTG_EP0StartXfer(pdev , ep)" bu fonksiyonun içinde mi?    Diğer kısım Sadece DMA adresini verip transferi başlatıyor. Eğer  gösterdiğim rutinde döngü yoksa, dma USB tamponuna yeterince hızlı transfer gerçekleştiriyordur.   Bence bu rutin işini yapıyor.  Ancak USB donanımının sana yetişemediğini tahmin ediyorum.  Muhtemelen USB fifo daha tamponu boşaltmadan sen yenisini eklemeye çalışıyorsun. 

Gökhan BEKEN

Yok hocam döngü main fonksiyonunda dönüyor.
Şimdi denemek için aşağıdaki şekilde 2 farklı lenght vererek çalıştırdım



64byte 5,654 us
while (1)
	{
		VCP_send_buffer(vcpBuffer ,255);
		GPIO_ToggleBits(GPIOD, GPIO_Pin_13);
	}
   

255 byte 20,9 us
while (1)
	{
		VCP_send_buffer(vcpBuffer ,255);
		GPIO_ToggleBits(GPIOD, GPIO_Pin_13);
	}


DMA'lı olsa bekletmezdi diye düşünüyorum, tabi kütüphane kasten block yapmadıysa...
Özel mesaj okumuyorum, lütfen göndermeyin.

Klein

akşam işyerine gidince kodları makinaya indirip bir bakayım.  Bu kısımda DMA ile transfer yapıyor ama asıl gönderme kısmında durum değişebilir.

Klein

Kodları biraz inceledim.  Evet vcp_send kodlarında APP_Rx_Buffer dizisini doldurmak için  döngü kullanılmış.  Fakat tampon dolduktan sonra nasıl  göndermeyi nasıl başattığını bulamadım. Muhtemelen tampon dolunca kesme içinde çağırılan başka bir rutin ya da donanım işlemi başlatıyor.
 
İş eninde sonunda  DCD_EP_Tx rutinine gidiyor. Bu rutini çağıran fonksiyonu buldum ama açıkçası o fonksiyonun nerede çağrıldığını takip edemedim.

Tamponu doldurma işini DMA'ya yaptırmak istiyorsan o kolay.  Örnekler bölümündeki DMA-USART örneklerinin herhangi birini biraz modifiye etsen yeter.
Kaynak adresine göndereceğin dizinin adresini  hedef adresine de  APP_Rx_Buffer  dizisinin adresini yazıp DMA'yı başlatman yeter.

Ama ben Gönderme için vcp_send kodları yerine
uint32_t  DCD_EP_Tx ( USB_OTG_CORE_HANDLE *pdev, uint8_t   ep_addr, uint8_t   *pbuf,uint32_t   buf_len)
fonksiyonunu çağırmayı denerdim.
Bu fonksiyonu kullanabilmek için   

static uint8_t  usbd_cdc_DataIn (void *pdev, uint8_t epnum)

fonksiyonunda belki değişiklik yapman gerekebilir. Emin değilim. 
DCD_EP_Tx fonksiyonu buradan çağırılıyor. Bu fonksiyon kesme içerisinden düzenli çağırılıyor olabilir.  Eğer bu fonksiyon kesme içerisinden çağırılıyorsa çakışma yaşayabilirsin.

USB işi biraz karışık ( en azından benim için karışık). Kodlar da hayli yüklü. Bu sebeple tamamını takip tmem çok zor. Eğer çok uğraşmayayım tamponu DMA ile doldurayım yeter dersen, DMA örneklerinden birini modifiye etmeyi dene, takıldığın yerde yardımcı olurum.

Gökhan BEKEN

Teşekkür ederim hocam, güzel fikirler verdiniz.
Bufferi dma ile doldurmak işe yaramaz sanırım, çünkü benim buffer 4800 byte, ama usb'den max 255 karakter gönderilebiliyor, belki ayarlarıyla oynayarak 512'ye çıkabilir ama o da yetmez.
Biraz daha uğraşayım ben...
Özel mesaj okumuyorum, lütfen göndermeyin.

memo333

Alıntı yapılan: Gökhan BEKEN - 11 Eylül 2015, 23:30:30
Teşekkür ederim hocam, güzel fikirler verdiniz.
Bufferi dma ile doldurmak işe yaramaz sanırım, çünkü benim buffer 4800 byte, ama usb'den max 255 karakter gönderilebiliyor, belki ayarlarıyla oynayarak 512'ye çıkabilir ama o da yetmez.
Biraz daha uğraşayım ben...

şuanki uğraştığınız durum farklı ama birşey eklemek istiyorum.

usb-vcp cdc sınıfında ve IN-endpoint bulk değil. bu nedenle zaten hızınız sınırlı olacak. tavsiyem bağımsız bir bulk IN-endpoint açın ve oradan datayı dma veya buffer olarak gönderin. PC tarafı için winusb kütüphanesi yeterli olacaktır.
Gömülü Linux Notları --> http://linuxedu.xyz/

Gökhan BEKEN

stm tarafı ve pc tarafı için örnek var mı @memo333 hocam, bu projede kullanmasam bile ilerde lazım olabilir.
Özel mesaj okumuyorum, lütfen göndermeyin.

fatih6761

Alıntı yapılan: memo333 - 12 Eylül 2015, 00:24:41
şuanki uğraştığınız durum farklı ama birşey eklemek istiyorum.

usb-vcp cdc sınıfında ve IN-endpoint bulk değil. bu nedenle zaten hızınız sınırlı olacak. tavsiyem bağımsız bir bulk IN-endpoint açın ve oradan datayı dma veya buffer olarak gönderin. PC tarafı için winusb kütüphanesi yeterli olacaktır.
@memo333 hocam USB CDC'yi denerken USB FS nin sınırına yaklaştım.
İlk başta dediğiniz gibi baud rate'in kısıtlayacağını düşündüm ama linux'ta named pipe üzerinden
iletişim sağlandığından (/dev/ttyACM0 named pipe ve fifo oluyor) PC tarafında doğru ayarları yapınca rahatlıkla 1,1mbyte/sn hıza ulaştım.
Tabi ADC bu hıza çıkamadığından şu an ~330kb/sn hızda (~165 ksample/sn) tek kanallı osiloskopumsu bir cihaz olarak kullanabiliyorum STM32F4 kitini :)

memo333

Alıntı yapılan: Gökhan BEKEN - 12 Eylül 2015, 00:34:51
stm tarafı ve pc tarafı için örnek var mı @memo333 hocam, bu projede kullanmasam bile ilerde lazım olabilir.

@Gökhan BEKEN   varDI.. elim bir kaza sonucu tüm çalışmalar gitti..

@fatih6761 dün bir hata yaptımve IN endpoint BULK değil demiştim. burası yanlış.. Named olayını bilmiyorum ama sadece BULK endpoint olması ve doğru ayarlar ile istenilen hız yakalanabilir.

Madem FS'i denedin, çalıştırdın birde HS'yi denesene yaw :P   
Gömülü Linux Notları --> http://linuxedu.xyz/

fatih6761

Alıntı yapılan: memo333 - 12 Eylül 2015, 09:09:09
@Gökhan BEKEN   varDI.. elim bir kaza sonucu tüm çalışmalar gitti..

@fatih6761 dün bir hata yaptımve IN endpoint BULK değil demiştim. burası yanlış.. Named olayını bilmiyorum ama sadece BULK endpoint olması ve doğru ayarlar ile istenilen hız yakalanabilir.

Madem FS'i denedin, çalıştırdın birde HS'yi denesene yaw :P
Hocam USB yine endpointlerle çalışıyor anladığım kadarıyla bunun yanında ep'den gelen veriler linuxta fifo dosyası denen (windowsta da geçen ortak adı named pipe) bir dosya türünde saklanıyor.
Yani iki ucu açık boru gibi. End Pointten interrupt geldiğinde paket (bendekiler hep 128byte oluyor, ep boyutu bu herhalde) borunun bir ucundan dolmaya başlıyor.
Diğer uçtaki programda gelen veriyi sırayla okuyabiliyor. Bu yüzden hız kısıtı olmuyor. Yani block device olarak gelen paketler character device olan tty gibi kullanılabiliyor.

Hocam HS için iki sorunum var. Birincisi STM32F4 kitine external phy takmak gerekiyor ancak bana 480mbit/s hız lazım olmadığından ekstra kart yapmakla uğraşmadım.
İkincisi de zaten 165ksample/sn lik veriyi ekrana zor basıyorum :) USB HS full çalıştırsam çipin ADC'si sanırım 1msample/sn civarına çıkar.
PC tarfında 2 thread'li bir program yazdım. Biri tty dosyasından (named pipe aslında) veriyi okuyor,
pakete dönüştürüp magic code'un eşleşip eşleşmediğine bakıyor ve paket sağlamsa
12-bitlik verileri 0..3 volt aralığında double verilere dönüştürüp global tanımlanan queue'ya basıyor.

Diğer thread'de halihazırda ekrana basılı olan veriler bitince tetikleme için rising/falling edge testi yapıyor.
Ayara göre düşen veya yükselen bir kenar geldiğinde kuyruktan gereken miktarda veriyi çekip ekran buffer'ına basıyor.

Asıl sıkıntı da bu kuyruk ve tamponları senkron tutabilmek için 2 mutex, bir cond. variable ve 1 scoped lock kullanıyorum.
Dolayısıyla bunların yarattığı gecikme pc tarafında hızımı kısıtlıyor.
Aslında kısıtlamıyor da mantık olarak basit bir scope yazılımının işlemciyi çok meşgul etmemesi için optimize etmeye çalışıyorum.

Yani yakın süreçte FS bana fazlasıyla yeter gibi görünüyor.
Eğer talep olursa geliştirme sürecindeki yazılımı (stm32f4 + pc) paylaşırım ama pc yazılımı linuxta yazıldı windows sürümünü daha yazmadım.

memo333

Alıntı yapılan: fatih6761 - 12 Eylül 2015, 11:43:21
Hocam USB yine endpointlerle çalışıyor anladığım kadarıyla bunun yanında ep'den gelen veriler linuxta fifo dosyası denen (windowsta da geçen ortak adı named pipe) bir dosya türünde saklanıyor.
Yani iki ucu açık boru gibi. End Pointten interrupt geldiğinde paket (bendekiler hep 128byte oluyor, ep boyutu bu herhalde) borunun bir ucundan dolmaya başlıyor.
Diğer uçtaki programda gelen veriyi sırayla okuyabiliyor. Bu yüzden hız kısıtı olmuyor. Yani block device olarak gelen paketler character device olan tty gibi kullanılabiliyor.

Hocam HS için iki sorunum var. Birincisi STM32F4 kitine external phy takmak gerekiyor ancak bana 480mbit/s hız lazım olmadığından ekstra kart yapmakla uğraşmadım.
İkincisi de zaten 165ksample/sn lik veriyi ekrana zor basıyorum :) USB HS full çalıştırsam çipin ADC'si sanırım 1msample/sn civarına çıkar.
PC tarfında 2 thread'li bir program yazdım. Biri tty dosyasından (named pipe aslında) veriyi okuyor,
pakete dönüştürüp magic code'un eşleşip eşleşmediğine bakıyor ve paket sağlamsa
12-bitlik verileri 0..3 volt aralığında double verilere dönüştürüp global tanımlanan queue'ya basıyor.

http://www.beyondlogic.org/usbnutshell/usb3.shtml

http://www.beyondlogic.org/usbnutshell/usb4.shtml#Bulk

kafama takılan tek konu 128byte olayı. 64 byte olması lazım. usb-description bölümünü paylaşabilir misin?
Gömülü Linux Notları --> http://linuxedu.xyz/

fatih6761

@memo333 hocam descriptor'da 64 diyor max packet size için ama iki endpoint var gibi sanırım iki ep ile 64+64=128 oluyor diye düşünmüştüm ilk gördüğümde.

/* USB Standard Device Descriptor */
__ALIGN_BEGIN static uint8_t USBD_CDC_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_DESC] __ALIGN_END =
{
  USB_LEN_DEV_QUALIFIER_DESC,
  USB_DESC_TYPE_DEVICE_QUALIFIER,
  0x00,
  0x02,
  0x00,
  0x00,
  0x00,
  0x40,
  0x01,
  0x00,
};


/* USB CDC device Configuration Descriptor */
__ALIGN_BEGIN uint8_t USBD_CDC_CfgFSDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END =
{
  /*Configuration Descriptor*/
  0x09,   /* bLength: Configuration Descriptor size */
  USB_DESC_TYPE_CONFIGURATION,      /* bDescriptorType: Configuration */
  USB_CDC_CONFIG_DESC_SIZ,                /* wTotalLength:no of returned bytes */
  0x00,
  0x02,   /* bNumInterfaces: 2 interface */
  0x01,   /* bConfigurationValue: Configuration value */
  0x00,   /* iConfiguration: Index of string descriptor describing the configuration */
  0xC0,   /* bmAttributes: self powered */
  0x32,   /* MaxPower 0 mA */
  
  /*---------------------------------------------------------------------------*/
  
  /*Interface Descriptor */
  0x09,   /* bLength: Interface Descriptor size */
  USB_DESC_TYPE_INTERFACE,  /* bDescriptorType: Interface */
  /* Interface descriptor type */
  0x00,   /* bInterfaceNumber: Number of Interface */
  0x00,   /* bAlternateSetting: Alternate setting */
  0x01,   /* bNumEndpoints: One endpoints used */
  0x02,   /* bInterfaceClass: Communication Interface Class */
  0x02,   /* bInterfaceSubClass: Abstract Control Model */
  0x01,   /* bInterfaceProtocol: Common AT commands */
  0x00,   /* iInterface: */
  
  /*Header Functional Descriptor*/
  0x05,   /* bLength: Endpoint Descriptor size */
  0x24,   /* bDescriptorType: CS_INTERFACE */
  0x00,   /* bDescriptorSubtype: Header Func Desc */
  0x10,   /* bcdCDC: spec release number */
  0x01,
  
  /*Call Management Functional Descriptor*/
  0x05,   /* bFunctionLength */
  0x24,   /* bDescriptorType: CS_INTERFACE */
  0x01,   /* bDescriptorSubtype: Call Management Func Desc */
  0x00,   /* bmCapabilities: D0+D1 */
  0x01,   /* bDataInterface: 1 */
  
  /*ACM Functional Descriptor*/
  0x04,   /* bFunctionLength */
  0x24,   /* bDescriptorType: CS_INTERFACE */
  0x02,   /* bDescriptorSubtype: Abstract Control Management desc */
  0x02,   /* bmCapabilities */
  
  /*Union Functional Descriptor*/
  0x05,   /* bFunctionLength */
  0x24,   /* bDescriptorType: CS_INTERFACE */
  0x06,   /* bDescriptorSubtype: Union func desc */
  0x00,   /* bMasterInterface: Communication class interface */
  0x01,   /* bSlaveInterface0: Data Class Interface */
  
  /*Endpoint 2 Descriptor*/
  0x07,                           /* bLength: Endpoint Descriptor size */
  USB_DESC_TYPE_ENDPOINT,   /* bDescriptorType: Endpoint */
  CDC_CMD_EP,                     /* bEndpointAddress */
  0x03,                           /* bmAttributes: Interrupt */
  LOBYTE(CDC_CMD_PACKET_SIZE),     /* wMaxPacketSize: */
  HIBYTE(CDC_CMD_PACKET_SIZE),
  0x10,                           /* bInterval: */ 
  /*---------------------------------------------------------------------------*/
  
  /*Data class interface descriptor*/
  0x09,   /* bLength: Endpoint Descriptor size */
  USB_DESC_TYPE_INTERFACE,  /* bDescriptorType: */
  0x01,   /* bInterfaceNumber: Number of Interface */
  0x00,   /* bAlternateSetting: Alternate setting */
  0x02,   /* bNumEndpoints: Two endpoints used */
  0x0A,   /* bInterfaceClass: CDC */
  0x00,   /* bInterfaceSubClass: */
  0x00,   /* bInterfaceProtocol: */
  0x00,   /* iInterface: */
  
  /*Endpoint OUT Descriptor*/
  0x07,   /* bLength: Endpoint Descriptor size */
  USB_DESC_TYPE_ENDPOINT,      /* bDescriptorType: Endpoint */
  CDC_OUT_EP,                        /* bEndpointAddress */
  0x02,                              /* bmAttributes: Bulk */
  LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),  /* wMaxPacketSize: */
  HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
  0x00,                              /* bInterval: ignore for Bulk transfer */
  
  /*Endpoint IN Descriptor*/
  0x07,   /* bLength: Endpoint Descriptor size */
  USB_DESC_TYPE_ENDPOINT,      /* bDescriptorType: Endpoint */
  CDC_IN_EP,                         /* bEndpointAddress */
  0x02,                              /* bmAttributes: Bulk */
  LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),  /* wMaxPacketSize: */
  HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
  0x00                               /* bInterval: ignore for Bulk transfer */
} ;


CDC_CMD_DATA_SIZE = 8
CDC_DATA_FS_MAX_PACKET_SIZE = 64 verilmiş. Diğer sabitlere ihtiyaç yok sanırım.

Bir de hocam IN ve OUT endpointlerini tanımda ayıran bir özellik göremedim ben. Yani yapısal olarak sadece adresleri farklı. Acaba İkisi de out için kullanılabilir mi diye düşünmedim değil.