STM32F3 SPI over DMA 接收问题
STM32F3 SPI over DMA receive issues
我正在使用 STM32F303VC,试图通过 DMA 连接到 SPI EEPROM,但在接收数据时遇到了一些问题。 DMA 设置为全双工,使用逻辑分析仪查看信号时,我可以看到传输和响应正常。问题出在 DMA 接收缓冲区中。由于某种原因,接收缓冲区没有显示正确的数据。看起来可能存在一些对齐问题,因为我看到了一些正确的值,只是顺序不正确。比如我是
发送以下内容:
1.
MOSI: 0x06
MISO: 0xFF
2.
MOSI: 0x05 0xFF
MISO: 0xFF 0x02
3.
MOSI: 0x05 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
MISO: 0xFF 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02
这些是上述每种情况下接收缓冲区的内容
0x00 (expected 0xFF)
0x02 0x00 (expected 0xFF, 0x02)
0x02 0xFF, 0x02, 0xFF, 0x02, 0x02, 0x02, 0x02 (expected 0xFF, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02)
这是我的初始化代码和函数调用:
// Define for SPI DMA channels
#define EEPROM_SPI_DMA_RX_CHANNEL DMA1_Channel4 /* SPI2 RX DMA is handled by DMA 1 Channel 4 */
#define EEPROM_SPI_DMA_TX_CHANNEL DMA1_Channel5 /* SPI2 TX DMA is handled by DMA 1 Channel 5 */
void EEPROM_Initialize(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// SPI CS, SCK, MISO and MOSI peripheral clock enable
RCC_AHBPeriphClockCmd(EEPROM_SPI_CLK | EEPROM_SPI_CS_GPIO_CLK | EEPROM_SPI_SCK_GPIO_CLK |
EEPROM_SPI_MISO_GPIO_CLK | EEPROM_SPI_MOSI_GPIO_CLK , ENABLE);
// Enable DMA1 clock
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// SPI Perihperal clock enable
RCC_APB1PeriphClockCmd(EEPROM_SPI_CLK, ENABLE);
// Configure SPI pins: CS
GPIO_InitStructure.GPIO_Pin = EEPROM_SPI_CS_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(EEPROM_SPI_CS_GPIO, &GPIO_InitStructure);
GPIO_SetBits(EEPROM_SPI_CS_GPIO, EEPROM_SPI_CS_PIN); // Drive CS high
// Configure SPI pins: SCK
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // Now that CS GPIO has been initialized to Mode: OUT, change GPIO Mode to AF
GPIO_InitStructure.GPIO_Pin = EEPROM_SPI_SCK_PIN;
GPIO_Init(EEPROM_SPI_SCK_GPIO, &GPIO_InitStructure);
// Configure SPI pins: MISO
GPIO_InitStructure.GPIO_Pin = EEPROM_SPI_MISO_PIN;
GPIO_Init(EEPROM_SPI_MISO_GPIO, &GPIO_InitStructure);
// Configure SPI pins: MOSI
GPIO_InitStructure.GPIO_Pin = EEPROM_SPI_MOSI_PIN;
GPIO_Init(EEPROM_SPI_MOSI_GPIO, &GPIO_InitStructure);
// Configure alternate function to SPI related GPIOs to act as SPI peripheral
GPIO_PinAFConfig(EEPROM_SPI_SCK_GPIO, EEPROM_SPI_SCK_PIN_SOURCE, EEPROM_SPI_SCK_AF);
GPIO_PinAFConfig(EEPROM_SPI_MISO_GPIO, EEPROM_SPI_MISO_PIN_SOURCE, EEPROM_SPI_MISO_AF);
GPIO_PinAFConfig(EEPROM_SPI_MOSI_GPIO, EEPROM_SPI_MOSI_PIN_SOURCE, EEPROM_SPI_MOSI_AF);
// Configure SPI
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(EEPROM_SPI, &SPI_InitStructure);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // Enable DMA1 clock
DMA_DeInit(EEPROM_SPI_DMA_TX_CHANNEL); // Reset DMA1 channe1 to default values;
DMA_DeInit(EEPROM_SPI_DMA_RX_CHANNEL); // Reset DMA1 channe1 to default values;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // M2M Disabled- Peripheral mode (requires timer trigger)
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // Normal mode
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; // Medium priority
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // Memory to Peripheral
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 8-bit Register
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // Always write to same register
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&EEPROM_SPI->DR; // Output data for SPI peripheral
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 8-bit array
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // Increment through array
DMA_InitStructure.DMA_MemoryBaseAddr = 0; // Initialize later
DMA_InitStructure.DMA_BufferSize = 1; // Initialize later
DMA_Init(EEPROM_SPI_DMA_TX_CHANNEL, &DMA_InitStructure); // Initialize TX DMA
// Initialize RX DMA
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // Peripheral to Memory
DMA_Init(EEPROM_SPI_DMA_RX_CHANNEL, &DMA_InitStructure); // Initialize RX DMA
}
void EEPROM_SPITransaction(uint8_t* commandData, uint8_t commandLength, const uint8_t* responseData, uint8_t responseLength)
{
DMA_SetCurrDataCounter(EEPROM_SPI_DMA_TX_CHANNEL, commandLength);
DMA_SetCurrDataCounter(EEPROM_SPI_DMA_RX_CHANNEL, responseLength);
// Configure the peripheral base address
EEPROM_SPI_DMA_TX_CHANNEL->CMAR = (uint32_t)commandData;
EEPROM_SPI_DMA_RX_CHANNEL->CMAR = (uint32_t)responseData;
/* The Data transfer is performed in the SPI using Direct Memory Access */
/* Enable DMA SPI TX Stream */
DMA_Cmd(EEPROM_SPI_DMA_TX_CHANNEL, ENABLE);
/* Enable DMA SPI RX Stream */
DMA_Cmd(EEPROM_SPI_DMA_RX_CHANNEL, ENABLE);
// Assert the CS
EEPROM_CS_Low();
/* Enable SPI DMA TX Requsts */
SPI_I2S_DMACmd(EEPROM_SPI, SPI_I2S_DMAReq_Tx, ENABLE);
/* Enable SPI DMA RX Requsts */
SPI_I2S_DMACmd(EEPROM_SPI, SPI_I2S_DMAReq_Rx, ENABLE);
/* Enable the SPI peripheral */
SPI_Cmd(EEPROM_SPI, ENABLE);
/* Waiting the end of Data transfer */
volatile unsigned int timeoutCounter = 0;
while ((DMA_GetFlagStatus(DMA1_FLAG_TC5)==RESET) && (timeoutCounter < EEPROM_TIMEOUT))
{
timeoutCounter++;
}
timeoutCounter = 0;
while ((DMA_GetFlagStatus(DMA1_FLAG_TC4)==RESET) && (timeoutCounter < EEPROM_TIMEOUT))
{
timeoutCounter++;
}
/* Clear DMA Flags */
DMA_ClearFlag(DMA1_FLAG_GL4 | DMA1_FLAG_HT4 | DMA1_FLAG_TC4 | DMA1_FLAG_GL5 | DMA1_FLAG_HT5 | DMA1_FLAG_TC5);
/* Disable DMA SPI TX Stream */
DMA_Cmd(EEPROM_SPI_DMA_TX_CHANNEL,DISABLE);
/* Disable DMA SPI RX Stream */
DMA_Cmd(EEPROM_SPI_DMA_RX_CHANNEL,DISABLE);
/* Disable SPI DMA TX Requsts */
SPI_I2S_DMACmd(EEPROM_SPI, SPI_I2S_DMAReq_Tx, DISABLE);
/* Disable SPI DMA RX Requsts */
SPI_I2S_DMACmd(EEPROM_SPI, SPI_I2S_DMAReq_Rx, DISABLE);
/* Disable the SPI peripheral */
SPI_Cmd(EEPROM_SPI, DISABLE);
// Release the CS
EEPROM_CS_High();
}
我在这件事上快要撞墙了
我解决了这个问题。
问题在于我正在执行字节范围的事务,默认情况下,当 FIFO 阈值达到 1/2 满时,会触发 RXNE(RX 缓冲区非空)标志。这个微控制器上的 FIFO 有 4 个字节深,所以等待 2 个字节填满会在错误的时间触发它。
我通过在初始化代码中添加以下内容,将其更改为在 FIFO 满 1/4 时触发:
// RXNE event is generated if the FIFO level is greater or equal to 1/4 (of 4 bytes) since we're dealing with byte long transfers
SPI_RxFIFOThresholdConfig(EEPROM_SPI,SPI_RxFIFOThreshold_QF);
我正在使用 STM32F303VC,试图通过 DMA 连接到 SPI EEPROM,但在接收数据时遇到了一些问题。 DMA 设置为全双工,使用逻辑分析仪查看信号时,我可以看到传输和响应正常。问题出在 DMA 接收缓冲区中。由于某种原因,接收缓冲区没有显示正确的数据。看起来可能存在一些对齐问题,因为我看到了一些正确的值,只是顺序不正确。比如我是
发送以下内容:
1.
MOSI: 0x06
MISO: 0xFF
2.
MOSI: 0x05 0xFF
MISO: 0xFF 0x02
3.
MOSI: 0x05 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
MISO: 0xFF 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02
这些是上述每种情况下接收缓冲区的内容
0x00 (expected 0xFF)
0x02 0x00 (expected 0xFF, 0x02)
0x02 0xFF, 0x02, 0xFF, 0x02, 0x02, 0x02, 0x02 (expected 0xFF, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02)
这是我的初始化代码和函数调用:
// Define for SPI DMA channels
#define EEPROM_SPI_DMA_RX_CHANNEL DMA1_Channel4 /* SPI2 RX DMA is handled by DMA 1 Channel 4 */
#define EEPROM_SPI_DMA_TX_CHANNEL DMA1_Channel5 /* SPI2 TX DMA is handled by DMA 1 Channel 5 */
void EEPROM_Initialize(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// SPI CS, SCK, MISO and MOSI peripheral clock enable
RCC_AHBPeriphClockCmd(EEPROM_SPI_CLK | EEPROM_SPI_CS_GPIO_CLK | EEPROM_SPI_SCK_GPIO_CLK |
EEPROM_SPI_MISO_GPIO_CLK | EEPROM_SPI_MOSI_GPIO_CLK , ENABLE);
// Enable DMA1 clock
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// SPI Perihperal clock enable
RCC_APB1PeriphClockCmd(EEPROM_SPI_CLK, ENABLE);
// Configure SPI pins: CS
GPIO_InitStructure.GPIO_Pin = EEPROM_SPI_CS_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(EEPROM_SPI_CS_GPIO, &GPIO_InitStructure);
GPIO_SetBits(EEPROM_SPI_CS_GPIO, EEPROM_SPI_CS_PIN); // Drive CS high
// Configure SPI pins: SCK
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // Now that CS GPIO has been initialized to Mode: OUT, change GPIO Mode to AF
GPIO_InitStructure.GPIO_Pin = EEPROM_SPI_SCK_PIN;
GPIO_Init(EEPROM_SPI_SCK_GPIO, &GPIO_InitStructure);
// Configure SPI pins: MISO
GPIO_InitStructure.GPIO_Pin = EEPROM_SPI_MISO_PIN;
GPIO_Init(EEPROM_SPI_MISO_GPIO, &GPIO_InitStructure);
// Configure SPI pins: MOSI
GPIO_InitStructure.GPIO_Pin = EEPROM_SPI_MOSI_PIN;
GPIO_Init(EEPROM_SPI_MOSI_GPIO, &GPIO_InitStructure);
// Configure alternate function to SPI related GPIOs to act as SPI peripheral
GPIO_PinAFConfig(EEPROM_SPI_SCK_GPIO, EEPROM_SPI_SCK_PIN_SOURCE, EEPROM_SPI_SCK_AF);
GPIO_PinAFConfig(EEPROM_SPI_MISO_GPIO, EEPROM_SPI_MISO_PIN_SOURCE, EEPROM_SPI_MISO_AF);
GPIO_PinAFConfig(EEPROM_SPI_MOSI_GPIO, EEPROM_SPI_MOSI_PIN_SOURCE, EEPROM_SPI_MOSI_AF);
// Configure SPI
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(EEPROM_SPI, &SPI_InitStructure);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // Enable DMA1 clock
DMA_DeInit(EEPROM_SPI_DMA_TX_CHANNEL); // Reset DMA1 channe1 to default values;
DMA_DeInit(EEPROM_SPI_DMA_RX_CHANNEL); // Reset DMA1 channe1 to default values;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // M2M Disabled- Peripheral mode (requires timer trigger)
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // Normal mode
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; // Medium priority
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // Memory to Peripheral
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 8-bit Register
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // Always write to same register
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&EEPROM_SPI->DR; // Output data for SPI peripheral
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 8-bit array
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // Increment through array
DMA_InitStructure.DMA_MemoryBaseAddr = 0; // Initialize later
DMA_InitStructure.DMA_BufferSize = 1; // Initialize later
DMA_Init(EEPROM_SPI_DMA_TX_CHANNEL, &DMA_InitStructure); // Initialize TX DMA
// Initialize RX DMA
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // Peripheral to Memory
DMA_Init(EEPROM_SPI_DMA_RX_CHANNEL, &DMA_InitStructure); // Initialize RX DMA
}
void EEPROM_SPITransaction(uint8_t* commandData, uint8_t commandLength, const uint8_t* responseData, uint8_t responseLength)
{
DMA_SetCurrDataCounter(EEPROM_SPI_DMA_TX_CHANNEL, commandLength);
DMA_SetCurrDataCounter(EEPROM_SPI_DMA_RX_CHANNEL, responseLength);
// Configure the peripheral base address
EEPROM_SPI_DMA_TX_CHANNEL->CMAR = (uint32_t)commandData;
EEPROM_SPI_DMA_RX_CHANNEL->CMAR = (uint32_t)responseData;
/* The Data transfer is performed in the SPI using Direct Memory Access */
/* Enable DMA SPI TX Stream */
DMA_Cmd(EEPROM_SPI_DMA_TX_CHANNEL, ENABLE);
/* Enable DMA SPI RX Stream */
DMA_Cmd(EEPROM_SPI_DMA_RX_CHANNEL, ENABLE);
// Assert the CS
EEPROM_CS_Low();
/* Enable SPI DMA TX Requsts */
SPI_I2S_DMACmd(EEPROM_SPI, SPI_I2S_DMAReq_Tx, ENABLE);
/* Enable SPI DMA RX Requsts */
SPI_I2S_DMACmd(EEPROM_SPI, SPI_I2S_DMAReq_Rx, ENABLE);
/* Enable the SPI peripheral */
SPI_Cmd(EEPROM_SPI, ENABLE);
/* Waiting the end of Data transfer */
volatile unsigned int timeoutCounter = 0;
while ((DMA_GetFlagStatus(DMA1_FLAG_TC5)==RESET) && (timeoutCounter < EEPROM_TIMEOUT))
{
timeoutCounter++;
}
timeoutCounter = 0;
while ((DMA_GetFlagStatus(DMA1_FLAG_TC4)==RESET) && (timeoutCounter < EEPROM_TIMEOUT))
{
timeoutCounter++;
}
/* Clear DMA Flags */
DMA_ClearFlag(DMA1_FLAG_GL4 | DMA1_FLAG_HT4 | DMA1_FLAG_TC4 | DMA1_FLAG_GL5 | DMA1_FLAG_HT5 | DMA1_FLAG_TC5);
/* Disable DMA SPI TX Stream */
DMA_Cmd(EEPROM_SPI_DMA_TX_CHANNEL,DISABLE);
/* Disable DMA SPI RX Stream */
DMA_Cmd(EEPROM_SPI_DMA_RX_CHANNEL,DISABLE);
/* Disable SPI DMA TX Requsts */
SPI_I2S_DMACmd(EEPROM_SPI, SPI_I2S_DMAReq_Tx, DISABLE);
/* Disable SPI DMA RX Requsts */
SPI_I2S_DMACmd(EEPROM_SPI, SPI_I2S_DMAReq_Rx, DISABLE);
/* Disable the SPI peripheral */
SPI_Cmd(EEPROM_SPI, DISABLE);
// Release the CS
EEPROM_CS_High();
}
我在这件事上快要撞墙了
我解决了这个问题。
问题在于我正在执行字节范围的事务,默认情况下,当 FIFO 阈值达到 1/2 满时,会触发 RXNE(RX 缓冲区非空)标志。这个微控制器上的 FIFO 有 4 个字节深,所以等待 2 个字节填满会在错误的时间触发它。
我通过在初始化代码中添加以下内容,将其更改为在 FIFO 满 1/4 时触发:
// RXNE event is generated if the FIFO level is greater or equal to 1/4 (of 4 bytes) since we're dealing with byte long transfers
SPI_RxFIFOThresholdConfig(EEPROM_SPI,SPI_RxFIFOThreshold_QF);