STM32L4 SPI 使用 DMA 传输完成中断仅触发一次
STM32L4 SPI Transfer complete interrupt using DMA fires only once
我正在尝试使用 SPI 和 DMA 在 2 个核板 (NUCLEO-L432KCU) 之间发送一个 10 字节的数组。我的目标是使用 Low Level APIs 为从板开发代码。主控板只是用来做测试,等一切正常后,再换成真正的系统。
在继续之前,这里有一些关于系统的更多细节:发件人被配置为主。 master 的代码是使用 HAL API 开发的。主板上的芯片Select是使用GPIO实现的。
接收器配置为从选项 Receive only slave 启用和 Hardware NSS input。使用 CubeMX 工具自动生成初始化代码G。
使用我当前的实现,我能够在从板上接收数据,但只能接收一次:实际上中断似乎只触发一次,我很难弄清楚我错过了什么!
我认为该错误与清除某些中断标志有关。我查看了参考手册,但看不出我做错了什么。
以下是我的发件人和收件人代码。
发件人代码
注意:关于发件人,我只报告主要功能,因为所有其他代码都是自动生成的。此外,我用逻辑分析器检查了代码是否有效。如果您需要更多详细信息,请告诉我。
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_SPI1_Init();
MX_SPI3_Init();
MX_USART2_UART_Init();
MX_TIM1_Init();
/* USER CODE BEGIN 2 */
uint8_t test[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A};
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,RESET);
HAL_SPI_Transmit(&hspi1,test,sizeof(test),1000);
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,SET);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
接收器代码
注意:DMA和SPI的配置大部分由CubeMX工具自动完成。我的项目的其他初始化提供到主函数中。
uint8_t aRxBuffer[10];
uint8_t received_buffer[100];
uint16_t cnt = 0;
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_SPI1_Init();
MX_SPI3_Init();
MX_USART2_UART_Init();
MX_TIM1_Init();
/* USER CODE BEGIN 2 */
// Custom configuration of DMA (after calling function MX_SPI3_INIT()
// Configure address of the buffer for receiving data
LL_DMA_ConfigAddresses(DMA2, LL_DMA_CHANNEL_1, LL_SPI_DMA_GetRegAddr(SPI3), (uint32_t)aRxBuffer,LL_DMA_GetDataTransferDirection(DMA2, LL_DMA_CHANNEL_1));
// Configure data length
LL_DMA_SetDataLength(DMA2, LL_DMA_CHANNEL_1,10);
// Enable DMA Transfer complete interrupt
LL_DMA_EnableIT_TC(DMA2, LL_DMA_CHANNEL_1);
// LL_DMA_EnableIT_TE(DMA2, LL_DMA_CHANNEL_1);
// We Want the SPI3 to receive 8-bit data
// Therefore we trigger the RXNE interrupt when the FIFO level is greater than or equal to 1/4 (8bit)
// See pag. 1221 of the TRM
LL_SPI_SetRxFIFOThreshold(SPI3,LL_SPI_RX_FIFO_TH_QUARTER);
LL_SPI_EnableDMAReq_RX(SPI3);
// Enable SPI_3
LL_SPI_Enable(SPI3);
// Enable DMA_2,CHANNEL_1
LL_DMA_EnableChannel(DMA2, LL_DMA_CHANNEL_1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
以下是 IRQ 处理程序(注释代码代表了使其工作的各种尝试!):
void DMA2_Channel1_IRQHandler(void)
{
/* USER CODE BEGIN DMA2_Channel1_IRQn 0 */
// Transfer-complete interrupt management
if(LL_DMA_IsActiveFlag_TC1(DMA2))
{
//LL_DMA_ClearFlag_TC1(DMA2);
LL_DMA_ClearFlag_GI1(DMA2);
/* Call function Transmission complete Callback */
DMA1_TransmitComplete_Callback();
}
else if(LL_DMA_IsActiveFlag_TE1(DMA2))
{
/* Call Error function */
int _error = 0;
}
// Enable SPI_3
//LL_SPI_Disable(SPI3);
// Enable DMA_2,CHANNEL_1
//LL_DMA_DisableChannel(DMA2, LL_DMA_CHANNEL_1);
//LL_DMA_EnableIT_TC(DMA2, LL_DMA_CHANNEL_1);
// LL_DMA_EnableIT_TE(DMA2, LL_DMA_CHANNEL_1);
// We Want the SPI3 to receive 8-bit data
// Therefore we trigger the RXNE interrupt when the FIFO level is greater than or equal to 1/4 (8bit)
// See pag. 1221 of the TRM
//LL_SPI_SetRxFIFOThreshold(SPI3,LL_SPI_RX_FIFO_TH_QUARTER);
//LL_SPI_EnableDMAReq_RX(SPI3);
// Enable SPI_3
//LL_SPI_Enable(SPI3);
// Enable DMA_2,CHANNEL_1
LL_DMA_EnableChannel(DMA2, LL_DMA_CHANNEL_1);
// LL_DMA_EnableIT_TE(DMA2, LL_DMA_CHANNEL_1);
/* USER CODE END DMA2_Channel1_IRQn 0 */
/* USER CODE BEGIN DMA2_Channel1_IRQn 1 */
/* USER CODE END DMA2_Channel1_IRQn 1 */
}
以下是 SPI 和 DMA(自动生成)的初始化:
/* SPI1 init function */
void MX_SPI1_Init(void)
{
LL_SPI_InitTypeDef SPI_InitStruct;
LL_GPIO_InitTypeDef GPIO_InitStruct;
/* Peripheral clock enable */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);
/**SPI1 GPIO Configuration
PA1 ------> SPI1_SCK
PA7 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = SCLK1_to_SpW_Pin|MOSI1_to_SpW_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_4BIT;
SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;
SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8;
SPI_InitStruct.BitOrder = LL_SPI_LSB_FIRST;
SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
SPI_InitStruct.CRCPoly = 7;
LL_SPI_Init(SPI1, &SPI_InitStruct);
LL_SPI_SetStandard(SPI1, LL_SPI_PROTOCOL_MOTOROLA);
LL_SPI_EnableNSSPulseMgt(SPI1);
}
/* SPI3 init function */
void MX_SPI3_Init(void)
{
LL_SPI_InitTypeDef SPI_InitStruct;
LL_GPIO_InitTypeDef GPIO_InitStruct;
/* Peripheral clock enable */
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI3);
/**SPI3 GPIO Configuration
PA4 ------> SPI3_NSS
PB3 (JTDO-TRACESWO) ------> SPI3_SCK
PB5 ------> SPI3_MOSI
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_4;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_6;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = SCLK_from_SpW_Pin|MOSI_from_SpW_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_6;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* SPI3 DMA Init */
/* SPI3_RX Init */
LL_DMA_SetPeriphRequest(DMA2, LL_DMA_CHANNEL_1, LL_DMA_REQUEST_3);
LL_DMA_SetDataTransferDirection(DMA2, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetChannelPriorityLevel(DMA2, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(DMA2, LL_DMA_CHANNEL_1, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA2, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA2, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA2, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA2, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_BYTE);
/* SPI3 interrupt Init */
NVIC_SetPriority(SPI3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
NVIC_EnableIRQ(SPI3_IRQn);
SPI_InitStruct.TransferDirection = LL_SPI_SIMPLEX_RX;
SPI_InitStruct.Mode = LL_SPI_MODE_SLAVE;
SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_4BIT;
SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;
SPI_InitStruct.NSS = LL_SPI_NSS_HARD_INPUT;
SPI_InitStruct.BitOrder = LL_SPI_LSB_FIRST;
SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
SPI_InitStruct.CRCPoly = 7;
LL_SPI_Init(SPI3, &SPI_InitStruct);
LL_SPI_SetStandard(SPI3, LL_SPI_PROTOCOL_MOTOROLA);
LL_SPI_DisableNSSPulseMgt(SPI3);
}
谢谢。
我最近实现了一个类似的系统,希望对大家有所帮助。我有几个问题,意见,可能可以解决你的问题,但如果不在场就很难做到。
- 你知道是SPI还是DMA故障吗?从机上是否发生 SPI 中断?这意味着 DMA 有故障,而不是 SPI。准确了解系统故障位置很重要。
LL_SPI_SetRxFIFOThreshold(SPI3,LL_SPI_RX_FIFO_TH_QUARTER);
是必要的,但应该在 init
期间完成
TCIF
标志应该在 IRQ
期间清除(就像您所做的那样)。
- 您应该使用
SPI_CR2_RXDMAEN
寄存器将 SPI 设置为触发 DMA(我在您的代码中没有看到它)。如果您不知道何时会收到数据,您也应该在 init
期间执行此操作。
- 出于同样的原因,我认为您应该在
init
期间启用 DMA 通道并保持启用状态。
我希望这些评论之一对您有所帮助。如果不行,我们再试一次。
编辑:干得好。我很高兴你解决了大部分问题 运行。根据您提供的信息,我弄清楚了缓冲区的主要问题是什么。
您将 DMA 设置为接收 10 个字节:
LL_DMA_SetDataLength(DMA2, LL_DMA_CHANNEL_1,10);
这会将 DMA 内部计数器设置为 10。对于它接收到的每个字节,计数器减一,直到它达到零。这就是它能够计算 10 个字节的原因。在normal模式下,如果你想再接收10个字节,那么你需要再次发送那个命令。在 circular 模式下,此值将 自动 重置为 10,这意味着它可以再接收 10 个字节。
因此,如果您希望总是 接收 10 个字节,那么 cicular 模式应该适合您。如果不是,那么你将不得不使用正常模式,并向MCU指定你期望的字节数(稍微复杂一点)。
来自代码
stm32l4xx_hal_spi.c: 55
Master Receive mode restriction:
(#) In Master unidirectional receive-only mode (MSTR =1, BIDIMODE=0, RXONLY=1)
or bidirectional receive mode (MSTR=1, BIDIMODE=1, BIDIOE=0), to ensure
that the SPI does not initiate a new transfer the following procedure has
to be respected:
(##) HAL_SPI_DeInit()
(##) HAL_SPI_Init()
所以在你调用 HAL_SPI_Receive_DMA() 之前
调用 HAL_SPI_DeInit 和 HAL_SPI_Init 它应该可以工作。
我发现只要你打电话
HAL_DMA_DeInit(HSPI_Handle->hdmatx) ;
HAL_DMA_Init(HSPI_Handle->hdmtx);
同样有效,仅 70us vs 106us。
我正在尝试使用 SPI 和 DMA 在 2 个核板 (NUCLEO-L432KCU) 之间发送一个 10 字节的数组。我的目标是使用 Low Level APIs 为从板开发代码。主控板只是用来做测试,等一切正常后,再换成真正的系统。
在继续之前,这里有一些关于系统的更多细节:发件人被配置为主。 master 的代码是使用 HAL API 开发的。主板上的芯片Select是使用GPIO实现的。 接收器配置为从选项 Receive only slave 启用和 Hardware NSS input。使用 CubeMX 工具自动生成初始化代码G。
使用我当前的实现,我能够在从板上接收数据,但只能接收一次:实际上中断似乎只触发一次,我很难弄清楚我错过了什么!
我认为该错误与清除某些中断标志有关。我查看了参考手册,但看不出我做错了什么。
以下是我的发件人和收件人代码。
发件人代码
注意:关于发件人,我只报告主要功能,因为所有其他代码都是自动生成的。此外,我用逻辑分析器检查了代码是否有效。如果您需要更多详细信息,请告诉我。
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_SPI1_Init();
MX_SPI3_Init();
MX_USART2_UART_Init();
MX_TIM1_Init();
/* USER CODE BEGIN 2 */
uint8_t test[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A};
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,RESET);
HAL_SPI_Transmit(&hspi1,test,sizeof(test),1000);
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,SET);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
接收器代码 注意:DMA和SPI的配置大部分由CubeMX工具自动完成。我的项目的其他初始化提供到主函数中。
uint8_t aRxBuffer[10];
uint8_t received_buffer[100];
uint16_t cnt = 0;
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_SPI1_Init();
MX_SPI3_Init();
MX_USART2_UART_Init();
MX_TIM1_Init();
/* USER CODE BEGIN 2 */
// Custom configuration of DMA (after calling function MX_SPI3_INIT()
// Configure address of the buffer for receiving data
LL_DMA_ConfigAddresses(DMA2, LL_DMA_CHANNEL_1, LL_SPI_DMA_GetRegAddr(SPI3), (uint32_t)aRxBuffer,LL_DMA_GetDataTransferDirection(DMA2, LL_DMA_CHANNEL_1));
// Configure data length
LL_DMA_SetDataLength(DMA2, LL_DMA_CHANNEL_1,10);
// Enable DMA Transfer complete interrupt
LL_DMA_EnableIT_TC(DMA2, LL_DMA_CHANNEL_1);
// LL_DMA_EnableIT_TE(DMA2, LL_DMA_CHANNEL_1);
// We Want the SPI3 to receive 8-bit data
// Therefore we trigger the RXNE interrupt when the FIFO level is greater than or equal to 1/4 (8bit)
// See pag. 1221 of the TRM
LL_SPI_SetRxFIFOThreshold(SPI3,LL_SPI_RX_FIFO_TH_QUARTER);
LL_SPI_EnableDMAReq_RX(SPI3);
// Enable SPI_3
LL_SPI_Enable(SPI3);
// Enable DMA_2,CHANNEL_1
LL_DMA_EnableChannel(DMA2, LL_DMA_CHANNEL_1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
以下是 IRQ 处理程序(注释代码代表了使其工作的各种尝试!):
void DMA2_Channel1_IRQHandler(void)
{
/* USER CODE BEGIN DMA2_Channel1_IRQn 0 */
// Transfer-complete interrupt management
if(LL_DMA_IsActiveFlag_TC1(DMA2))
{
//LL_DMA_ClearFlag_TC1(DMA2);
LL_DMA_ClearFlag_GI1(DMA2);
/* Call function Transmission complete Callback */
DMA1_TransmitComplete_Callback();
}
else if(LL_DMA_IsActiveFlag_TE1(DMA2))
{
/* Call Error function */
int _error = 0;
}
// Enable SPI_3
//LL_SPI_Disable(SPI3);
// Enable DMA_2,CHANNEL_1
//LL_DMA_DisableChannel(DMA2, LL_DMA_CHANNEL_1);
//LL_DMA_EnableIT_TC(DMA2, LL_DMA_CHANNEL_1);
// LL_DMA_EnableIT_TE(DMA2, LL_DMA_CHANNEL_1);
// We Want the SPI3 to receive 8-bit data
// Therefore we trigger the RXNE interrupt when the FIFO level is greater than or equal to 1/4 (8bit)
// See pag. 1221 of the TRM
//LL_SPI_SetRxFIFOThreshold(SPI3,LL_SPI_RX_FIFO_TH_QUARTER);
//LL_SPI_EnableDMAReq_RX(SPI3);
// Enable SPI_3
//LL_SPI_Enable(SPI3);
// Enable DMA_2,CHANNEL_1
LL_DMA_EnableChannel(DMA2, LL_DMA_CHANNEL_1);
// LL_DMA_EnableIT_TE(DMA2, LL_DMA_CHANNEL_1);
/* USER CODE END DMA2_Channel1_IRQn 0 */
/* USER CODE BEGIN DMA2_Channel1_IRQn 1 */
/* USER CODE END DMA2_Channel1_IRQn 1 */
}
以下是 SPI 和 DMA(自动生成)的初始化:
/* SPI1 init function */
void MX_SPI1_Init(void)
{
LL_SPI_InitTypeDef SPI_InitStruct;
LL_GPIO_InitTypeDef GPIO_InitStruct;
/* Peripheral clock enable */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);
/**SPI1 GPIO Configuration
PA1 ------> SPI1_SCK
PA7 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = SCLK1_to_SpW_Pin|MOSI1_to_SpW_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_4BIT;
SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;
SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8;
SPI_InitStruct.BitOrder = LL_SPI_LSB_FIRST;
SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
SPI_InitStruct.CRCPoly = 7;
LL_SPI_Init(SPI1, &SPI_InitStruct);
LL_SPI_SetStandard(SPI1, LL_SPI_PROTOCOL_MOTOROLA);
LL_SPI_EnableNSSPulseMgt(SPI1);
}
/* SPI3 init function */
void MX_SPI3_Init(void)
{
LL_SPI_InitTypeDef SPI_InitStruct;
LL_GPIO_InitTypeDef GPIO_InitStruct;
/* Peripheral clock enable */
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI3);
/**SPI3 GPIO Configuration
PA4 ------> SPI3_NSS
PB3 (JTDO-TRACESWO) ------> SPI3_SCK
PB5 ------> SPI3_MOSI
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_4;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_6;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = SCLK_from_SpW_Pin|MOSI_from_SpW_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_6;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* SPI3 DMA Init */
/* SPI3_RX Init */
LL_DMA_SetPeriphRequest(DMA2, LL_DMA_CHANNEL_1, LL_DMA_REQUEST_3);
LL_DMA_SetDataTransferDirection(DMA2, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetChannelPriorityLevel(DMA2, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(DMA2, LL_DMA_CHANNEL_1, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA2, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA2, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA2, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA2, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_BYTE);
/* SPI3 interrupt Init */
NVIC_SetPriority(SPI3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
NVIC_EnableIRQ(SPI3_IRQn);
SPI_InitStruct.TransferDirection = LL_SPI_SIMPLEX_RX;
SPI_InitStruct.Mode = LL_SPI_MODE_SLAVE;
SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_4BIT;
SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;
SPI_InitStruct.NSS = LL_SPI_NSS_HARD_INPUT;
SPI_InitStruct.BitOrder = LL_SPI_LSB_FIRST;
SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
SPI_InitStruct.CRCPoly = 7;
LL_SPI_Init(SPI3, &SPI_InitStruct);
LL_SPI_SetStandard(SPI3, LL_SPI_PROTOCOL_MOTOROLA);
LL_SPI_DisableNSSPulseMgt(SPI3);
}
谢谢。
我最近实现了一个类似的系统,希望对大家有所帮助。我有几个问题,意见,可能可以解决你的问题,但如果不在场就很难做到。
- 你知道是SPI还是DMA故障吗?从机上是否发生 SPI 中断?这意味着 DMA 有故障,而不是 SPI。准确了解系统故障位置很重要。
LL_SPI_SetRxFIFOThreshold(SPI3,LL_SPI_RX_FIFO_TH_QUARTER);
是必要的,但应该在init
期间完成
TCIF
标志应该在IRQ
期间清除(就像您所做的那样)。- 您应该使用
SPI_CR2_RXDMAEN
寄存器将 SPI 设置为触发 DMA(我在您的代码中没有看到它)。如果您不知道何时会收到数据,您也应该在init
期间执行此操作。 - 出于同样的原因,我认为您应该在
init
期间启用 DMA 通道并保持启用状态。
我希望这些评论之一对您有所帮助。如果不行,我们再试一次。
编辑:干得好。我很高兴你解决了大部分问题 运行。根据您提供的信息,我弄清楚了缓冲区的主要问题是什么。
您将 DMA 设置为接收 10 个字节:
LL_DMA_SetDataLength(DMA2, LL_DMA_CHANNEL_1,10);
这会将 DMA 内部计数器设置为 10。对于它接收到的每个字节,计数器减一,直到它达到零。这就是它能够计算 10 个字节的原因。在normal模式下,如果你想再接收10个字节,那么你需要再次发送那个命令。在 circular 模式下,此值将 自动 重置为 10,这意味着它可以再接收 10 个字节。
因此,如果您希望总是 接收 10 个字节,那么 cicular 模式应该适合您。如果不是,那么你将不得不使用正常模式,并向MCU指定你期望的字节数(稍微复杂一点)。
来自代码 stm32l4xx_hal_spi.c: 55
Master Receive mode restriction:
(#) In Master unidirectional receive-only mode (MSTR =1, BIDIMODE=0, RXONLY=1)
or bidirectional receive mode (MSTR=1, BIDIMODE=1, BIDIOE=0), to ensure
that the SPI does not initiate a new transfer the following procedure has
to be respected:
(##) HAL_SPI_DeInit()
(##) HAL_SPI_Init()
所以在你调用 HAL_SPI_Receive_DMA() 之前 调用 HAL_SPI_DeInit 和 HAL_SPI_Init 它应该可以工作。
我发现只要你打电话
HAL_DMA_DeInit(HSPI_Handle->hdmatx) ;
HAL_DMA_Init(HSPI_Handle->hdmtx);
同样有效,仅 70us vs 106us。