我一直试图让SPI主传输使用DMA和STM32 LL驱动程序,在STM32G030C8上工作。
我确实让SPI在没有DMA的情况下与LL驱动程序一起工作,所以我相信至少我的接线是正确的。
我所做的:
SPI1_TX
请求设置为DMA1通道1,将SPI1_TX
设置为在cubeMX中使用DMA
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);
}
}
螺旋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:
DMA_CPARx
寄存器中设置外围寄存器地址。数据在外围事件发生后或通道以内存到内存模式启用后从这个地址移动到/从这个地址移动到/从这个地址。DMA_CMARx
寄存器中设置内存地址。在外围事件之后或在内存到内存的mode.DMA_CNDTRx
寄存器中要传输的数据总数。在每次数据传输之后,此值为decremented.DMA_CCRx
寄存器中列出的参数:-通道优先级--数据传输方向--循环模式--外围设备和内存增量模式--外围设备和内存数据大小--中断启用一半和/或完全传输和/或通过在DMA_CCRx
寄存器中设置EN位来传输信道。通道一旦启用,就可以从连接到该通道的外围设备中处理任何DMA请求,或者启动内存到内存块的传输。。
根据我的理解,步骤1、2、3和5是在main.c中完成的,步骤4已经在spi.c中完成了。
也是在关于SPI和DMA的参考手册中:
在设置
SPIx_CR2
寄存器中的TXE或RXNE启用位时请求DMA访问。必须向Tx和Rx缓冲区发出单独的请求。
-In传输时,每次TXE设置为1时,都会发出DMA请求。
和
在使用DMA启动通信时,为了防止DMA信道管理引发错误事件,必须遵循以下步骤:
G 246
据我理解,我已经做了所有的步骤,但我看不到任何与我的示波器连接到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
因此,似乎所有的东西都启用了,没有错误,但是没有数据被传输!
任何帮助都是非常感谢的!谢谢!
发布于 2022-01-02 21:13:22
经过一段时间的调试,我发现STM32cubeMX代码生成器中有一个bug。(这似乎也已经被报道了(https://community.st.com/s/question/0D53W00001HJ3EhSAL/wrong-initialization-sequence-with-cubemx-in-cubeide-when-using-i2s-with-dma?t=1641156995520&searchQuery)
当选择SPI和DMA时,生成器首先初始化SPI,然后使用
MX_SPIx_Init();
MX_DMA_Init();
问题是SPI初始化尝试设置DMA寄存器,但DMA时钟尚未启用,因此不保存值。
这也是我让它与USART一起工作的原因,USART初始化是在DMA初始化之后出现的。
因此,简单的解决方案是在其他外围设备之前移动DMA初始化。但是,由于每次在cubeMX中进行更改时都会自动生成此代码,因此更改将丢失。
为了解决这个问题,我使用cubeMX在高级设置下禁用项目管理器选项卡中的自动初始化调用:
然后在以下用户代码段中手动调用这些初始化函数,现在如下所示:
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
/* USER CODE BEGIN 2 */
MX_SPI1_Init();
MX_USART1_UART_Init();
/* USER CODE END 2 */
https://stackoverflow.com/questions/70558087
复制相似问题