I'm trying to use SPI communication between 2 boards (custom design). Register programming approach is used.
Sometimes SPI works fine, sometimes it doesn't. When it doesn't work properly, one of first 2 (in rare case 3) digits mess up. The speed of SPI doesn't affect errors (21 MHz, 10.5 MHz,...). The SPI master (STM32F405VGT) runs at 168 MHz, APB1 (SPI is on APB1) = 42 MHz. The SPI slave (STM32F401CEU) runs at 84 MHz, APB1 (SPI is on APB1) = 42 MHz. Any idea is welcome.
Here is code for the SPI slave:
volatile uint8_t txSpi[SPI_BUFF_SIZE] = "\r\nSlave\r\n";
volatile uint8_t rxSpi[SPI_BUFF_SIZE] = {0};
void Init_Spi(void)
{
Init_SPI1_Slave();
Init_SS();
}
void Init_SPI1_Slave(void)
{
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; // portA clock
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; // SPI1 clock
GPIOA->MODER |= PA5_MODER_AF | PA6_MODER_AF | PA7_MODER_AF; // set GPIO pins to be alternate
GPIOA->OTYPER |= PA5_PUSH_PULL | PA6_PUSH_PULL | PA7_PUSH_PULL; // set GPIO pins to be push-pull
GPIOA->PUPDR |= PA5_NO_PULL_UP_DOWN | PA6_NO_PULL_UP_DOWN | PA7_NO_PULL_UP_DOWN; // no pull-up or pull-down
GPIOA->OSPEEDR |= PA5_VERY_HIGH_SPEED | PA6_VERY_HIGH_SPEED | PA7_VERY_HIGH_SPEED; // output speed for dedicated pins
GPIOA->AFR[0] |= PA5_SET_AF_SPI1 | PA6_SET_AF_SPI1 | PA7_SET_AF_SPI1; // set alternate function for corresponding pin
SPI1->CR1 &= ~(SPI_CR1_SPE); // disable SPI1
SPI1->CR1 = SPI_CR1_BIDIMODE_2_LINE | SPI_CR1_CRCEN_DISABLE | SPI_CR1_DATA_FORMAT_8_BIT | SPI_CR1_RXONLY_FULL_DUPLEX |
SPI_CR1_SSM_ENABLE | SPI_CR1_LSBFIRST_MSB_FIRST | SPI_CR1_MSTR_SLAVE | SPI_CR1_BAUD_RATE_FPCLK_8 |
SPI_CR1_CPOL_0_IDLE | SPI_CR1_CPHA_1_EDGE;
SPI1->CR2 = 0x0000;
SPI1->I2SCFGR &= ~(SPI_I2SCFGR_SPI_MODE); // SPI_MODE_SELECTED = ~0x0800, I2S_MODE_SELECTED = 0x0800
SPI1->CR1 |= SPI_CR1_SPE; // enable SPI1
}
void SPI1_TxRx_Slave(uint8_t *spiTx, uint8_t *spiRx, uint16_t size)
{
uint16_t i = 0, j = 0;
uint16_t txSize = size, rxSize = size;
uint8_t txAllowed = 1;
*(volatile uint8_t *)&SPI1->DR = *(&spiTx[i]);
i++;
txSize--;
txAllowed = 0;
while ((txSize > 0) && (rxSize > 0))
{
if(((SPI1->SR & SPI_SR_RXNE) == SPI_SR_RXNE) && (rxSize > 0))
{
*(&spiRx[j]) = *(volatile uint8_t *)&SPI1->DR;
j++;
rxSize--;
txAllowed = 1;
}
if(((SPI1->SR & SPI_SR_TXE) == SPI_SR_TXE) && (txSize > 0) && (txAllowed == 1))
{
*(volatile uint8_t *)&SPI1->DR = *(&spiTx[i]);
i++;
txSize--;
txAllowed = 0;
}
}
}
void Init_SS(void)
{
GPIOA->MODER &= ~(PA4_MODER_INPUT); // set GPIO pin to be input
GPIOA->OTYPER &= ~(PA4_PUSH_PULL); // set GPIO pin to be push-pull
GPIOA->PUPDR &= ~(PA4_NO_PULL_UP_DOWN); // no pull-up or pull-down
GPIOA->OSPEEDR |= PA4_VERY_HIGH_SPEED; // output speed for dedicated pin
EXTI->IMR |= PA4_IT; // config interrupt on pin
EXTI->FTSR |= PA4_FALLING_EDGE; // config pin to detect falling edge
HAL_NVIC_SetPriority(EXTI4_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI4_IRQn);
}
void EXTI4_IRQHandler(void)
{
if(__HAL_GPIO_EXTI_GET_IT(SS_PIN) != RESET)
{
__HAL_GPIO_EXTI_CLEAR_IT(SS_PIN);
SPI1_TxRx_Slave(txSpi, rxSpi, SPI_BUFF_SIZE);
usart1_send_buffer(rxSpi, SPI_BUFF_SIZE);
}
}
// .h file
#include <stdbool.h>
#define SS_PIN (GPIO_PIN_4)
#define SPI_BUFF_SIZE (50)
#define PA5_MODER_AF (0x00000800)
#define PA6_MODER_AF (0x00002000)
#define PA7_MODER_AF (0x00008000)
#define PA5_PUSH_PULL (0x0000)
#define PA6_PUSH_PULL (0x0000)
#define PA7_PUSH_PULL (0x0000)
#define PA5_NO_PULL_UP_DOWN (0x00000000)
#define PA6_NO_PULL_UP_DOWN (0x00000000)
#define PA7_NO_PULL_UP_DOWN (0x00000000)
#define PA5_VERY_HIGH_SPEED (0x00000C00)
#define PA6_VERY_HIGH_SPEED (0x00003000)
#define PA7_VERY_HIGH_SPEED (0x0000C000)
#define PA5_SET_AF_SPI1 (0x00500000)
#define PA6_SET_AF_SPI1 (0x05000000)
#define PA7_SET_AF_SPI1 (0x50000000)
#define PA4_MODER_INPUT (0x00000300)
#define PA4_PUSH_PULL (0x0010)
#define PA4_NO_PULL_UP_DOWN (0x00000300)
#define PA4_VERY_HIGH_SPEED (0x00000300)
#define PA4_IT (0x00000010)
#define PA4_FALLING_EDGE (0x00000010)
#define SPI_CR1_BIDIMODE_2_LINE (0x00000000)
#define SPI_CR1_CRCEN_DISABLE (0x00000000)
#define SPI_CR1_DATA_FORMAT_8_BIT (0x00000000)
#define SPI_CR1_RXONLY_FULL_DUPLEX (0x00000000)
#define SPI_CR1_SSM_ENABLE (0x00000200)
#define SPI_CR1_LSBFIRST_MSB_FIRST (0x00000000)
#define SPI_CR1_MSTR_SLAVE (0x00000000)
#define SPI_CR1_CPOL_0_IDLE (0x00000000)
#define SPI_CR1_CPHA_1_EDGE (0x00000000)
#define SPI_CR1_BAUD_RATE_FPCLK_256 (0x00000038)
#define SPI_CR1_BAUD_RATE_FPCLK_128 (0x00000030)
#define SPI_CR1_BAUD_RATE_FPCLK_64 (0x00000028)
#define SPI_CR1_BAUD_RATE_FPCLK_32 (0x00000020)
#define SPI_CR1_BAUD_RATE_FPCLK_16 (0x00000018)
#define SPI_CR1_BAUD_RATE_FPCLK_8 (0x00000010)
#define SPI_CR1_BAUD_RATE_FPCLK_4 (0x00000008)
#define SPI_CR1_BAUD_RATE_FPCLK_2 (0x00000000)
#define SPI_I2SCFGR_SPI_MODE (0x0800)
void Init_Spi(void);
void Init_SPI1_Slave(void);
void SPI1_TxRx_Slave(uint8_t *spiTx, uint8_t *spiRx, uint16_t size);
void Init_SS(void);
extern volatile uint8_t txSpi[SPI_BUFF_SIZE];
extern volatile uint8_t rxSpi[SPI_BUFF_SIZE];
Code for the SPI master:
void Init_Spi2_Master(void) // PB13 = SPI2_SCK, PB14 = SPI2_MISO, PB15 = SPI2_MOSI
{
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; // portB clock
RCC->APB1ENR |= RCC_APB1ENR_SPI2EN; // SPI2 clock
GPIOB->MODER |= PB13_MODER_AF | PB14_MODER_AF | PB15_MODER_AF; // set GPIO pins to be alternate
GPIOB->OTYPER |= PB13_PUSH_PULL | PB14_PUSH_PULL | PB15_PUSH_PULL; // set GPIO pins to be push-pull
GPIOB->PUPDR |= PB13_NO_PULL_UP_DOWN | PB14_NO_PULL_UP_DOWN | PB15_NO_PULL_UP_DOWN; // no pull-up or pull-down
GPIOB->OSPEEDR |= PB13_VERY_HIGH_SPEED | PB14_VERY_HIGH_SPEED | PB15_VERY_HIGH_SPEED; // output speed for dedicated pins
GPIOB->AFR[1] |= PB13_SET_AF_SPI2 | PB14_SET_AF_SPI2 | PB15_SET_AF_SPI2; // set alternate function for corresponding pin
SPI2->CR1 &= ~(SPI_CR1_SPE); // disable SPI
SPI2->CR1 = SPI_CR1_BIDIMODE_2_LINE | SPI_CR1_CRCEN_DISABLE | SPI_CR1_DATA_FORMAT_8_BIT | SPI_CR1_RXONLY_FULL_DUPLEX |
SPI_CR1_SSM_ENABLE | SPI_CR1_SSI | SPI_CR1_LSBFIRST_MSB_FIRST | SPI_CR1_MSTR_MASTER |
SPI_CR1_CPOL_0_IDLE | SPI_CR1_CPHA_1_EDGE | SPI_CR1_BAUD_RATE_FPCLK_2;
SPI2->CR2 = 0x0000;
SPI2->I2SCFGR &= ~(SPI_I2SCFGR_SPI_MODE); // SPI_MODE_SELECTED = ~0x0800, I2S_MODE_SELECTED = 0x0800
SPI2->CR1 |= SPI_CR1_SPE; // enable SPI
}
void Spi2_TxRx_Master(uint8_t *spiTx, uint8_t *spiRx, uint16_t size)
{
uint16_t i = 0, j = 0;
uint16_t txSize = size, rxSize = size;
uint8_t txAllowed = 1;
Spi2_Activate_SS();
while((txSize > 0) && (rxSize > 0))
{
if(((SPI2->SR & SPI_SR_TXE) == SPI_SR_TXE) && (txSize > 0) && (txAllowed == 1))
{
*(volatile uint8_t *)&SPI2->DR = *(&spiTx[i]);
i++;
txSize--;
txAllowed = 0;
}
if(((SPI2->SR & SPI_SR_RXNE) == SPI_SR_RXNE) && (rxSize > 0))
{
*(&spiRx[j]) = *(volatile uint8_t *)&SPI2->DR;
j++;
rxSize--;
txAllowed = 1;
}
}
Spi2_Deactivate_SS();
}
void Spi2_Activate_SS(void)
{
GPIOB->BSRR |= SPI2_SS << 16;
}
void Spi2_Deactivate_SS(void)
{
GPIOB->BSRR |= SPI2_SS;
}
void Init_SS_Spi2(void)
{
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
GPIOB->MODER |= PB12_MODER_OUTPUT; // set GPIO pin to be output
GPIOB->OTYPER &= ~(PB12_PUSH_PULL); // set GPIO pin to be push-pull
GPIOB->PUPDR |= PB12_PULL_UP; // GPIO pin pull-up
GPIOB->OSPEEDR |= PB12_VERY_HIGH_SPEED; // output speed for dedicated pin
Spi2_Deactivate_SS();
}
// .h file
#define PB15_MODER_AF (0x80000000) //****************************
#define PB14_MODER_AF (0x20000000) // //
#define PB13_MODER_AF (0x08000000) // //
#define PB15_PUSH_PULL (0x0000) // //
#define PB14_PUSH_PULL (0x0000) // //
#define PB13_PUSH_PULL (0x0000) // //
#define PB15_NO_PULL_UP_DOWN (0x00000000) // //
#define PB14_NO_PULL_UP_DOWN (0x00000000) // SPI2 //
#define PB13_NO_PULL_UP_DOWN (0x00000000) // //
#define PB15_VERY_HIGH_SPEED (0xC0000000) // //
#define PB14_VERY_HIGH_SPEED (0x30000000) // //
#define PB13_VERY_HIGH_SPEED (0x0C000000) // //
#define PB15_SET_AF_SPI2 (0x50000000) // //
#define PB14_SET_AF_SPI2 (0x05000000) // //
#define PB13_SET_AF_SPI2 (0x00500000) //****************************
#define PB12_MODER_OUTPUT (0x01000000) //**************************
#define PB12_PUSH_PULL (0x00001000) // SLAVE SELECT //
#define PB12_PULL_UP (0x01000000) // SPI2 //
#define PB12_VERY_HIGH_SPEED (0x03000000) //**************************
#define SPI_CR1_BIDIMODE_2_LINE (0x00000000)
#define SPI_CR1_CRCEN_DISABLE (0x00000000)
#define SPI_CR1_DATA_FORMAT_8_BIT (0x00000000)
#define SPI_CR1_RXONLY_FULL_DUPLEX (0x00000000)
#define SPI_CR1_SSM_ENABLE (0x00000200)
#define SPI_CR1_LSBFIRST_MSB_FIRST (0x00000000)
#define SPI_CR1_MSTR_MASTER (0x00000004)
#define SPI_CR1_CPOL_0_IDLE (0x00000000)
#define SPI_CR1_CPHA_1_EDGE (0x00000000)
#define SPI_CR1_BAUD_RATE_FPCLK_256 (0x00000038)
#define SPI_CR1_BAUD_RATE_FPCLK_128 (0x00000030)
#define SPI_CR1_BAUD_RATE_FPCLK_64 (0x00000028)
#define SPI_CR1_BAUD_RATE_FPCLK_32 (0x00000020)
#define SPI_CR1_BAUD_RATE_FPCLK_16 (0x00000018)
#define SPI_CR1_BAUD_RATE_FPCLK_8 (0x00000010)
#define SPI_CR1_BAUD_RATE_FPCLK_4 (0x00000008)
#define SPI_CR1_BAUD_RATE_FPCLK_2 (0x00000000)
#define SPI_I2SCFGR_SPI_MODE (0x0800)