STM32 SPI: comportamiento extraño en TXFIFO vacío (¿historial de bytes anterior?)

3

El siguiente código configura y habilita SPI2 como esclavo en mi placa STM32F303RE , escribe 0xAA, 0xBB, 0xCC, 0xDD bytes en DR registrarse y hacer un bucle en while (1) :

/* Enable clocks for GPIOB (SPI2 pins) and SPI2 peripheral. */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);

/* SPI pin mappings. */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource12, GPIO_AF_5); /* SPI2_NSS */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_5); /* SPI2_SCK */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_5); /* SPI2_MISO */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_5); /* SPI2_MOSI */

GPIO_InitTypeDef gpio_init_struct =
{
    .GPIO_Mode = GPIO_Mode_AF,
    .GPIO_OType = GPIO_OType_PP,
    .GPIO_PuPd = GPIO_PuPd_DOWN,
    .GPIO_Speed = GPIO_Speed_50MHz
};

/* SPI NSS pin configuration. */
gpio_init_struct.GPIO_Pin = GPIO_Pin_12;
GPIO_Init(GPIOB, &gpio_init_struct);
/* SPI SCK pin configuration. */
gpio_init_struct.GPIO_Pin = GPIO_Pin_13;
GPIO_Init(GPIOB, &gpio_init_struct);
/* SPI MISO pin configuration. */
gpio_init_struct.GPIO_Pin = GPIO_Pin_14;
GPIO_Init(GPIOB, &gpio_init_struct);
/* SPI  MOSI pin configuration. */
gpio_init_struct.GPIO_Pin = GPIO_Pin_15;
GPIO_Init(GPIOB, &gpio_init_struct);

SPI_InitTypeDef spi_init_struct =
{
    .SPI_Direction          = SPI_Direction_2Lines_FullDuplex,
    .SPI_Mode               = SPI_Mode_Slave,
    .SPI_DataSize           = SPI_DataSize_8b,
    .SPI_CPOL               = SPI_CPOL_Low,
    .SPI_CPHA               = SPI_CPHA_1Edge,
    .SPI_NSS                = SPI_NSS_Hard,
    .SPI_BaudRatePrescaler  = SPI_BaudRatePrescaler_2,
    .SPI_FirstBit           = SPI_FirstBit_MSB,
    .SPI_CRCPolynomial      = 7
};

SPI_I2S_DeInit(SPI2);
SPI_Init(SPI2, &spi_init_struct);
SPI_CalculateCRC(SPI2, DISABLE);
SPI_TIModeCmd(SPI2, DISABLE);
SPI_NSSPulseModeCmd(SPI2, DISABLE);

SPI_Cmd(SPI2, ENABLE);
SPI_SendData8(SPI2, (uint8_t) 0xAA);
SPI_SendData8(SPI2, (uint8_t) 0xBB);
SPI_SendData8(SPI2, (uint8_t) 0xCC);
SPI_SendData8(SPI2, (uint8_t) 0xDD);

while(1) { }

Con un maestro que solicita 2 bytes por selección de chip , el maestro recibe:

0xAA 0xBB
0xCC 0xDD
0xAA 0xAA -----> TXFIFO should be empty here, why not "0x00 0x00"?
0xAA 0xAA
0xAA 0xAA
0xAA 0xAA
0xAA 0xAA
0xAA 0xAA
0xAA 0xAA
......... (0xAA 0xAA infinite times)

Habría esperado que el maestro reciba "0x00 0x00" cuando TXFIFO se quede vacío . ¿Por qué obtengo " 0xAA 0xAA " continuamente en su lugar? No pude encontrar algo que apuntara a tal comportamiento en el manual.

ACTUALIZACIÓN 1

Esperando a que las transacciones terminen solo antes de while (1) y escribiendo ceros en el SPI después, de esta manera:

while(SPI_GetTransmissionFIFOStatus(SPI2) != SPI_TransmissionFIFOStatus_Empty) { }
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) != RESET) { }

#define ZEROS_CNT   (1)
for(int i = 0; i < ZEROS_CNT; i++)
    SPI_SendData8(SPI2, 0);

while(1) { }

representa el siguiente comportamiento maestro para diferentes valores de ZEROS_CNT:

ZEROS_CNT = 0 => master receives after TXFIFO is empty: 0xAA infinitely
ZEROS_CNT = 1 => master receives after TXFIFO is empty: 0x00 1 times, followed by 0xBB infinitely
ZEROS_CNT = 2 => master receives after TXFIFO is empty: 0x00 2 times, followed by 0xCC infinitely
ZEROS_CNT = 3 => master receives after TXFIFO is empty: 0x00 3 times, followed by 0xDD infinitely
ZEROS_CNT >= 4 => master receives after TXFIFO is empty: 0x00 infinitely

Parece que el periférico SPI tiene algún tipo de historial de lo que se escribió en el TXFIFO y cuando se vacía, envía bytes de ese historial.

ACTUALIZACIÓN 2

Se comporta de la misma manera independientemente de cuántos bytes solicite el maestro en una sola selección de chip. He intentado solicitar 1, 2, 4 y 5 a la vez.

    
pregunta Corneliu Zuzu

2 respuestas

3

Descubrí esto y decidí hacer esta respuesta más completa creando algunas animaciones. En primer lugar, hay que tener en cuenta 2 hechos que determinan la lógica detrás del comportamiento de TXFIFO:

  • Como indica el manual, TXFIFO es de tamaño 32 bits = 4 bytes - deje que esos bytes sean B1 | B2 | B3 | B4 en ese orden.
  • HECHO # 1 : cuando el maestro solicita un byte del esclavo, el byte que aparece y se devuelve es B1 pero no lo es realmente eliminado de TXFIFO --- > el contenido de TXFIFO es izquierdo-rotated , y no está a la izquierda- cambiado . Esto hace que TXFIFO parezca B2 | B3 | B4 | B1 después de este pop, en lugar de B2 | B3 | B4 | 00 (que es lo que @ogrenci quiso decir con su respuesta).
  • DATO # 2 : cuando TXFIFO se vacía, por alguna razón, una solicitud maestra recibe el primer byte que está en TXFIFO @ ese momento en lugar de 0x00 como hubiera esperado.

RXFIFO probablemente se comporte de la misma manera.

El algoritmo para push / pop , escrito en C # es el siguiente:

public class TXFIFO
{
    public byte[] data;
    byte push_position = 1;
    byte occupied = 0;

    public TXFIFO()
    {
        data = new byte[4];
    }

    public byte Push(byte v)
    {
        // write
        data[push_position - 1] = v;
        // push_position
        if (push_position < 4) push_position++;
        else push_position = 1;
        // occupied
        if (occupied < 4) occupied++;
        return v;
    }

    public byte Pop()
    {
        // read
        if (occupied == 0) return data[0];
        byte v = data[0];
        // rotate left once
        for (int i = 1; i < 4; i++)
            data[i - 1] = data[i];
        data[3] = v;
        //push_position
        if (push_position > 1) push_position--;
        else push_position = 4;
        //occupied
        if (occupied > 0) occupied--;
        return v;
    }

    public byte GetOccupied()
    {
        return occupied;
    }
}

Y aquí hay 5 animaciones que ilustran los escenarios ZEROS_CNT descritos originalmente (consulte la pregunta ACTUALIZACIÓN 1 ). Tenga en cuenta que, para aclarar el punto, en lugar de insertar ceros , inserté 0x01-0x02 - .. to ..- ZEROS_CNT valores aquí.

ZEROS_CNT = 0:

ZEROS_CNT=1:

ZEROS_CNT=2:

ZEROS_CNT=3:

ZEROS_CNT=4:

ZEROS_CNT=5:

... y así sucesivamente ...

Como se mencionó anteriormente en la pregunta, una solución alternativa para enviar 0x00 cuando TXFIFO está vacío sería mantener el SPI periférico desactivado cuando TXFIFO está vacío hasta que se escriben nuevos datos en DR , que es lo que terminé haciendo, después de comprender lo que está pasando.

    
respondido por el Corneliu Zuzu
0

Los valores después del 4to byte no se obtienen del búfer de TX del esclavo. Examine el diagrama de bloques SPI. Se envían originalmente como datos de volcado por el maestro cuando el SCI realiza un ciclo y regresan del registro de desplazamiento del esclavo y aparecen como datos válidos. Cuando el maestro extrae datos, eso es lo que se espera que se anule, pero no lo hace en este caso porque el esclavo no inserta datos en el búfer de TX y no carga el registro de desplazamiento.

    
respondido por el Ayhan

Lea otras preguntas en las etiquetas