0
\$\begingroup\$

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)
\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

You are the best person here to figure out this answer. It's not something that can be done without experimentation and debugging.

Here are a couple of things I would try:

  • Look at oscilloscope plots of the signals in question at both the driver and receiver sides

  • Loop through every data value (or at least every initial 3-bit combination). Send each value 10000 times and track of the number of times it is successfully received, and list all of the values that are incorrect with their counts. Repeat experiment at several clock frequencies. Analyze the data (using Python/Excel/whatever you are familiar with) to determine if there is any dependence on error rates based on data value and clock frequency. For example, you might figure out if errors are truly random, or if perhaps the initial bits appear to be delayed instead sometimes.

\$\endgroup\$
1
  • \$\begingroup\$ Yep - I've been caught out by this before: look at oscilloscope plots of the signals in question at both the driver and receiver sides, clk & dta \$\endgroup\$
    – D Duck
    Commented Jun 26, 2020 at 10:22
0
\$\begingroup\$

I post question after I look for answer more then 4 days and now all works, omg.. I will test more. Here is what I change and what lead to fine results:

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;
        }
    }
}

and this:

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_RXNE) == SPI_SR_RXNE) && (rxSize > 0))
        {
            *(&spiRx[j]) = *(volatile uint8_t *)&SPI2->DR;
            j++;
            rxSize--;
            txAllowed = 1;
        }

        if(((SPI2->SR & SPI_SR_TXE) == SPI_SR_TXE) && (txSize > 0) && (txAllowed == 1))
        {
            *(volatile uint8_t *)&SPI2->DR = *(&spiTx[i]);
            i++;
            txSize--;
            txAllowed = 0;
        }
    }

    Spi2_Deactivate_SS();
}
\$\endgroup\$
1
  • \$\begingroup\$ Glad you got it working! Just looking at your code, are you sure you want those while loops to only run when both txSize > 0 and rxSize > 0? It seems like the && should maybe be || instead? \$\endgroup\$
    – Justin
    Commented Jun 25, 2020 at 15:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.