STM32的SPI口的DMA读写[原创www.cnblogs.com/helesheng]

【STM32的SPI口的DMA读写[原创www.cnblogs.com/helesheng]】SPI是我最常用的接口之一 , 连接管脚仅为4根;在常见的芯片间通信方式中 , 速度远优于UART、I2C等其他接口 。STM32的SPI口的同步时钟最快可到PCLK的二分之一,单个字节或字的通信时间都在us以下,因此大多数情况下我们会使用查询法控制SPI口的传输 。但对于大量且连续的通信,再使用查询法就显得有些浪费CPU的时间,DMA控制SPI的读写显然成为一种不错的选择 。
为DMA控制SPI批量数据读写的功能,参照官方代码编写的DMA控制SPI口在主/从两种模式下,读写数据的的代码,供各位网友直接使用或批评指正 。先直接上我得到结论:
1、运用STM32的SPI口的DMA的功能 , 能够提升STM32与外设之间通信的速率和实时性 。
2、但在STM32的SPI的主机模式下 , DMA控制器无法自动产生片选CS信号,只能与无需同步CS信号的外设器件通信 。为产生同步的CS信号 , 只能由软件控制SPI逐字发送,而DMA仅用于接收SPI数据 , 这样做的效率和不使用DMA时一样 。
3、主模式下,软件控制片选CS信号和SPI读写时,存在至少50%的时间空隙,降低了其SPI通信的效率 。
4、STM32的SPI主机模式下,无法只使用DMA接收 , 而不发送 。原因是没有触发SPI的DMA接收的信号 。但SPI的发送可以是软件控制的逐字发送 , 也可以是DMA控制的连续发送 。
5、STM32的SPI若要使用DMA方式,最合适的是让STM32工作在SPI的从模式,由外部主机(如FPGA)来控制通信的实时性和高速性 。
以下原创内容欢迎网友转载,但请注明出处: https://www.cnblogs.com/helesheng
一、STM32做SPI主机(Master)时的DMA传输
STM32做SPI主机进行DMA通信时,尤其需要注意的是:不能单独使用SPI接收数据DMA,一定要配合SPI发送数据,DMA接收数据通道才能收到数据 。道理很简单:STM32做主机时,如果不主动发送数据将无法产生时钟和片选等信号,亦无法在传输完成后触发DMA接收数据 。但在使用时,这一点非常容易被忽视 , 从而造成DMA接收SPI数据通道DMA1CH2和DMACH4“不工作” 。

STM32的SPI口的DMA读写[原创www.cnblogs.com/helesheng]

文章插图
图1、STM32 DMA1各通道功能
具体来说,使用SPI口的DMA接收功能有两种配置方法:
1、SPI口的接收和发送各使用一个DMA通道
这样做最符合DMA控制大量数据连续发送和接收的设计初衷,此种情况下的SPI口和两个DMA通道的配置分别如下:
STM32的SPI口的DMA读写[原创www.cnblogs.com/helesheng]

文章插图
STM32的SPI口的DMA读写[原创www.cnblogs.com/helesheng]

文章插图
1 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE ); 2 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;//PA5 6 7是SPI1的SCK MIOS MOSI 3 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出 4 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 5 GPIO_Init(GPIOA, &GPIO_InitStructure); 6 GPIO_SetBits(GPIOA , GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);//将其置位 7 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工 8 SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//设置SPI工作模式:设置为主SPI 9 SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;//设置SPI的数据大小:SPI发送接收8位帧结构10 SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;//空闲时时钟为低电平11 SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;//数据捕获于第1个时钟沿12 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//SPI_NSS_Hard;////NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制13 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;//定义波特率预分频的值14 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始15 SPI_InitStructure.SPI_CRCPolynomial = 7;//CRC值计算的多项式16 SPI_Init(SPI1, &SPI_InitStructure);//根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器17 SPI_Cmd(SPI1, ENABLE); //使能SPI1外设SPI的配置
STM32的SPI口的DMA读写[原创www.cnblogs.com/helesheng]

文章插图
STM32的SPI口的DMA读写[原创www.cnblogs.com/helesheng]

文章插图
1 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//使能DMA传输 2 ///////以下配置DMA CH2用于接收SPI的DMA通道///// 3 DMA_DeInit(DMA1_Channel2);//将DMA的通道1寄存器重设为缺省值 4 DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(SPI1->DR);//DMA外设基地址 5 DMA_InitStructure.DMA_MemoryBaseAddr = (u32)spi_rx_buff;//DMA内存基地址 6 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//数据传输方向,从外设读取数据到内存 7 DMA_InitStructure.DMA_BufferSize = num;//DMA通道的DMA缓存的大小 8 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址寄存器不变 9 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存地址寄存器递增10 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//数据宽度为16位11 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位12 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//工作在正常模式13 DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; //DMA通道 x拥有中优先级14 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//DMA通道x没有设置为内存到内存传输15 DMA_Init(DMA1_Channel2, &DMA_InitStructure);//根据DMA_InitStruct中指定的参数初始化DMA的通道16 ///////以下配置DMA的SPI发送通道///////////17 DMA_DeInit(DMA1_Channel3);18 DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(SPI1->DR); //设置接收外设(0x4001300C) 地址(源地址)19 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)spi_tx_buff;//设置 SRAM 存储地址(源地址)20 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//传输方向 内存-外设21 DMA_InitStructure.DMA_BufferSize = num;//设置 SPI1 接收长度22 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址增量(不变)23 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存地址增量(变化)24 DMA_InitStructure.DMA_PeripheralDataSize = DMA_MemoryDataSize_HalfWord; //外设传输宽度(字节)25 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //内存传输宽度(字节)26 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //传输方式,一次传输完停止,不重新加载27 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA优先级28 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//内存到内存方式禁止29 DMA_Init(DMA1_Channel3, &DMA_InitStructure);

推荐阅读