Следующий код настраивает и включает SPI2 в качестве подчиненного устройства на моей плате STM32F303RE , записывает байты 0xAA, 0xBB, 0xCC, 0xDD в регистр DR и зацикливается через некоторое время (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) { }
С мастером, который запрашивает 2 байта на выбор чипа , мастер получает:
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)
Я ожидал, что мастер получит «0x00 0x00» после того, как TXFIFO станет пустым . Почему вместо этого я постоянно получаю « 0xAA 0xAA »? Я не мог найти что-то, что указывало бы на такое поведение в руководстве.
ОБНОВЛЕНИЕ 1
Ожидание завершения транзакций непосредственно перед while(1) и последующая запись нулей в SPI , например:
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) { }
отображает следующее основное поведение для различных значений 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
Похоже, что периферия SPI имеет какую-то историю того, что было записано в TXFIFO, и когда он становится пустым, он отправляет байты из этой истории.
ОБНОВЛЕНИЕ 2
Он ведет себя одинаково независимо от того, сколько байтов мастер запрашивает за один выбор микросхемы. Я пробовал запрашивать 1, 2, 4 и 5 за раз.
Я понял это и решил сделать этот ответ более полным, создав несколько анимаций. Прежде всего, следует помнить о двух фактах, определяющих логику поведения TXFIFO:
RXFIFO, скорее всего, ведет себя так же.
Алгоритм для push/pop , написанный на C# , выглядит следующим образом:
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;
}
}
А вот 5 анимаций, которые иллюстрируют изначально описанные сценарии ZEROS_CNT (см. ОБНОВЛЕНИЕ 1 вопроса ). Обратите внимание, что для большей ясности вместо нулей я вставил сюда значения 0x01-0x02-..to..-ZEROS_CNT .
ZEROS_CNT = 0:
ZEROS_CNT = 1:
ZEROS_CNT = 2:
ZEROS_CNT = 3:
ZEROS_CNT = 4:
ZEROS_CNT = 5:
...и так далее...
Как упоминалось ранее в вопросе, обходной путь для отправки 0x00 , когда TXFIFO пуст, будет отключать периферийное устройство SPI , когда TXFIFO пуст, пока новые данные не будут записаны в DR , что я и сделал, после понимания того, что происходит .
Значения после 4-го байта не берутся из буфера TX подчиненного устройства. Изучите блок-схему SPI. Первоначально они отправляются мастером в виде данных дампа, когда SCI зацикливается, и они возвращаются из регистра сдвига подчиненного устройства и отображаются как действительные данные. Когда ведущий извлекает данные, ожидается, что это будет переопределено, но в данном случае этого не происходит, потому что подчиненный не помещает данные в буфер TX и не загружает сдвиговый регистр.
Владимир Краверо
Зузу Корнелиу
Владимир Краверо
Зузу Корнелиу
домен
Зузу Корнелиу
домен
Зузу Корнелиу
Зузу Корнелиу
домен
Владимир Краверо
Зузу Корнелиу