STM32F756VGH6 SPI pins are not responding
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();
}
}
}
