El siguiente código configura y habilita SPI2 como esclavo en mi placa STM32F303RE , escribe 0xAA, 0xBB, 0xCC, 0xDD bytes en el registro DR y hace un bucle en un tiempo (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 recibiera "0x00 0x00" después de que TXFIFO se vacíe . ¿ Por qué obtengo " 0xAA 0xAA " continuamente en su lugar? No pude encontrar algo que apuntara a tal comportamiento en el manual.
ACTUALIZAR 1
Esperar a que las transacciones terminen justo antes del tiempo (1) y luego escribir ceros en el SPI , así:
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 como si el periférico SPI tuviera algún tipo de historial de lo que se escribió en el TXFIFO y cuando se vacía, envía bytes de ese historial.
ACTUALIZAR 2
Se comporta igual independientemente de cuántos bytes solicite el maestro en una sola selección de chip. He intentado pedir 1, 2, 4 y 5 a la vez.
Me di cuenta de esto y decidí hacer esta respuesta más completa creando algunas animaciones. En primer lugar, hay 2 hechos a tener en cuenta que determinan la lógica detrás del comportamiento de TXFIFO:
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 (ver la ACTUALIZACIÓN 1 de la pregunta ). Tenga en cuenta que, para aclarar el punto, en lugar de insertar ceros , inserté valores 0x01-0x02-..to..-ZEROS_CNT aquí.
CEROS_CNT = 0:
CEROS_CNT = 1:
CEROS_CNT = 2:
CEROS_CNT = 3:
CEROS_CNT = 4:
CEROS_CNT = 5:
...etcétera...
Como se mencionó anteriormente en la pregunta, una solución para enviar 0x00 cuando TXFIFO está vacío sería mantener el periférico SPI deshabilitado cuando TXFIFO está vacío hasta que se escriban nuevos datos en DR , que es lo que terminé haciendo, después de entender lo que está pasando. .
Los valores después del 4º byte no provienen del búfer TX del esclavo. Examine el diagrama de bloques SPI. El maestro los envía originalmente como datos de volcado cuando se cicla el SCI y regresan del registro de desplazamiento del esclavo y aparecen como datos válidos. Cuando el maestro extrae datos, eso es lo que se esperaba que se anulara, pero no lo hace en este caso porque el esclavo no inserta datos en el búfer de TX y carga el registro de desplazamiento.
vladimir cravero
zuzu cornelio
vladimir cravero
zuzu cornelio
cúpula
zuzu cornelio
cúpula
zuzu cornelio
zuzu cornelio
cúpula
vladimir cravero
zuzu cornelio