STM32 | SPI full-duplex communication problem based on hal

Problem Description

Recently, while conducting SPI full-duplex communication between two stm32 chips, I found that the answers to this kind of communication on the Internet are not very comprehensive. Most of them are based on interrupt processing to send and receive data. After consulting the manual, I found that polling can be used. Query the flag bit to perform data sending and receiving processing, so there is the following.

Basic introduction

This example is based on the STM32F407 chip to implement SPI3 and SPI3 master-slave communication between the two chips. Among them, SPI3_! is configured as the master and SPI3_2 is configured as the slave, both configured in full-duplex mode. Hardware connection diagram:

Among them, we need to pay attention to the fact that the SPI slave cannot actively send data and can only respond to data.

So the master uses query mode to send data to the slave. The slave also uses polling to receive and return customized data, saves the received data into a customized receiving data array, and then sends the customized data to the host. The slave always sends data to the slave when it receives data from the master. That is, the slave passively sends data, that is, the host actively requests data.

Final effect

The following is the effect achieved:

Configuration and explanation

My spi configuration is configured by myself, not cubmax. Of course, it is not difficult to configure it with cubmax. Just refer to it.

/**
 * @brief SPI3 initialization code
 * @note Host mode, 8-bit data, disable hardware chip select
 * @param none
 * @retval None
 */
void spi3_init(void)
{
    SPI3_SPI_CLK_ENABLE(); /* SPI3 clock enable */

    g_spi3_handler.Instance = SPI3_SPI; /* SPI3 */
    g_spi3_handler.Init.Mode = SPI_MODE_MASTER; /* Set the SPI working mode and set it to the master mode */
    g_spi3_handler.Init.Direction = SPI_DIRECTION_2LINES; /* Set the SPI one-way or two-way data mode: SPI is set to two-line mode */
    g_spi3_handler.Init.DataSize = SPI_DATASIZE_8BIT; /* Set the data size of SPI: SPI transmit and receive 16-bit frame structure */
    g_spi3_handler.Init.CLKPolarity = SPI_POLARITY_HIGH; /* The idle state of the serial synchronization clock is high */
    g_spi3_handler.Init.CLKPhase = SPI_PHASE_1EDGE; /* Data is sampled on the second edge (rising or falling) of the serial synchronization clock */
    g_spi3_handler.Init.NSS = SPI_NSS_SOFT; /* Whether the NSS signal is managed by hardware (NSS pin) or software (using SSI bit): the internal NSS signal is controlled by the SSI bit */
    g_spi3_handler.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; /* Define the baud rate prescaler value: the baud rate prescaler value is 256 */
    g_spi3_handler.Init.FirstBit = SPI_FIRSTBIT_MSB; /* Specify whether the data transmission starts from the MSB bit or the LSB bit: the data transmission starts from the MSB bit */
    g_spi3_handler.Init.TIMode = SPI_TIMODE_DISABLE; /* Turn off TI mode */
    g_spi3_handler.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; /* Turn off hardware CRC check */
    g_spi3_handler.Init.CRCPolynomial = 7; /* Polynomial for CRC value calculation */
    HAL_SPI_Init( & amp;g_spi3_handler); /* Initialization */

    __HAL_SPI_ENABLE( & amp;g_spi3_handler); /* Enable SPI3 */

    spi3_read_write_byte(0XFF); /* Start transmission, which actually generates 8 clock pulses to clear DR, not necessary */
}

Afraid that some novices don’t know the configuration very well, I will briefly explain some of the more important configurations:

  1. The first thing to configure is the master-slave mode of spi. What I configure here is the host, so for the mode option, we select master. Similarly, if the slave is configured as slave, it can be configured as slave.
  2. Configure the one-way and two-way mode of spi. Because I want to interact with data on both sides, I choose the two-line mode here.
  3. The data size is easy to understand. Is the data you want to send and receive sixteen or eight bits?
  4. Because the spi clock is sent by the host, we can actually choose any configuration here. Whether it is high or low, the only difference is whether the trigger is a rising trigger or a falling trigger.
  5. Then look at whether the edge trigger is the first or the second trigger for data exchange, which is also relatively simple to understand.
  6. Note that the mode of ti is closed here, that is, without interruption.

After the basic configuration is completed, we need to deal with the logic of spi sending and receiving data.

First, our host needs to turn on the chip select signal, so what I configure here is SPI3_CS(0); //Turn on the chip select

Then we call a function that is commonly used in spi in the hal library.

 HAL_SPI_TransmitReceive( & amp;g_spi3_handler, txdata, rxdata, len, 2000);//Send and receive data

Go to send and receive data. Note that there is a very important point here. If you simply open the chip select and call this function on both sides for data interaction, various unexpected problems such as garbled characters will occur. This is where the project got stuck. After a while, I looked at the register configuration of spi and found that the solution is actually very simple, we will talk about it later. Now that we’re talking about registers, let’s take a look at the register configuration of spi first.

This picture is easy to find in the manual, we Just look at the TXE and RXNE flags of the sr register. We can know the detailed meaning by opening the instructions in the hal library.

#define SPI_FLAG_RXNE SPI_SR_RXNE /* SPI status flag: Rx buffer not empty flag */
#define SPI_FLAG_TXE SPI_SR_TXE /* SPI status flag: Tx buffer empty flag */

Then we can use the flag acquisition function in the hal library to judge the flag bits, and logically judge and process the data in the data buffer area to clarify the timing.

Here I will simply post the host program.

/*------------------------------------------------ -spi host custom function------------------------------------------------- ----- */
/**
* @brief SPI3 host sends and receives a set of data at the same time
 * @param rxdata: Pointer to the array receiving data
 * @param txdata: pointer to the data array to be sent
 * @param len: length of sent and received data
 * @retval None
 * @Explanation: This function is a function for sending and receiving data from the spi host and has full-duplex function.
          After turning on the chip select function, master-slave communication is performed, and data cache judgment and interaction are performed through flag bits.
 */

void SPI3_Master_TransmitReceive_Byte(uint8_t *txdata,uint8_t *rxdata, uint8_t len)
{
    SPI3_CS(0); //Open chip select
    HAL_SPI_TransmitReceive( & amp;SPI3_Handler, txdata, rxdata, len, 2000);//Send and receive data
    if(__HAL_SPI_GET_FLAG( & amp;g_spi3_handler, SPI_FLAG_TXE)) //The sending buffer is empty
    {
        if(__HAL_SPI_GET_FLAG( & amp;g_spi3_handler, SPI_FLAG_RXNE)) //When the receive buffer is not empty
        {
            /* Operation when the receive buffer is not empty */
// HAL_SPI_Receive( & amp;g_spi3_handler, rxdata, 4, 2000); // Receive data through SPI3
        }
        else
        {
            SPI3_CS(1); //Close chip select
            printf("%s%s",txdata,rxdata); //Print the sent and received data
        }
    }
}

There are more detailed notes inside, so I won’t explain them here.

The slave code will not be posted here, the implementation is relatively simple.

It should be noted that the host here will only trigger the slave when it actively sends data. Therefore, the author uses external triggering in the main function to send customized data to the slave through mosi. The slave receives the data and sends back the data synchronously. to the host. Another thing to note is that data transmission is not a simple direct mutual transmission, but data exchange. Therefore, if you want to get data from the slave machine, the host must send a data there, even if it is empty.

Since the author’s own abilities are limited, if you have any other questions, you can contact the author directly for communication^^