cancel
Showing results for 
Search instead for 
Did you mean: 

SPI NSSP on STM32F767ZI

sinannov
Associate

Hello,
Disclosure: I am fairly new to using STM based Microcontrollers, as well as DMA.

I am using an STM32F767ZI MCU on a custom PCB. I am trying to Implement an SPI interface to an DAC Chip (AD5443) through DMA (no interrupts as of the moment). My DMA is configured as a circular buffer.


One requirement from the DAC is that after one data frame, the CS (NSS) must go high for a minimum of 30 ns to drive the output current. However, the issue I face is that the NSS signal never goes high in between data frames. To address this, I set Bit 3 on SPI2_CR2 to enable NSS Pulse, which is supposed to create a pulse on NSS in between two frames (I checked the timing, one pulse should equate to 40 ns). But the NSS pin remains low throughout. Can someone explain to me what I am doing wrong?

Currently, I have configured the following SPI registers as such:
SPI2_CR1: 0x0044
SPI2_CR2: 0x0F0E

It seems that most of configuration is correct, as I see that the MOSI and CLK signals are correct. However, my NSS pin never goes back high.

Here are the relevant signals.

sinannov_1-1733871204901.png

Ch1 Yellow - MOSI
Ch2 Blue - CS
Ch3 Red - SCLK (cursors are aligned to show one data frame of 16 bits)

Note: I've confirmed that the data being sent over is not an issue, as manually sending data over SPI (without DMA) works fine and drives the output. I've also manually toggled the NSS pin to confirm that the connection is not an issue. 

Relevant Code:

 

static void SPI2_PinConfig(void)
{
	// Step 1: Enable Clock for GPIO Ports and the SPI Peripheral by accessing Registers RCC_AHB1ENR and RCC_APB1ENR (found on RM0410)
	RCC->AHB1ENR |= (RCC_AHB1ENR_GPIOBEN); 	// Set Bit 1 for GPIOB
	RCC->AHB1ENR |= (RCC_AHB1ENR_GPIOCEN);	// Set bit 2 for GPIOC

	RCC->APB1ENR |= (RCC_APB1ENR_SPI2EN);	// Set Bit 14 to enable Peripheral Clock for SPI2

	// Step 2: Configure the Pin Mode for All Pins used (pg231 of RM0410 and the Datasheet for STM32F767xx Devices)
	// PB10 - SPI2 SCK
	GPIOB->MODER &= ~(3u << (2 * 10)); 		// Clear mode bits for PB10
	GPIOB->MODER |= (2u << (2 * 10)); 		// Set Mode of this Pin as Alternate Function
	GPIOB->AFR[1] &= ~(						// Access AFR HIGHER (corresponds to Pins 8 to 15)
				(15u << (4 * (10 - 8)))		// Clear PB10 AF (- 8 is included for offset)
			);
	GPIOB->AFR[1] |= (						// Access AFR HIGHER (corresponds to Pins 8 to 15)
				(5u << (4 * (10 - 8)))		// Set PB10 AF to AF5 corresponds to SCK SPI2
			);
	GPIOB->OSPEEDR |= (3u << (2 * 10));		// Set Speed of pin to Very Fast

	// PC2 - SPI2 MISO
	GPIOC->MODER &= ~(3u << (2 * 2));		// Clear mode bits for PC2
	GPIOC->MODER |= (2u << (2 * 2));		// Set Mode to Alternate Function
	GPIOC->AFR[0] &= ~(						// Access AFR Lower (Pins 0 to 7)
				(15u << (4 * 2))			// Clear PC2 AF
			);
	GPIOC->AFR[0] |= (						// Access AFR LOWER
				(5u << (4 * 2))				// Set PC2 AF to AF5 corresponds to MISO SPI2
				);
	GPIOC->OSPEEDR |= (3u << (2 * 2));		// Set Speed of pin to Very Fast

	// PC3 - SPI2 MOSI
	GPIOC->MODER &= ~(3u << (2 * 3));		// Clear Mode bits for PC3
	GPIOC->MODER |= (2u << (2 * 3));		// Set mode to Alternate Function
	GPIOC->AFR[0] &= ~(						// Access AFR Lower (Pins 0 to 7)
				(15u << (4 * 3))			// Clear PC3 AF
			);
	GPIOC->AFR[0] |= (						// Access AFR LOWER
				(5u << (4 * 3))				// Set PC3 AF to AF5 corresponds to MOSI SPI2
				);
	GPIOC->OSPEEDR |= (3u << (2 * 3));		// Set Speed of pin to Very Fast

	// PB9 - SPI2 NSS
	GPIOB->MODER &= ~(3u << (2 * 9));		// clear mode bits for PB9
	GPIOB->MODER |= (2u << (2 * 9));		// set mode to Alternate Function
	GPIOB->AFR[1] &= ~(15u << (4 * (9 - 8)));	// Clear Alternate Function Bits
	GPIOB->AFR[1] |= (5u << (4 * (9 - 8)));	// Set to alternate function - corresponds to SPI2 NSS
	GPIOB->OSPEEDR |= (3u << (2 * 9));		// Set speed of pin to very fast
}

static void SPI2Configuration(void)		// Referencing Rm0410 pg1347
{
	// Step 1: Clear Control Registers
	SPI2->CR1 = 0;
	SPI2->CR2 = 0;

	// Step 2: Configure Control Register 1
											// No need to Adjust Bit 1-0. CK should be 0 when Idle, and Sampled on the first edge
	SPI2->CR1 |= (2u << 1);					// Set SPI2 as Master Mode
//	SPI2->CR1 |= (3u << SPI_CR1_BR_Pos);
											// Given a PCC Clock Frequency of 96MHz, the default SPI Baudrate Prescaler is 000 (or /2), meaning SPI2 has a frequency of 48MHz. AD5443 is rated up to 50MHz max clock frequency -> i do not need to adjust
											// Skipping Enable Bit (6) for now -> SPI can only be configured while the Peripheral is Disabled
											// No need to Adjust Bit 7 as data is MSB first
	SPI2->CR1 &= ~SPI_CR1_SSM;  			// Disable Software NSS Management
											// Disregard bits 13-11 as I am not using CRC
											// Disregard bits 15-14 as I am using 2-line unidirectional mode (MOSI + MISO)

	// Step 3: Configure Control Register 2
	SPI2->CR2 |= SPI_CR2_TXDMAEN;			// Enable TX on SPI2 to be controlled by DMA
	SPI2->CR2 |= (2u << 1);					// Set SSOE to enable Slave Select Output
	SPI2->CR2 |= (15u << 8);				// Set bits 11-8 to indicate that I am performing 16bit data transfers
	SPI2->CR2 |= (3u << 1);					// Enable NSS Pulse Management to generate a pulse between two consecutive data cycles (THIS IS NEEDED to allow the CS pin to go back high in between data transfers)

	// Step 4: Enable SPI2
	SPI2->CR1 |= (1u << 6);					// Enable SPI2
}

static void MyDMASPI2Enabler(uint16_t *dataBuffer, uint16_t dataSize)
{
	RCC->AHB1ENR |= (RCC_AHB1ENR_DMA1EN); 	// Enable Clock for DMA1
    while (DMA1_Stream4->CR & DMA_SxCR_EN);  // Wait until the stream is disabled


	DMA1_Stream4->CR &= ~DMA_SxCR_EN; 		// Disable DMA STream 4 while performing configuration

	DMA1_Stream4->PAR = (uint32_t)&(SPI2->DR);	// Set peripheral address of DMA to the Data register of SPI2

	DMA1_Stream4->M0AR = (uint32_t)dataBuffer;	// Set memory address to dataBuffer passed in (will be reading from Sine LUT memory address)

	DMA1_Stream4->NDTR = dataSize;			// set number of data items to transfer (corresponds to the length of LUT)

	DMA1_Stream4->CR = 0;					// Clear Control register for Stream 4

	DMA1_Stream4->CR |= (0 << DMA_SxCR_CHSEL_Pos);	// Select Channel 0 (SPI2 TX)

	// Skipping MBURST and PBURST bits (24:21) as these should remain as 0)
	// Bit 20 Reserved, kept at 0
	// Skipping Current Target CT bit (19) and Double Buffer Mode (18) as I will not be needing double buffer

	DMA1_Stream4->CR |= DMA_SxCR_PL_1;		// Set priority Level to High

	// SKipping PINCOS bit 15

	DMA1_Stream4->CR |= DMA_SxCR_MSIZE_0;	// Set Memory data size as 16bit by setting Bit 14:13 to 01

	DMA1_Stream4->CR |= DMA_SxCR_PSIZE_0;	// Set Peripheral Data Size to 16bit by setting Bit 12:11 to 01

	DMA1_Stream4->CR |= DMA_SxCR_MINC;		// Enable Memory Increment Mode by setting bit 10

	// skipping Bit 9 PINC as peripheral address should not increment (since Data Register for SPI does not change)

	DMA1_Stream4->CR |= DMA_SxCR_CIRC;		// Enable Circular Buffer Mode so DMA continuously writes the data buffer (Bit 8)

	DMA1_Stream4->CR |= DMA_SxCR_DIR_0;		// Set Direction as Memory to peripheral by setting Bit 7:6 to 01

	// Leave Bit5 alone as DMA will be the flow controller

	// Disregarding Bits 4:1 as I will not be using interrupts for now

	DMA1_Stream4->FCR |= DMA_SxFCR_DMDIS;  // Enable FIFO mode
	DMA1_Stream4->FCR |= DMA_SxFCR_FTH_0;  // Set FIFO threshold to 1/4 full

	DMA1_Stream4->CR |= DMA_SxCR_EN;		// Enable DMA1 Stream 4

}

 

 

 

1 ACCEPTED SOLUTION

Accepted Solutions
	SPI2->CR2 |= (2u << 1);					// Set SSOE to enable Slave Select Output
	SPI2->CR2 |= (15u << 8);				// Set bits 11-8 to indicate that I am performing 16bit data transfers
	SPI2->CR2 |= (3u << 1);					// Enable NSS Pulse Management to generate a pulse between two consecutive data cycles (THIS IS NEEDED to allow the CS pin to go back high in between data transfers)

You probably meant (1<<2) to set SSOE and (1<<3) to enable NSSP.

To avoid such blunders, don't use "magic numbers" but symbols from the CMSIS-mandated device header, also don't perform several RMW operations on the registers, rather calculate a value and write it once (see e.g. end of this writeup).

JW

View solution in original post

4 REPLIES 4
MasterT
Lead

Recently I discovered on H743 that NSSP is not drivven if set to 1, works fine with values 2-15

Settings:

  hspi2.Instance                        = SPI2;
  hspi2.Init.Mode                       = SPI_MODE_MASTER;// SPI_MODE_SLAVE;
  hspi2.Init.Direction                  = SPI_DIRECTION_2LINES_RXONLY;//SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize                   = SPI_DATASIZE_32BIT;

  hspi2.Init.CLKPolarity                = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase                   = SPI_PHASE_2EDGE; //SPI_PHASE_1EDGE;
  hspi2.Init.NSS                        = SPI_NSS_HARD_OUTPUT;
  hspi2.Init.BaudRatePrescaler          = SPI_BAUDRATEPRESCALER_2; //16;
  hspi2.Init.FirstBit                   = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode                     = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation             = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial              = 0x0;
  //  hspi2.Init.NSSPMode                   = SPI_NSS_PULSE_DISABLE;
  hspi2.Init.NSSPMode                   = SPI_NSS_PULSE_ENABLE;
  hspi2.Init.NSSPolarity                = SPI_NSS_POLARITY_LOW;
  hspi2.Init.FifoThreshold              = SPI_FIFO_THRESHOLD_01DATA;
  hspi2.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
  hspi2.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
  hspi2.Init.MasterSSIdleness           = SPI_MASTER_SS_IDLENESS_00CYCLE;
  hspi2.Init.MasterInterDataIdleness    = SPI_MASTER_INTERDATA_IDLENESS_02CYCLE;
  hspi2.Init.MasterReceiverAutoSusp     = SPI_MASTER_RX_AUTOSUSP_DISABLE;
  hspi2.Init.MasterKeepIOState          = SPI_MASTER_KEEP_IO_STATE_ENABLE;
  hspi2.Init.IOSwap                     = SPI_IO_SWAP_DISABLE;

  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    Error_Handler();
  }
	SPI2->CR2 |= (2u << 1);					// Set SSOE to enable Slave Select Output
	SPI2->CR2 |= (15u << 8);				// Set bits 11-8 to indicate that I am performing 16bit data transfers
	SPI2->CR2 |= (3u << 1);					// Enable NSS Pulse Management to generate a pulse between two consecutive data cycles (THIS IS NEEDED to allow the CS pin to go back high in between data transfers)

You probably meant (1<<2) to set SSOE and (1<<3) to enable NSSP.

To avoid such blunders, don't use "magic numbers" but symbols from the CMSIS-mandated device header, also don't perform several RMW operations on the registers, rather calculate a value and write it once (see e.g. end of this writeup).

JW

@MasterT,

just to avoid OP's confusion, let's put down, that the 'H7 SPI is very, very different from all other STM32 SPI modules and it is an overcomplicated beast. The 'F7 SPI does not allow to set the NSSP pulse width, in 'F7 it's fixed.

JW

Wow, such a simple mistake. Thanks JW.

Thanks for providing that writeup as well. If I had to guess, this also helps with timing as its performing less write functions.