我一直在尝试使用DMA和STM32 LL驱动程序使SPI主传输正常工作,在STM32G030C8上。我已经成功地使用LL驱动程序而没有DMA使SPI工作,因此我相信至少我的连线是正确的。
我所做的事情:
1. 在cubeMX中设置SPI使用DMA,将SPI1_TX请求设置为DMA1通道1。 2. 在代码中设置传输:
main.c
从参考手册中,我找到了以下DMA配置步骤:
通道配置过程 需要配置DMA通道x的以下序列:
1. 在DMA_CPARx寄存器中设置外设寄存器地址。在外设事件之后,或在内存到内存模式下启用通道后,数据将从/到该地址移动到/从内存中。 2. 在DMA_CMARx寄存器中设置内存地址。在外设事件之后或在内存到内存模式下启用通道后,数据将写入/从内存中读取。 3. 在DMA_CNDTRx寄存器中配置要传输的总数据量。每次数据传输后,该值都会减少。 4. 在DMA_CCRx寄存器中配置以下参数: - 通道优先级 - 数据传输方向 - 循环模式 - 外设和内存递增模式 - 外设和内存数据大小 - 在半传输和/或全传输和/或传输错误时使能中断 5. 通过在DMA_CCRx寄存器中设置EN位来激活通道。一旦启用通道,它就可以为连接到该通道的外设提供任何DMA请求,或者可以启动内存到内存块传输。
据我理解,步骤1、2、3和5在main.c中完成,步骤4已在spi.c中完成。
参考手册还介绍了有关SPI和DMA的信息:
当在SPIx_CR2寄存器中设置TXE或RXNE使能位时,将请求DMA访问。必须向Tx和Rx缓冲区分别发出请求。
- 在传输中,每次将TXE设置为1时,都会发出DMA请求。然后DMA写入SPIx_DR寄存器
并且
使用DMA开始通信时,为了防止DMA通道管理引发错误事件,必须按照以下顺序执行这些步骤:
1. 如果使用DMA Rx,则在SPI_CR2寄存器的RXDMAEN位中启用DMA Rx缓冲区。 2. 如果使用流,则在DMA寄存器中启用Tx和Rx DMA流。 3. 如果使用DMA Tx,则在SPI_CR2寄存器的TXDMAEN位中启用DMA Tx缓冲区。 4. 通过设置SPE位来启用SPI。
据我理解,我已经完成了所有步骤,但是我无法看到连接到SPI1线路的示波器上的任何内容。
我一定遗漏了什么(或者某些步骤顺序有误),但我无法弄清楚哪里出了问题。
在其他一些问题中,问题是DMA通道错误,并且不支持SPI,但是在此MCU中,如果我理解正确,DMAMUX处理这个问题,任何信号都应该可用于任何DMA通道?(在spi.c中配置)
我所做的事情:
1. 在cubeMX中设置SPI使用DMA,将SPI1_TX请求设置为DMA1通道1。 2. 在代码中设置传输:
main.c
#include "main.h"
#include "dma.h"
#include "gpio.h"
#include "spi.h"
uint8_t test_data[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
void SystemClock_Config(void);
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SPI1_Init();
MX_DMA_Init();
while (1) {
LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_1, (uint32_t)(&test_data[0]),
(uint32_t)LL_SPI_DMA_GetRegAddr(SPI1),
LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1));
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 8);
LL_SPI_EnableDMAReq_TX(SPI1);
LL_SPI_Enable(SPI1);
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
HAL_Delay(1000);
HAL_GPIO_TogglePin(STATUS_LED_GPIO_Port, STATUS_LED_Pin);
}
}
spi.c:
#include "spi.h"
void MX_SPI1_Init(void)
{
LL_SPI_InitTypeDef SPI_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = LL_GPIO_PIN_1;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_0;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = LL_GPIO_PIN_2;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_0;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_1, LL_DMAMUX_REQ_SPI1_TX);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_BYTE);
NVIC_SetPriority(SPI1_IRQn, 0);
NVIC_EnableIRQ(SPI1_IRQn);
SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
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_DIV4;
SPI_InitStruct.BitOrder = LL_SPI_MSB_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_DisableNSSPulseMgt(SPI1);
}
dma.c:
#include "dma.h"
void MX_DMA_Init(void)
{
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
NVIC_SetPriority(DMA1_Channel1_IRQn, 0);
NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
从参考手册中,我找到了以下DMA配置步骤:
通道配置过程 需要配置DMA通道x的以下序列:
1. 在DMA_CPARx寄存器中设置外设寄存器地址。在外设事件之后,或在内存到内存模式下启用通道后,数据将从/到该地址移动到/从内存中。 2. 在DMA_CMARx寄存器中设置内存地址。在外设事件之后或在内存到内存模式下启用通道后,数据将写入/从内存中读取。 3. 在DMA_CNDTRx寄存器中配置要传输的总数据量。每次数据传输后,该值都会减少。 4. 在DMA_CCRx寄存器中配置以下参数: - 通道优先级 - 数据传输方向 - 循环模式 - 外设和内存递增模式 - 外设和内存数据大小 - 在半传输和/或全传输和/或传输错误时使能中断 5. 通过在DMA_CCRx寄存器中设置EN位来激活通道。一旦启用通道,它就可以为连接到该通道的外设提供任何DMA请求,或者可以启动内存到内存块传输。
据我理解,步骤1、2、3和5在main.c中完成,步骤4已在spi.c中完成。
参考手册还介绍了有关SPI和DMA的信息:
当在SPIx_CR2寄存器中设置TXE或RXNE使能位时,将请求DMA访问。必须向Tx和Rx缓冲区分别发出请求。
- 在传输中,每次将TXE设置为1时,都会发出DMA请求。然后DMA写入SPIx_DR寄存器
并且
使用DMA开始通信时,为了防止DMA通道管理引发错误事件,必须按照以下顺序执行这些步骤:
1. 如果使用DMA Rx,则在SPI_CR2寄存器的RXDMAEN位中启用DMA Rx缓冲区。 2. 如果使用流,则在DMA寄存器中启用Tx和Rx DMA流。 3. 如果使用DMA Tx,则在SPI_CR2寄存器的TXDMAEN位中启用DMA Tx缓冲区。 4. 通过设置SPE位来启用SPI。
据我理解,我已经完成了所有步骤,但是我无法看到连接到SPI1线路的示波器上的任何内容。
我一定遗漏了什么(或者某些步骤顺序有误),但我无法弄清楚哪里出了问题。
在其他一些问题中,问题是DMA通道错误,并且不支持SPI,但是在此MCU中,如果我理解正确,DMAMUX处理这个问题,任何信号都应该可用于任何DMA通道?(在spi.c中配置)
编辑:
从SPI和DMA读取标志:
LL_SPI_IsActiveFlag_BSY(SPI1) returns 0
LL_SPI_IsEnabledDMAReq_TX(SPI1) returns 1
LL_SPI_IsEnabled(SPI1) returns 1
LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1) returns 1
LL_DMA_IsActiveFlag_TE1(DMA1) returns 0
LL_SPI_IsActiveFlag_TXE(SPI1) returns 1
看起来一切都已启用,没有错误,但是没有数据传输!
非常感谢您的帮助!