找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 576|回复: 3
打印 上一主题 下一主题
收起左侧

STM32F407标准库,SPI DMA中断方式收发数据,怎么也调不通,一进DMA收发中断程序就...

[复制链接]
回帖奖励 100 黑币 回复本帖可获得 10 黑币奖励! 每人限 5 次
跳转到指定楼层
楼主
ID:138119 发表于 2024-8-19 15:07 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
STM32F407标准库,SPI DMA中断方式收发数据,怎么也调不通,一进DMA收发中断程序死了~(等待方式没有问题),下面是代码,请高手哥哥姐姐帮忙,谢谢!

//STM32F407标准库,SPI DMA中断方式收发数据,怎么也调不通,一进DMA收发中断程序就...

#define SPI1_RX_LEN                        2000
#define SPI1_TX_LEN                        2000

#define SPI1_DR_ADDR        (uint32_t)0x4001300C

unsigned char SPI1_RX_BUFFER[SPI1_RX_LEN]={0};
unsigned char SPI1_TX_BUFFER[SPI1_TX_LEN]={0};

//函数名:初始化SPI接口
void SPI1_Init(void)
{
        GPIO_InitTypeDef  GPIO_InitStructure;
        SPI_InitTypeDef  SPI_InitStructure;
       
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOD, ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);                                //使能SPI1时钟
       
        GPIO_InitStructure.GPIO_Pin = SPI1_NSS_Pin;                                                        //IO
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;                                                //标准功能
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                                                //推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;                                        //100MHz
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;                                                //上拉
        GPIO_Init(SPI1_NSS_GPIO_Port, &GPIO_InitStructure);                                        //初始化
        SPI1_NSS_Set();        //NSS高
       
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;                        //PA5\6\7复用功能输出       
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;                                                        //复用功能
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                                                        //推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;                                                //100MHz
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;                                                        //上拉
        GPIO_Init(GPIOA, &GPIO_InitStructure);                                                                        //初始化
       
        GPIO_PinAFConfig(GPIOA,GPIO_PinSource5,GPIO_AF_SPI1);                                        //PB3复用为 SPI1
        GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_SPI1);                                        //PB4复用为 SPI1
        GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_SPI1);                                        //PB5复用为 SPI1
        //只针对SPI口初始化
        RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);                                                //复位SPI1
        RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);                                        //停止复位SPI1
       
        SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;                //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
        SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                                                        //设置SPI工作模式:设置为主SPI
        SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                                                //设置SPI的数据大小:SPI发送接收8位帧结构
        SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;                                                                //串行同步时钟的空闲状态为高电平
        SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;                                                        //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
        SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                                                                //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
        SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;                //定义波特率预分频的值:波特率预分频值为2:42M;4:21M;8:10.5M;16:5.25M
        SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                                                //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
        SPI_InitStructure.SPI_CRCPolynomial = 7;                                                                //CRC值计算的多项式
        SPI_Init(SPI1, &SPI_InitStructure);                                                                          //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器

        SPI_Cmd(SPI1, ENABLE);                                //使能SPI外设
        spi1_dma_init(41,41);                                //DMA初始化
        SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);
        SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE);
}

void spi1_dma_init(unsigned short RXSize,unsigned short TXSize)
{
        DMA_InitTypeDef DMA_InitStructure;
        NVIC_InitTypeDef NVIC_InitStructure;
        /*DMA2*/
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
        /* DMA RX config */
        DMA_InitStructure.DMA_Channel = DMA_Channel_3;                                                        //DMA  通道
        DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_ADDR;                                 //外设地址
        DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)SPI1_RX_BUFFER;                //接收缓冲区(内存中的有一个数组)
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;                                        //DMA 传输方向
        DMA_InitStructure.DMA_BufferSize = RXSize;                                                                //DMA 传输的数量        这个后期还可以再改
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                //外设地址自增  取消
        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                                        //内存地址自增  使能
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;                        //传输的 单位 (byte  8bit)
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;        //传输的 单位 (byte  8bit)
        DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                                                        //普通模式  传输完成一次就自动结束
        DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;                                        //优先级 中等
        DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;                                        //不使用 FIFO
        DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;                        //
        DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;                                //
        DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
        DMA_Init(DMA2_Stream2, &DMA_InitStructure);                                                                //初始化
        /* DMA TX Config */
        DMA_InitStructure.DMA_Channel = DMA_Channel_3;                                                        //DMA  通道
        DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_ADDR;                                //外设地址
        DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)SPI1_TX_BUFFER;                //接收缓冲区(内存中的有一个数组)
        DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;                                        //DMA传输方向
        DMA_InitStructure.DMA_BufferSize = TXSize;                                                                //DMA传输的数量        这个后期还可以再改
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                //外设地址自增  取消
        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                                        //内存地址自增  使能
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;                        //传输的 单位 (byte  8bit)
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;        //传输的 单位 (byte  8bit)
        DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                                                        //普通模式  传输完成一次就自动结束
        DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;                                        //优先级 中等
        DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
        DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
        DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
        DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
        DMA_Init(DMA2_Stream3, &DMA_InitStructure);                                                                //初始化
       
        //DMA中断方式发送
        DMA_ITConfig(DMA2_Stream3, DMA_IT_TC, ENABLE);                                                        //DMA发送中断开
        NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream3_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
        DMA_ClearITPendingBit(DMA2_Stream3, DMA_IT_TCIF3);
       
        //DMA中断方式接收       
        DMA_ITConfig(DMA2_Stream2, DMA_IT_TC, ENABLE);                                                        //DMA接收中断开
        NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream2_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
        DMA_ClearITPendingBit(DMA2_Stream2, DMA_IT_TCIF2);
}

//读写数据调用此函数
void spi1_trans(unsigned char *rx_buf, unsigned char *tx_buf, unsigned short length)       
{
        DMA_Cmd(DMA2_Stream2, DISABLE);                                                                                //关闭DMA
        DMA_Cmd(DMA2_Stream3, DISABLE);                                                                                //关闭DMA
       
        DMA_SetCurrDataCounter(DMA2_Stream2, (unsigned short)length);                //设置DMA的传输参数
        DMA_SetCurrDataCounter(DMA2_Stream3, (unsigned short)length);                //设置DMA的传输参数

        DMA2_Stream2->M0AR = (uint32_t)rx_buf;                                                                //设置内存的起始地址
        DMA2_Stream3->M0AR = (uint32_t)tx_buf;                                                                //设置内存的起始地址
       
        SPI1->DR;
        //while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);        //等待先前的发送完毕

        DMA_Cmd(DMA2_Stream2, ENABLE);                                                                                //打开DMA开始发送
        DMA_Cmd(DMA2_Stream3, ENABLE);                                                                                //打开DMA开始接收

        //while( DMA_GetFlagStatus(DMA2_Stream3, DMA_FLAG_TCIF3) == RESET);  //等待数据发送完
        //while( DMA_GetFlagStatus(DMA2_Stream2, DMA_FLAG_TCIF2) == RESET);  //等待
        //DMA_Cmd(DMA2_Stream2, DISABLE);                                                                        //发送完关闭DMA
        //DMA_Cmd(DMA2_Stream3, DISABLE);                                                                        //发送完关闭DMA
        //DMA_ClearFlag(DMA2_Stream2, DMA_FLAG_TCIF2);                                                //清楚标志位
        //DMA_ClearFlag(DMA2_Stream3, DMA_FLAG_TCIF3);                                                //清楚标志位
}

//发送中断
void DMA2_Stream2_IRQHandler(void)  
{
        // 清除DMA发送完成标志
        if(DMA_GetITStatus(DMA2_Stream2, DMA_IT_TCIF2)!= RESET)
    {
                // 清除DMA发送完成标志
                DMA_ClearITPendingBit(DMA2_Stream2, DMA_IT_TCIF2);
                DMA_Cmd(DMA2_Stream2, DISABLE);
                u1_printf("S2_TX_IRQ\r\n");
    }
        // 传输错误中断
        if(DMA_GetITStatus(DMA2_Stream2, DMA_IT_TEIF2) != RESET)
        {
                 DMA_ClearITPendingBit(DMA2_Stream2, DMA_IT_TEIF2);
                u1_printf("S2_err_TEIF2\r\n");
        }
}       

//接收中断
void DMA2_Stream3_IRQHandler(void)
{
        // 清除DMA发送完成标志
        if(DMA_GetITStatus(DMA2_Stream3, DMA_IT_TCIF3)!= RESET)
    {
                DMA_ClearITPendingBit(DMA2_Stream3, DMA_IT_TCIF3);
                DMA_Cmd(DMA2_Stream3, DISABLE);
                u1_printf("S3_RX_IRQ\r\n");
    }
       
        // 传输错误中断
        if(DMA_GetITStatus(DMA2_Stream3, DMA_IT_TEIF3) != RESET)
        {
                 DMA_ClearITPendingBit(DMA2_Stream3, DMA_IT_TEIF3);
                        u1_printf("S3_err_TEIF3\r\n");
        }
}


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:404160 发表于 2024-8-27 12:12 | 只看该作者
DMA中断使能:您在spi1_dma_init函数中使能了DMA中断,但在spi1_trans函数中似乎没有适当的方式去等待DMA中断发生。while循环被注释掉了,如果DMA中断没有正确触发,程序就不会继续执行。

DMA传输方向设置:在spi1_dma_init函数中,RX和TX的DMA配置都使用了相同的DMA通道3,这可能会导致问题。通常,接收和发送应该使用不同的DMA通道,以便它们可以同时进行。

中断服务函数:在DMA中断服务函数DMA2_Stream2_IRQHandler和DMA2_Stream3_IRQHandler中,您只处理了传输完成中断(TCIF),但是没有处理半传输完成中断(HTIF),这可能影响DMA的效率和性能。

SPI传输开始:在spi1_trans函数中,您直接启动了DMA,但是没有通过SPI的发送函数开始传输。这可能导致DMA尝试从SPI读取/写入数据,但是SPI并未开始传输。

代码注释和逻辑:部分代码被注释掉,比如等待DMA传输完成的循环。这些代码如果取消注释,可能会帮助程序正常工作

针对您的代码,以下是一些具体的检查点:

确保DMA2_Stream2_IRQHandler和DMA2_Stream3_IRQHandler中断服务函数中没有无限循环。
检查中断优先级设置是否合理,没有其他中断被错误地屏蔽。
确保ISR中没有调用阻塞函数或执行耗时操作。
确保DMA中断标志位在ISR中被正确清除。
检查是否有可能在ISR中访问了被其他任务使用的共享资源。
回复

使用道具 举报

板凳
ID:276685 发表于 2024-8-27 18:25 | 只看该作者
建议debug一下看程序死在哪,检查一下void XXXX_IRQHandler(void)这个函数在.h文件定义了没有,如果没有系统会在中断来临时进入空函数循环
回复

使用道具 举报

地板
ID:982617 发表于 2024-8-29 09:34 | 只看该作者
你的代码中可能存在以下几个问题,导致程序进入中断后卡死:  1. **DMA通道配置冲突:** 你在 `spi1_dma_init` 函数中,对 DMA2 的 Stream2 和 Stream3 都使用了 `DMA_Channel_3`。  STM32F407 的 DMA2 控制器中,Stream2 和 Stream3 共享一个通道(Channel 3)。 你需要根据 SPI1 的实际 DMA 请求映射选择正确的 Stream 和 Channel 组合。 请参考 STM32F407 数据手册中 "DMA requests mapping" 章节查找 SPI1_TX 和 SPI1_RX 对应的 DMA 请求。  2. **中断处理函数中的 `printf`:**  在中断处理函数 `DMA2_Stream2_IRQHandler` 和 `DMA2_Stream3_IRQHandler` 中使用了 `u1_printf` 函数。 在中断处理函数中使用 `printf` 等重量级函数可能会导致程序卡死,尤其是在中断频繁触发的情况下。 尽量简化中断处理函数的逻辑,例如仅设置标志位或执行一些简单的操作,将耗时的操作放到主循环中处理。  3. **DMA 缓冲区大小:** 你的 `SPI1_RX_BUFFER` 和 `SPI1_TX_BUFFER` 数组大小为 2000 字节,请确保你的 MCU 有足够的 RAM 空间来容纳这两个数组。  4. **DMA 传输模式:** 你使用了 `DMA_Mode_Normal` 模式,这意味着每次 DMA 传输完成后都需要重新配置 DMA 控制器。 如果你的应用需要连续传输数据,可以考虑使用 `DMA_Mode_Circular` 循环模式,避免频繁配置 DMA 控制器。  5. **SPI 配置:** 请确保 SPI1 的波特率、时钟极性、时钟相位等配置与你的 SPI 从设备一致。   **建议修改:**  1. **确认 DMA 通道映射:**  仔细阅读 STM32F407 数据手册,确定 SPI1_TX 和 SPI1_RX 对应的 DMA 请求和通道。 修改 `spi1_dma_init` 函数,使用正确的 Stream 和 Channel 初始化 DMA。  2. **简化中断处理:** 将 `u1_printf` 从中断处理函数中移除,可以使用全局变量或标志位来指示 DMA 传输完成或出现错误。  3. **检查内存分配:** 确保你的 MCU 有足够的 RAM 空间来存储 DMA 缓冲区。  4. **尝试循环模式:** 如果需要连续传输数据,修改 DMA 模式为 `DMA_Mode_Circular`。  5. **仔细检查 SPI 配置:**  确保 SPI1 的配置参数与你的 SPI 从设备匹配。   **调试建议:**  1. 使用调试器单步执行程序,观察程序卡死的位置。  2. 在中断处理函数中设置断点,查看中断是否被正确触发。  3. 使用逻辑分析仪抓取 SPI 总线上的信号,分析 SPI 通信是否正常。     通过以上步骤排查问题,你应该能够解决 SPI DMA 中断方式数据传输的问题。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表