STM32 SPI ve DMA Kullanımı

Başlatan Mucit23, 04 Kasım 2020, 22:13:35

Mucit23

Selamlar

STD Periph Library kullanarak SPI ve DMA gerektiren bir uygulama üzerinde çalışıyorum. SPI ve DMA'yı ikisini birlikte çalıştıramadım.

SPI iletişimi Tek yönlü yani half dublex modunda sadece TX yönünde oluyor. Elimde 64 Byte lık sabit bir veri var Bu veriyi DMA kullanarak göndereceğim. Aşağıdaki kodlarla SPI ve DMA'yı kurdum.

void SPI_DMA_Configuration(void)
{
	  GPIO_InitTypeDef GPIO_InitStructure;  
    SPI_InitTypeDef   SPI_InitStructure;
		DMA_InitTypeDef    DMA_InitStructure;
	  NVIC_InitTypeDef NVIC_InitStructure;

	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
	  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	  GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	  /* SPI1 Config -------------------------------------------------------------*/
	  SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
	  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
	  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
	  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;
	  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	  SPI_InitStructure.SPI_CRCPolynomial = 7;
	  SPI_Init(SPI1, &SPI_InitStructure);
			
		/* SPI_MASTER_Tx_DMA_Channel configuration ---------------------------------*/
		DMA_DeInit(DMA1_Channel3);
		DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SPI_DR_Address;
		DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&SPI_MASTER_Buffer_Tx;
		DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
		DMA_InitStructure.DMA_BufferSize = 64;
		DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
		DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
		DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
		DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
		DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
		DMA_InitStructure.DMA_Priority = DMA_Priority_High;
		DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
		DMA_Init(DMA1_Channel3, &DMA_InitStructure);
		
		NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel3_IRQn;
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
		NVIC_Init(&NVIC_InitStructure);
		
	  DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE);
		
    /* Enable SPI_MASTER DMA Tx request */
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);
	
    /* Enable SPI_MASTER */
    SPI_Cmd(SPI1, ENABLE);
		
    /* Enable DMA channels */
    DMA_Cmd(DMA1_Channel3, ENABLE);
}


void DMA1_Channel3_IRQHandler(void)
{
	if(DMA_GetITStatus(DMA1_IT_TC3))
	{
		GPIOA->ODR^=GPIO_Pin_1;
		DMA_ClearITPendingBit(DMA1_IT_TC3);// Clear All the Flags
	}
}

SPI1 donanımı DMA1_Channel3'ü kullanıyor. Referans manual'de DMA SPI üzerinden TXE bayrağı üzerinden Request alır diyor. Bu durumda SPI donanımını aktif ettiğim anda SPI1->Dr boş olacağından hemen DMA'nın ilk transferini yapması lazım. Ama hiçbirşey olmuyor. Ben özel olarak SPI1->DR registerine veri yazdığımda Transfer gerçekleşiyor. Yani DMA çalışmıyor.

Ref manual'de kayboldum. Nasıl çalışıyor tam anlayamadım. Daha önce SPI ile DMA'yı birlikte kullanan oldumu?

Tagli

Ben olaya tam tersi bir şekilde yaklaşıp SPI'ı sürekli açık bırakıyorum. Transfer yapmak istediğimde DMA'yı açıyorum.

Senin kodu inceleyip çözmem zor ama örnek olarak kendi kodumu koyabilirim. F407'de SPI2'yi 16 byte almak için kullanıyordum. Bunu yapmak için SPI'ı sanki 16 byte gönderiyormuş gibi kandırmak gerekiyor ki transfer başlasın. Yani 2 ayrı DMA kuruluyor. Alış tarafındaki DMA'yı otomatik olarak double buffer dolduracak şekilde ayarlamıştım. Periyodik bir TIM kesmesi içinde DMA'yı etkinleştirerek 16 byte'lık transferleri tetikliyordum.

DMA ayarları:
// DMA1 Stream 4 Settings (for SPI2 dummy transfer)
DMA1_Stream4->CR |= (0b000 << DMA_SxCR_CHSEL_Pos) // CH0: SPI2_TX (default value)
		| (0b01 << DMA_SxCR_MSIZE_Pos) // Memory data size: 16 bit
		| (0b01 << DMA_SxCR_PSIZE_Pos) // Peripheral data size: 16 bit
		| (0b01 << DMA_SxCR_DIR_Pos); // Memory to peripheral
DMA1_Stream4->M0AR = (uint32_t)&dummy; // Source: dummy 0's...
DMA1_Stream4->PAR = (uint32_t)&(SPI2->DR); // Target: SPI TX register
DMA1_Stream4->NDTR = 8; // 8 x 16 bit ADC data
// DMA1 is enabled in TIM4 interrupt handler

// DMA1 Stream 3 Settings (for continuous SPI2 reception)
DMA1_Stream3->CR |= (0b000 << DMA_SxCR_CHSEL_Pos) // CH0: SPI2_RX (default value)
		| (0b01 << DMA_SxCR_MSIZE_Pos) // Memory data size: 16 bit
		| (0b01 << DMA_SxCR_PSIZE_Pos) // Peripheral data size: 16 bit
		| (0b00 << DMA_SxCR_DIR_Pos) // Peripheral to memory (default value)
		| DMA_SxCR_MINC // Memory pointer is incremented after each transfer
		| DMA_SxCR_CIRC // Circular mode
		| DMA_SxCR_DBM // Double buffered mode
		| DMA_SxCR_TCIE; // Transfer complete interrupt is enabled.
DMA1_Stream3->PAR = (uint32_t)&(SPI2->DR); // Source: SPI RX register
DMA1_Stream3->M0AR = (uint32_t)&(launchBay[0].adcData[0]); // Target 0
DMA1_Stream3->M1AR = (uint32_t)&(launchBay[1].adcData[0]); // Target 1
DMA1_Stream3->NDTR = adcBufSize / 2; // (adcBufSize / 2) x 16 bit ADC data
DMA1_Stream3->CR |= DMA_SxCR_EN; // Enable DMA1 Stream 3

SPI ayarı:
// SPI2 Settings (used for reading AD7606)
SPI2->CR1 |= SPI_CR1_DFF // 16 bit data frame format
		| (0b001 << SPI_CR1_BR_Pos) // Baud: (24 / 4) MHz
		| SPI_CR1_SSM // software slave management
		| SPI_CR1_SSI // soft slave select is 1
		| SPI_CR1_MSTR // Master mode
		//| SPI_CR1_CPHA // Phase setting
		| SPI_CR1_CPOL; // Clock is 1 (high) when idle
SPI2->CR2 |= SPI_CR2_TXDMAEN // Enable SPI2 TX DMA requests
		| SPI_CR2_RXDMAEN; // Enable SPI2 RX DMA requests
SPI2->CR1 |= SPI_CR1_SPE; // Enable SPI2 module

Bu da TIM kesmesinin ilgili bölümü:
TIM4->SR = 0; // Reset all TIM4 interrupt flags
DMA1->HIFCR |= DMA_HIFCR_CFEIF4 | DMA_HIFCR_CTCIF4 | DMA_HIFCR_CHTIF4; // Clear interrupt flags
DMA1_Stream4->CR |= DMA_SxCR_EN; // Enable DMA for SPI2_TX

F4 ve F1 serilerinin DMA modülleri tam aynı değil. F4'lerde DMA'yı tekrar etkinleştirmek için kesme bayraklarının sıfırlanması gerekiyor. Başka farklar da olabilir, pek hatırlamıyorum.
Gökçe Tağlıoğlu