OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4332|回复: 11

SPI+DMA提高SPI传输速度,发送数据存在漏发送问题

[复制链接]

14

主题

72

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
268
金钱
268
注册时间
2021-10-7
在线时间
44 小时
发表于 2021-11-22 10:50:58 | 显示全部楼层 |阅读模式
1金钱
本帖最后由 cyradg 于 2021-11-22 11:13 编辑

测试开发板为迷你开发板,SPI为SPI1口,SPI时钟设置为2M频率(好像是,不太记得了),不用DMA,读取256个字节大概需要1.2ms,加上DMA方式,读取256个字节大概为960多us,多少有些提升。用逻辑分析仪发现,不用DMA方式,每个字节的读取之间,时间间隔比较大,大约1us左右,如果用DMA方式,每个字节的读取之间,时间间隔比较小,大约200ns左右。
DMA刚学,不是太清楚,因为没有CPU介入,可能每个字节的读取之间的时间间隔会缩小,一定程度上提高了SPI传输速率。但是我目前出现了一个问题,就是通过DMA方式发送数据时,总是会漏掉最后两个字节不发送,例如,我要求发送4个字节,实际是发送了两个,要求发送6个,实际是发送4个,目前不知道原因,下面的代码很多是参考本网站别人写的代码:

//----------------------SPI_user.h-----------------
#ifndef __SPI__USER__
#define __SPI__USER__
#include <stm32f10x.h>
//SPI片选函数
typedef void (*SPI_CSFUN)(void);
//SPI时钟类型
typedef enum{
        SPI_SCK_FCPU2=0,
        SPI_SCK_FCPU4=1,
        SPI_SCK_FCPU8=2,
        SPI_SCK_FCPU16=3,
        SPI_SCK_FCPU32=4,
        SPI_SCK_FCPU64=5,
        SPI_SCK_FCPU128=6,
        SPI_SCK_FCPU256=7
}SPI_CLOCK;
typedef struct
{
        SPI_TypeDef *SPIx;                        //SPI口
        SPI_CLOCK SCK;                        //SPI时钟频率
        u16 TimeOut;                                //超时设置
        SPI_CSFUN CSLow;                        //CS片选,信号拉低函数
        SPI_CSFUN CSHigh;                        //CS片选,信号拉高函数
}SPIx_TypeDef;

void SPIx_DMA_Init(SPIx_TypeDef *SPI);
u8 SPIx_DMA_ReadWriteBytes(SPIx_TypeDef *SPI,u8 *RxData,u8 *TxData,u16 DataLen );
u8 SPIx_DMA_ReadBytes(SPIx_TypeDef *SPI,u8* RxData,u16 DataLen);
u8 SPIx_DMA_WriteBytes(SPIx_TypeDef *SPI,u8* TxData,u16 DataLen);

#endif

//-------------------SPI_user.c-----------------------
#include "SPI_user.h"

typedef struct
{
        DMA_Channel_TypeDef *DMARx;
        DMA_Channel_TypeDef *DMATx;
}SPIx_DMA_TypeDef;

/*****************************************************************
**SPIx_GetDMA:获取DMA通道
**参数:
**                SPI:SPI从设备
**                DMARx:接收DMA通道
**                DMATx:发送DMA通道
**返回值:0获取失败,1获取失败
******************************************************************/
u8 SPIx_GetDMA(SPIx_TypeDef *SPI,SPIx_DMA_TypeDef *DMA)
{
        DMA->DMARx=0;
        DMA->DMATx=0;
        if (SPI1==SPI->SPIx)
        {
                DMA->DMARx=DMA1_Channel2;        //SPI1的接收对应DMA1的通道2
                DMA->DMATx=DMA1_Channel3;        //SPI1的发送对应DMA1的通道3
        }
        else if (SPI2==SPI->SPIx)
        {
                DMA->DMARx=DMA1_Channel4;        //SPI2的接收对应DMA1的通道4
                DMA->DMATx=DMA1_Channel5;        //SPI2的发送对应DMA1的通道5
        }
        else if (SPI3==SPI->SPIx)
        {
                DMA->DMARx=DMA2_Channel1;        //SPI3的接收对应DMA2的通道1
                DMA->DMATx=DMA2_Channel2;        //SPI3的发送对应DMA2的通道2
        }
        else return 0;
        return 1;
}
/*****************************************************************
**__SPIx_Init:初始化SPI
**参数:
**                SPI:SPI从设备
******************************************************************/
void __SPIx_Init(SPIx_TypeDef *SPI)
{
        if ((0==SPI->CSLow) || (0==SPI->CSHigh)) return;
        if (0==SPI->SPIx) SPI->SPIx=SPI1;
        if ((0!=SPI->SCK) && (0==(SPI->SCK&0x7))) SPI->SCK=SPI_SCK_FCPU256;
        if (0==SPI->TimeOut) SPI->TimeOut=6000;
        if (SPI1==SPI->SPIx)
        {
                RCC->APB2ENR|=1<<2;       //PORTA时钟使能         
                RCC->APB2ENR|=1<<12;      //SPI1时钟使能
                //这里只针对SPI口初始化
                GPIOA->CRL&=0X000FFFFF;
                GPIOA->CRL|=0XBBB00000;//PA5.6.7复用            
                GPIOA->ODR|=0X7<<5;    //PA5.6.7上拉
        }
        else if (SPI2==SPI->SPIx)
        {
                RCC->APB2ENR|=1<<3;       //PORTB时钟使能         
                RCC->APB1ENR|=1<<14;      //SPI2时钟使能
                //这里只针对SPI口初始化
                GPIOB->CRH&=0X000FFFFF;
                GPIOB->CRH|=0XBBB00000;//PB13,14,15复用            
                GPIOB->ODR|=0X7<<13;    //PB13,14,15上拉
        }        
        else if (SPI3==SPI->SPIx)
        {
                RCC->APB2ENR|=1<<3;       //PORTB时钟使能         
                RCC->APB1ENR|=1<<15;      //SPI3时钟使能
                //这里只针对SPI口初始化
                GPIOB->CRL&=0xFF000FFF;
                GPIOB->CRL|=0X00BBB000;//PB3,4,5复用            
                GPIOB->ODR|=0X7<<3;    //PB3,4,5上拉
        }
        SPI->SPIx->CR1|=0<<10;//全双工模式        
        SPI->SPIx->CR1|=1<<9; //软件nss管理
        SPI->SPIx->CR1|=1<<8;  

        SPI->SPIx->CR1|=1<<2; //SPI主机
        SPI->SPIx->CR1|=0<<11;//8bit数据格式        
        SPI->SPIx->CR1|=1<<1; //空闲模式下SCK为1 CPOL=1
        SPI->SPIx->CR1|=1<<0; //数据采样从第二个时间边沿开始,CPHA=1  
        SPI->SPIx->CR1|=SPI->SCK<<3; //Fsck=Fcpu/256
        SPI->SPIx->CR1|=0<<7; //MSBfirst
}
/*****************************************************************
**SPIx_DMA_Config:SPI设备DMA配置
**参数:
**                DMARx:SPI设备的接收DMA通道
**                DMATx:SPI设备的发送DMA通道
******************************************************************/
void SPIx_DMA_Config(SPIx_TypeDef *SPI)
{
        SPIx_DMA_TypeDef DMA;
        if (!SPIx_GetDMA(SPI,&DMA)) return;
        DMA_Channel_TypeDef *DMARx=DMA.DMARx;
        DMA_Channel_TypeDef *DMATx=DMA.DMATx;
        if ((DMA1_Channel2==DMARx) || (DMA1_Channel4==DMARx))
                RCC->AHBENR |= 1<<0;                    //DMA1时钟使能
        else
                RCC->AHBENR |=1<<1;                                                //DMA2时钟使能
        /*------------------配置SPI1_RX_DMA通道---------------------*/

            DMARx->CCR &= ~( 1<<14 ) ;        //非存储器到存储器模式
        DMARx->CCR |=    1<<12   ;        //通道优先级高
        DMARx->CCR &= ~( 3<<10 ) ;        //存储器数据宽度8bit
        DMARx->CCR &= ~( 3<<8  ) ;        //外设数据宽度8bit
        DMARx->CCR |=    1<<7    ;        //存储器地址增量模式
        DMARx->CCR &= ~( 1<<6  ) ;        //不执行外设地址增量模式
        DMARx->CCR &= ~( 1<<5  ) ;        //不执行循环操作
        DMARx->CCR &= ~( 1<<4  ) ;        //从外设读

        DMARx->CNDTR &= 0x0000   ;        //传输数量寄存器清零
        DMARx->CNDTR = 0 ;       //传输数量设置为buffersize个

        DMARx->CPAR = (u32)&SPI->SPIx->DR ;      //设置外设地址,注意PSIZE
        DMARx->CMAR = 0 ; //设置DMA存储器地址,注意MSIZE
        /*------------------配置SPI1_TX_DMA通道Channel3---------------------*/

        DMATx->CCR &= ~( 1<<14 ) ;        //非存储器到存储器模式
        DMATx->CCR |=    1<<12   ;        //通道优先级中等
        DMATx->CCR &= ~( 3<<10 ) ;        //存储器数据宽度8bit
        DMATx->CCR &= ~( 3<<8 )  ;        //外设数据宽度8bit
        DMATx->CCR |=    1<<7    ;        //存储器地址增量模式
        DMATx->CCR &= ~( 1<<6 )  ;        //不执行外设地址增量模式
        DMATx->CCR &= ~( 1<<5 ) ;         //不执行循环操作
        DMATx->CCR |=    1<<4    ;        //从存储器读

        DMATx->CNDTR &= 0x0000   ;        //传输数量寄存器清零
        DMATx->CNDTR = 0 ;       //传输数量设置为buffersize个
        
        DMATx->CPAR = (u32)&SPI->SPIx->DR ;      //设置外设地址,注意PSIZE
        DMATx->CMAR = 0 ; //设置DMA存储器地址,注意MSIZE               
}
/*****************************************************************
**SPIx_DMA_Init:初始化SPI
**参数:
**                SPI:SPI从设备
******************************************************************/
void SPIx_DMA_Init(SPIx_TypeDef *SPI)
{
        __SPIx_Init(SPI);  
        SPI->SPIx->CR2 |= 1<<1  ;              //发送缓冲区DMA使能
        SPI->SPIx->CR2 |= 1<<0  ;              //接收缓冲区DMA使能        
        SPI->SPIx->CR1|=1<<6;                       //SPI设备使能
        SPIx_DMA_Config(SPI);
}
/*****************************************************************
**SPI1_DMA_ReadWriteBytes:发送接收多个字节
**参数:
**                SPI:SPI从设备
**                RxData:收到的缓存
**
**返回值:0接收失败,1:接收成功
******************************************************************/
u8 SPIx_DMA_ReadWriteBytes(SPIx_TypeDef *SPI,
        u8 *RxData,u8 *TxData,u16 DataLen )
{
        SPIx_DMA_TypeDef DMARTx;
        if (!SPIx_GetDMA(SPI,&DMARTx)) return 0;
        DMA_Channel_TypeDef *DMARx=DMARTx.DMARx;
        DMA_Channel_TypeDef *DMATx=DMARTx.DMATx;
        DMARx->CNDTR = 0x0000   ;           //传输数量寄存器清零
        DMARx->CNDTR = DataLen ;         //传输数量设置为buffersize个
        DMARx->CCR &= ~( 1<<5  ) ;        //不执行循环操作
        DMARx->CCR |=    1<<7    ;        //存储器地址增量模式
        DMARx->CMAR=(u32)RxData;

        DMATx->CNDTR = 0x0000   ;           //传输数量寄存器清零
        DMATx->CNDTR = DataLen ;         //传输数量设置为buffersize个
        DMATx->CCR &= ~( 1<<5 ) ;         //不执行循环操作
        DMATx->CCR |=    1<<7    ;        //存储器地址增量模式
        DMATx->CMAR=(u32)TxData;

        DMA_TypeDef *DMA;
        u32 rx_ifcr,tx_ifcr;
        if (DMARx==DMA1_Channel2)
        {
                rx_ifcr=0xF<<4;
                tx_ifcr=0xF<<8;
                DMA=DMA1;
        }
        else if (DMARx==DMA1_Channel4)
        {
                rx_ifcr=0xF<<12;
                tx_ifcr=0xF<<16;
                DMA=DMA1;
        }
        else
        {
                rx_ifcr=0xF;
                tx_ifcr=0xF<<4;
                DMA=DMA2;
        }
        DMA->IFCR = rx_ifcr ;                         //清除Rx通道的标志位
        DMA->IFCR = tx_ifcr ;                        //清除Tx通道的标志位

        SPI->SPIx->DR ;                                                                        //接送前读一次SPI1->DR,保证接收缓冲区为空

        u32 t=SPI->TimeOut;
        while( ( SPI->SPIx->SR & 0x02 ) == 0 )
        {
                if ((t--)==0) return 0;
        }
        
        DMATx->CCR |= 1 << 0 ;              //开启DMA通道3
        DMARx->CCR |= 1 << 0 ;              //开启DMA通道2        

        t=SPI->TimeOut*0xFFFF;
        u8 res=1;
        u32 isr=0;
        if (DMA1_Channel2==DMARx) isr=1<<5;//DMA1通道2
        else if (DMA1_Channel4==DMARx) isr=1<<13;//DMA1通道4
        else isr=1<<0;//DMA2通道1
        while( ( DMA->ISR & isr ) == 0 )
        {
                if (0==(t--))
                {
                        res=0;
                        break;
                }
        }
        DMATx->CCR &= ~( 1 << 0 ) ;         //关闭DMA通道3
        DMARx->CCR &= ~( 1 << 0 ) ;         //关闭DMA通道2
        return res;
}
/*****************************************************************
**SPIx_DMA_ReadBytes:发送一个字节
**参数:
**                SPI:SPI从设备
**                RxData:收到的字节
**返回值:0接收失败,1:接收成功
******************************************************************/
u8 SPIx_DMA_ReadBytes(SPIx_TypeDef *SPI,u8* RxData,u16 DataLen)
{
        u8 b=0xFF;
        SPIx_DMA_TypeDef DMARTx;
        if (!SPIx_GetDMA(SPI,&DMARTx)) return 0;
        DMA_Channel_TypeDef *DMARx=DMARTx.DMARx;
        DMA_Channel_TypeDef *DMATx=DMARTx.DMATx;
        DMARx->CNDTR = 0x0000   ;           //传输数量寄存器清零
        DMARx->CNDTR = DataLen ;         //传输数量设置为buffersize个
        DMARx->CCR &= ~( 1<<5  ) ;        //不执行循环操作
        DMARx->CMAR=(u32)RxData;

        DMATx->CNDTR = 0x0000   ;           //传输数量寄存器清零
        DMATx->CNDTR = 1 ;         //传输数量设置为buffersize个
        DMATx->CCR &=~(1<<7)    ;        //存储器地址不增量模式
        DMATx->CCR |= ( 1<<5 ) ;         //执行循环操作
        DMATx->CMAR=(u32)&b;

        DMA_TypeDef *DMA;
        u32 rx_ifcr,tx_ifcr;
        if (DMARx==DMA1_Channel2)
        {
                rx_ifcr=0xF<<4;
                tx_ifcr=0xF<<8;
                DMA=DMA1;
        }
        else if (DMARx==DMA1_Channel4)
        {
                rx_ifcr=0xF<<12;
                tx_ifcr=0xF<<16;
                DMA=DMA1;
        }
        else
        {
                rx_ifcr=0xF;
                tx_ifcr=0xF<<4;
                DMA=DMA2;
        }
        DMA->IFCR = rx_ifcr ;                         //清除Rx通道的标志位
        DMA->IFCR = tx_ifcr ;                        //清除Tx通道的标志位

        SPI->SPIx->DR ;                                                                        //接送前读一次SPI1->DR,保证接收缓冲区为空

        u32 t=SPI->TimeOut;
        while( ( SPI->SPIx->SR & 0x02 ) == 0 )
        {
                if ((t--)==0) return 0;
        }
        
        DMATx->CCR |= 1 << 0 ;              //开启DMA通道3
        DMARx->CCR |= 1 << 0 ;              //开启DMA通道2        

        t=SPI->TimeOut*0xFFFF;
        u8 res=1;
        u32 isr=0;
        if (DMA1_Channel2==DMARx) isr=1<<5;//DMA1通道2
        else if (DMA1_Channel4==DMARx) isr=1<<13;//DMA1通道4
        else isr=1<<0;//DMA2通道1
        while( ( DMA->ISR & isr ) == 0 )
        {
                if (0==(t--))
                {
                        res=0;
                        break;
                }
        }
        DMATx->CCR &= ~( 1 << 0 ) ;         //关闭DMA通道3
        DMARx->CCR &= ~( 1 << 0 ) ;         //关闭DMA通道2
        return res;
}
/*****************************************************************
**SPIx_DMA_WriteBytes:发送多个字节
**参数:
**                SPI:SPI从设备
**                TxData:收到的字节
**返回值:0接收失败,1:接收成功
******************************************************************/
u8 SPIx_DMA_WriteBytes(SPIx_TypeDef *SPI,u8* TxData,u16 DataLen)
{
        u32 b=0xFF;
        SPIx_DMA_TypeDef DMARTx;
        if (!SPIx_GetDMA(SPI,&DMARTx)) return 0;
        DMA_Channel_TypeDef *DMARx=DMARTx.DMARx;
        DMA_Channel_TypeDef *DMATx=DMARTx.DMATx;
        DMARx->CNDTR = 0x0000   ;           //传输数量寄存器清零
        DMARx->CNDTR = 1 ;         //传输数量设置为buffersize个
        DMARx->CCR &=~(1<<7)    ;        //存储器地址不增量模式
        DMARx->CCR |= ( 1<<5  ) ;        //执行循环操作
        DMARx->CMAR=(u32)&b;

        DMATx->CNDTR = 0x0000   ;           //传输数量寄存器清零
        DMATx->CNDTR = DataLen ;         //传输数量设置为buffersize个
        DMATx->CCR |=    1<<7    ;        //存储器地址增量模式
        DMATx->CCR &= ~( 1<<5 ) ;         //不执行循环操作
        DMATx->CMAR=(u32)TxData;

        DMA_TypeDef *DMA;
        u32 rx_ifcr,tx_ifcr;
        if (DMARx==DMA1_Channel2)
        {
                rx_ifcr=0xF<<4;
                tx_ifcr=0xF<<8;
                DMA=DMA1;
        }
        else if (DMARx==DMA1_Channel4)
        {
                rx_ifcr=0xF<<12;
                tx_ifcr=0xF<<16;
                DMA=DMA1;
        }
        else
        {
                rx_ifcr=0xF;
                tx_ifcr=0xF<<4;
                DMA=DMA2;
        }
        u32 isr=0;
        if (DMA1_Channel3==DMATx) isr=1<<9;//DMA1通道3
        else if (DMA1_Channel5==DMATx) isr=1<<17;//DMA1通道5
        else isr=1<<5;//DMA2通道2
        DMA->IFCR = rx_ifcr ;                         //清除Rx通道的标志位
        DMA->IFCR = tx_ifcr ;                        //清除Tx通道的标志位

        SPI->SPIx->DR ;                                                                        //接送前读一次SPI1->DR,保证接收缓冲区为空

        u32 t=SPI->TimeOut;
        while( ( SPI->SPIx->SR & 0x02 ) == 0 )
        {
                if ((t--)==0) return 0;
        }
        
        DMATx->CCR |= 1 << 0 ;              //开启DMA通道3
        DMARx->CCR |= 1 << 0 ;              //开启DMA通道2        

        t=SPI->TimeOut*0xFFFF;
        u8 res=1;
        while( ( DMA->ISR & isr ) == 0 )
        {
//                if (0==(t--))
//                {
//                        res=0;
//                        break;
//                }
        }
        DMATx->CCR &= ~( 1 << 0 ) ;         //关闭DMA通道3
        DMARx->CCR &= ~( 1 << 0 ) ;         //关闭DMA通道2
        return res;
}

//---------------main.c------------------------
#include "SPI_user.h"
u8 SPI1_TX_Buff[400];
void CS_LOW(void)
{
        GPIOA->ODR &=~(0X1<<2);                    //PA2 下拉
}
void CS_High(void)
{
        GPIOA->ODR |=0X1<<2;                    //PA2 上拉
}

int main(void)
{
        //初始化LED灯,按键
        RCC->APB2ENR |=1<<2;
        RCC->APB2ENR |=1<<4;
        GPIOA->CRH &=0xFFFFFFF0;
        GPIOA->CRH |=3;
        GPIOA->BRR=1<<8;
        GPIOC->CRL &=0xFF0FFFFF;
        GPIOC->CRL |=8<<20;
        GPIOC->BSRR=1<<5;
        //////////////////////////////////////
        //W25Q64的片选脚配置
        GPIOA->CRL&=0XFFFFF0FF;
        GPIOA->CRL|=3<<(2<<2);                //PA2.3.4 推挽            
        GPIOA->ODR|=0X1<<2;                    //PA2.3.4上拉
        /////////////////////////////////////
        u8 key=1;
        SPIx_TypeDef SPI;
        SPI.SPIx=SPI1;
        SPI.SCK=SPI_SCK_FCPU32;
        SPI.TimeOut=6800;
        SPI.CSLow=CS_LOW;
        SPI.CSHigh=CS_High;
        SPI1_TX_Buff[0]=W25X_ReadData; //W25Q64的读数据命令
        SPI1_TX_Buff[1]=0;               //W25Q64的读取内存地址0
        SPI1_TX_Buff[2]=0;             //W25Q64的读取内存地址0
        SPI1_TX_Buff[3]=0;             //W25Q64的读取内存地址0
        for (u16 i=0;i<256;i++)
        {
                SPI1_TX_Buff[4+i]=0xFF;//发送0xFF读数据
        }
        while (1)
        {
                if ((key==1) && (!(GPIOC->IDR & (1<<5))))
                {
                        key=0;
                        GPIOA->BSRR=1<<8;
                        SPIx_DMA_Init(&SPI);
                        SPI.CSLow();
                        SPIx_DMA_WriteBytes(&SPI,SPI1_TX_Buff,4); //<-------------这里实际只发送了2个数据,原因未知,从逻辑分析仪来看,时钟还在,CS提前拉高了,说明提前结束了。
                        SPI.CSHigh();
                }
        }
}

SPIx_DMA_ReadWriteBytes,SPIx_DMA_ReadBytes都测试了256个字节,都正常,但是测试SPIx_DMA_WriteBytes,发现总是会漏掉最后两个字节没发送,不知道什么原因。DMA目前不是太明白,架构代码是抄来自己改的。为什么会有SPIx_DMA_WriteBytes这个方式,是因为发送时,只关注发送了多少,并不关注接收了多少,使用SPIx_DMA_ReadWriteBytes函数,需要事先分配一个匹配的接收数据缓存,但是这个缓存数据我并不关心,除了占内存没多大意义,所以SPIx_DMA_WriteBytes的思路如下:
1、SPI的DMA接收通道设置为循环模式,地址不增量模式,内存地址就是指向一个32位的变量,外设地址就是SPI的DR寄存器。
2、SPI的DMA发送通道设置为不循环模式,地址增量模式,内存地址指向要发送数据的地址,外设地址就是SPI的DR寄存器。
类似这种方式SPIx_DMA_ReadBytes函数,测试256个字节都是正常的,但是SPIx_DMA_WriteBytes函数,测试发送4个字节,实际只发送了2个,不知何故。

最佳答案

查看完整内容[请看2#楼]

对,接收内存地址不增量就行。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

12

主题

3399

帖子

1

精华

论坛元老

Rank: 8Rank: 8

积分
8674
金钱
8674
注册时间
2020-5-11
在线时间
4146 小时
发表于 2021-11-22 10:50:59 | 显示全部楼层
cyradg 发表于 2021-11-22 14:07
对了,是否可以把接收设置一定长度接收,内存地址不增量?然后代码改成等待接收完成,回去试试,或许可行 ...

对,接收内存地址不增量就行。
专治疑难杂症
回复

使用道具 举报

1

主题

385

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1023
金钱
1023
注册时间
2019-9-21
在线时间
269 小时
发表于 2021-11-22 11:46:09 | 显示全部楼层
帮顶一下。
回复

使用道具 举报

12

主题

3399

帖子

1

精华

论坛元老

Rank: 8Rank: 8

积分
8674
金钱
8674
注册时间
2020-5-11
在线时间
4146 小时
发表于 2021-11-22 12:01:23 | 显示全部楼层
SPI.CSHigh();之前加延时看看,
也许DMA只管把数据丢给DR,就认为传输完成了,并未等待发送完成标志,DR还需要慢慢移位发出去才算完成。
专治疑难杂症
回复

使用道具 举报

14

主题

72

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
268
金钱
268
注册时间
2021-10-7
在线时间
44 小时
 楼主| 发表于 2021-11-22 13:39:41 | 显示全部楼层
LcwSwust 发表于 2021-11-22 12:01
SPI.CSHigh();之前加延时看看,
也许DMA只管把数据丢给DR,就认为传输完成了,并未等待发送完成标志,DR还 ...

有道理,DMA_Read能成功是因为代码在等待接收完成,而接收是在后面的,DMA_Write是发送在前,发送完了没有等接收,非常感谢!
看来还是不能等发送,只能等接收,但是接收是无限循环的,不知道这个怎么处理。
回复

使用道具 举报

14

主题

72

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
268
金钱
268
注册时间
2021-10-7
在线时间
44 小时
 楼主| 发表于 2021-11-22 13:45:31 | 显示全部楼层
本帖最后由 cyradg 于 2021-11-22 13:47 编辑
LcwSwust 发表于 2021-11-22 12:01
SPI.CSHigh();之前加延时看看,
也许DMA只管把数据丢给DR,就认为传输完成了,并未等待发送完成标志,DR还 ...

SPI使用DMA就是为了尽可能快,所以延迟函数不建议使用,因为不知道要延迟多久。使用DMA能加快速度,我个人认为就是省去了CPU倒腾发送接收数据所消耗的时间。用逻辑分析仪来看,发送和接收每个字节之间,不用DMA的话,有很大一个时间间隔,这个时间消耗估计和CPU参与倒腾数据花费的时间有关。
回复

使用道具 举报

12

主题

3399

帖子

1

精华

论坛元老

Rank: 8Rank: 8

积分
8674
金钱
8674
注册时间
2020-5-11
在线时间
4146 小时
发表于 2021-11-22 13:54:43 | 显示全部楼层
本帖最后由 LcwSwust 于 2021-11-22 13:58 编辑
cyradg 发表于 2021-11-22 13:39
有道理,DMA_Read能成功是因为代码在等待接收完成,而接收是在后面的,DMA_Write是发送在前,发送完了没 ...

我也没啥好办法,只想到加延时,没找到有合适的标志位。
另一办法是:如果发送的数组不怕被改写,可以设置SPI接收与发送DMA都用相同的数组、不循环。
对了,不必使用相同数组,把接收DMA的地址增量改一下就行了呀,就不用循环模式了。
专治疑难杂症
回复

使用道具 举报

12

主题

3399

帖子

1

精华

论坛元老

Rank: 8Rank: 8

积分
8674
金钱
8674
注册时间
2020-5-11
在线时间
4146 小时
发表于 2021-11-22 14:04:40 | 显示全部楼层
看了下代码,你接收是“不增量模式”,那就改下:
DMARx->CNDTR = DataLen ;         //传输数量设置为buffersize个
DMARx->CCR &= ~( 1<<5  ) ;        //不执行循环操作

然后等待接收标志就行了。
专治疑难杂症
回复

使用道具 举报

14

主题

72

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
268
金钱
268
注册时间
2021-10-7
在线时间
44 小时
 楼主| 发表于 2021-11-22 14:05:21 | 显示全部楼层
LcwSwust 发表于 2021-11-22 13:54
我也没啥好办法,只想到加延时,没找到有合适的标志位。
另一办法是:如果发送的数组不怕被改写,可以设 ...

DMA_ReadWrite是可以的,代码是等待接收完成。这个函数的缺点是必须同时提供RX和TX数据缓存,对于仅仅传送数据而言,RX缓存是多余的,就是浪费内存,也正是因为这个原因,我改成了DMA_Write,只需要提供一个字节长度的接收内存,无限循环在这个字节里接收数据。
回复

使用道具 举报

12

主题

3399

帖子

1

精华

论坛元老

Rank: 8Rank: 8

积分
8674
金钱
8674
注册时间
2020-5-11
在线时间
4146 小时
发表于 2021-11-22 14:06:45 | 显示全部楼层
cyradg 发表于 2021-11-22 14:05
DMA_ReadWrite是可以的,代码是等待接收完成。这个函数的缺点是必须同时提供RX和TX数据缓存,对于仅仅传 ...

嗯,了解,看我7楼又说了下,不用循环,改下接收数量就行。
专治疑难杂症
回复

使用道具 举报

14

主题

72

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
268
金钱
268
注册时间
2021-10-7
在线时间
44 小时
 楼主| 发表于 2021-11-22 14:07:37 | 显示全部楼层
LcwSwust 发表于 2021-11-22 13:54
我也没啥好办法,只想到加延时,没找到有合适的标志位。
另一办法是:如果发送的数组不怕被改写,可以设 ...

对了,是否可以把接收设置一定长度接收,内存地址不增量?然后代码改成等待接收完成,回去试试,或许可行。
回复

使用道具 举报

2

主题

592

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1458
金钱
1458
注册时间
2019-7-28
在线时间
137 小时
发表于 2021-11-23 17:12:52 | 显示全部楼层
学到了   
回复

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-12 22:42

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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