cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F756VGH6 SPI pins are not responding

Vilius
Associate III

Hi,

I am developing and exploring a register programming based SPI driver. I took my time to read the datasheet and analyze the SPI startup sequence. For testing, I am using pre-programmed SST25VF080B memory IC. 

I wrote a simple and low speed (less than 1 Mbps) SPI driver using registers. It did not look too complicated for me, I verified my setup function with a debugger (not 100% sure, if the setup code itself is correct, but I know for sure, that every line in the SPI_Setup function gets executed correct.) Problem arises when I enter the SPI_Send function - attempt to write data into SPI4->DR register for transmission fails, the register isnt acquiring the data... Logic analyzer even shows that the clock is not generated either. It may be a simple mistake or mistype somewhere, but I can not get it any further on my own, its my first time dealing with STM32 registers in SPI... Any help is much appreciated.

Some notes: I double checked the wiring and hardware, if I use the SPI pins as regular GPIOs, they work just fine, so hardware problems are very unlikely. I use the CS pin with hardware slave management - the exact SPI4_NSS pin is used and it is automatically pulled down as soon as SPI peripheral is enabled. Logic analyzer shows the CS pin toggling, so I have at least one step into it... If I missed some important project details, please let me know.

#include "main.h"

void Core_Clock_Setup (void){

	RCC->CR |= RCC_CR_HSEON;               //Set the clock source to external crystal/resonator (HSE)
	while (!(RCC->CR & RCC_CR_HSEON));	   //Wait until clock gets stable

	RCC->APB1ENR |= RCC_APB1ENR_PWREN;     //Enable power interface clock
	PWR->CR1  &= ~(1U << 14);
	PWR->CR1  &= ~(1U << 15);              //Set internal voltage regulator to is reset value (scale 1)

	FLASH->ACR &= ~FLASH_ACR_ARTEN;        //Disable ART accelerator
	FLASH->ACR &= ~FLASH_ACR_ARTRST;       //Reset ART accelerator
	FLASH->ACR |= FLASH_ACR_PRFTEN;        //Enable prefetch
	FLASH->ACR |= FLASH_ACR_LATENCY_6WS;   //Set 7 CPU clock cycle flash memory access time (in order to get 200 MHz core clock)

	//@ 25 MHz crystal, 200 MHz core clock configuration down below

	RCC->CFGR &= ~(((1 << (7 - 4 + 1)) - 1) << 4);
	RCC->CFGR |= (0 << 4);                 //Core clock division by 1 (core clock is not devided)

	RCC->CFGR |= (0b101 << 10);            //APB1 Low speed prescaler of 4 (50 MHz, max is 54 Mhz)
	RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;      //APB2 High speed prescaler of 2 (100 MHz, max is 108 Mhz)

	RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE;//HSE is set to be PLL entry

	RCC->PLLCFGR |= RCC_PLLCFGR_PLLP_0;    //PLLP Setting corresponding PLL prescalers (division by 2)

	RCC->PLLCFGR &= ~((1 << 6) - 1);
	RCC->PLLCFGR |= (16 & ((1 << 6) - 1));  //PLLM Setting corresponding PLL prescalers (division by 16)

	RCC->PLLCFGR &= ~(((1 << (14 - 6 + 1)) - 1) << 6);
	RCC->PLLCFGR |= (256 << 6);          	//PLLN Setting corresponding PLL prescalers ( multiplication by 256)

	RCC->CR |= RCC_CR_PLLON;               //Enable PLL
	while (!(RCC->CR & RCC_CR_PLLRDY));	   //Wait until PLL gets stable

	RCC->CFGR |= RCC_CFGR_SW_PLL;          //PLL is set to be core clock
	//RCC->CFGR |= RCC_CFGR_SW_HSE;        //HSE is set to be core clock
	while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // Wait until PLL indeed becomes core clock source
}

void Timer_Setup(void){

	//RCC->DCKCFGR1 &= ~(1 << 24);          //TIMxCLK = 2xPCLKx
	RCC->DCKCFGR1 |= (1 << 24);             //TIMxCLK = 4xPCLKx

	RCC->APB1ENR |= (1 << 4);               //Enable Timer 6 clock
	TIM6->PSC = 99;                         //APB1 is 50 Mhz and 100 MHZ for timer (The number is set: Clock in MHz - 1)
	TIM6->ARR = 0xFFFF;                     //Auto reload max value of 16 bits (0xFFFF)
	TIM6->CR1 |= (1 << 0);                  //Enable Timer 6 counter
	while(!(TIM6->SR & (1<<0)));            //Wait until timer update bit is set
}

void delay_ms (uint16_t ms){
	for(uint16_t i = 0; i<ms; i++)
	{
		TIM6->CNT = 0;                      //Reset counter
		while (TIM6->CNT < 1000);           //Wait until counter reaches desired value
	}
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void GPIO_Setup(void){

	RCC->AHB1ENR |= (1 << 2);				 //Enable clock for GPIO bank C
	RCC->AHB1ENR |= (1 << 4);                //Enable clock for GPIO bank E

	delay_ms(1);

	//PC14 OUTPUT
	GPIOC->MODER |= (0b01 << 28);            //PC14 General purpose output mode
	GPIOC->OTYPER &= ~ (1 << 14);            //PC14 Output push-pull (reset state)
	GPIOC->OSPEEDR |= (0b11 << 28);          //PC14 very high GPIO speed
	GPIOC->PUPDR |= (0b10 << 28);            //PC14 pull down resistors
	//PC15 OUTPUT
	GPIOC->MODER |= (0b01 << 30);            //PC15 General purpose output mode
	GPIOC->OTYPER &= ~ (1 << 15);            //PC15 Output push-pull (reset state)
	GPIOC->OSPEEDR |= (0b11 << 30);          //PC15 very high GPIO speed
	GPIOC->PUPDR |= (0b10 << 30);            //PC15 pull down resistors
	/*
	//PE4 OUTPUT
	GPIOE->MODER |= (0b01 << 8);             //PE4 General purpose output mode
	GPIOE->OTYPER &= ~ (1 << 4);             //PE4 Output push-pull (reset state)
	GPIOE->OSPEEDR |= (0b11 << 8);           //PE4 very high GPIO speed
	GPIOE->PUPDR |= (0b10 << 8);             //PE4 pull down resistors
	//PE0 OUTPUT
	GPIOE->MODER |= (0b01 << 0);             //PE0 General purpose output mode
	GPIOE->OTYPER &= ~ (1 << 0);             //PE0 Output push-pull (reset state)
	GPIOE->OSPEEDR |= (0b11 << 0);           //PE0 very high GPIO speed
	GPIOE->PUPDR |= (0b10 << 0);             //PE0 pull down resistors
	*/
	//-----------------------------------------------------------------------------------
	//PE5 INPUT                              ALL BUTTONS EXTERNALLY PULLED UP
	GPIOE->MODER |= (0b00 << 0);               //PE0 General purpose input mode
	//PE6 INPUT
	//GPIOE->MODER |= (0b00 << 12);            //PE6 General purpose input mode

}

void SPI_Setup(void){
		/*
		SCK  -> PE2
		CS 	 -> PE4
		MISO -> PE5
		MOSI -> PE6
		*/
		RCC->APB2ENR |= (1 << 13);               //Enable SPI4 Clock
		//GPIOE clock has been enabled earlier
		//RCC->AHB1ENR |= (1 << 4);                //Enable clock for GPIO bank E

		GPIOE->AFR[0] |= (0b0101 << 8);          	//Select alternate function as SPI SCK on PE2
		GPIOE->AFR[0] |= (0b0101 << 16);	   	 	//Select alternate function as SPI CS on PE4
		GPIOE->AFR[0] |= (0b0101 << 20);          	//Select alternate function as SPI MISO on PE5
		GPIOE->AFR[0] |= (0b0101 << 24);	   	 	//Select alternate function as SPI MOSI on PE6

		GPIOE->MODER |= (0b10 << 2);             	//PE2 as alternate function
		GPIOE->MODER |= (0b10 << 6);             	//PE4 as alternate function
		GPIOE->MODER |= (0b10 << 8);             	//PE5 as alternate function
		GPIOE->MODER |= (0b10 << 10);            	//PE6 as alternate function

		GPIOE->OSPEEDR |= (0b11 << 2);          		//PE2 as very high speed
		GPIOE->OSPEEDR |= (0b11 << 6);          	//PE4 as very high speed
		GPIOE->OSPEEDR |= (0b11 << 8);          	//PE5 as very high speed
		GPIOE->OSPEEDR |= (0b11 << 10);          	//PE6 as very high speed
		//---------------------------------------------------------------------------------------------------
		SPI4->CR1 &= ~ (1 << 0); 					//CPOL bit, clock polarity, according to SPI Slave
		SPI4->CR1 &= ~ (1 << 1); 					//CPHA bit, clock phase, according to SPI Slave
		SPI4->CR1 |= (1 << 2); 						//Master mode selected
		SPI4->CR1 |= (0b111 << 3);                  //Baud rate: 100 MHZ / 256
		SPI4->CR1 &= ~ (1 << 7);					//MSB sent first
		SPI4->CR1 &= ~ (1 << 9);					//Software slave management disabled
		SPI4->CR1 &= ~ (1 << 10);					//Full duplex mode

		SPI4->CR2 |= (1 << 2); 						//Single master mode selected
		SPI4->CR2 &= ~ (1 << 4); 					//Motorola mode selected
		SPI4->CR2 |= (0b0111 << 8); 				//8 bit data frame format selected
}

void SPI_Enable (void){
	SPI4->CR1 |= (1 << 6);						//Enable SPI
}

void SPI_Disable (void){
	SPI4->CR1 &= ~ (1 << 6);					//Disable SPI
}
void SPI_Send (uint8_t data){
	while(!(SPI4->SR &(1 << 1)));               //Wait until TX buffer is empty
	SPI4->DR = data;
	while(!(SPI4->SR &(1 << 1)));               //Wait until TX buffer is empty
	while(SPI4->SR &(1 << 7));               	//Wait until bus is not busy
	//Perform dummy read to flush registers
	uint8_t test = SPI4->DR;
			test = SPI4->SR;
}

uint8_t SPI_Receive (void){
	uint8_t data;
	while(SPI4->SR &(1 << 7));               	//Wait until bus is not busy
	SPI4->DR = 0;								//Send dummy data

	while(!(SPI4->SR &(1 << 0)));               //Wait until RX buffer is not empty
	data = SPI4->DR;

	return data;
}

int main (void){

	Core_Clock_Setup();
	Timer_Setup();
	GPIO_Setup();
	SPI_Setup();

	uint8_t data = 0;

	while(1){

		GPIOC->BSRR |= ((1 << 14) << 16);
		delay_ms(100);
		GPIOC->BSRR |= (1 << 14);
		delay_ms(100);

		if(!(GPIOE->IDR&(1 << 0)))
		{

		SPI_Enable();
		SPI_Send(0x03);
		SPI_Send(0x00);
		SPI_Send(0x00);
		SPI_Send(0b00010000);
		data = SPI_Receive();
		SPI_Disable();

		}
	}

}



3 REPLIES 3

Without even having a look at your code: read out and check/post the SPI and relevant GPIO registers content.

JW

If I understood your message correctly, my SPI CR1 and CR2 registers look exactly how I treat them in my setup function, like I said, I have verified that every line in the setup function gets executed correctly. 

> I have verified that every line in the setup function gets executed correctly

And have you verified also that there's nothing which would change those registers afterwards?

The same for the relevant GPIO registers.

Reading out and checking content of registers is 101. Post for others to look at them.

I also presume that you've already tested the SPI_CK pin's continuity to the point where you are measuring it, by setting given pin to GPIO Output and toggling it.

JW