一是想总结一下SPI总线的特点与注意点,二是总结一下SPI DMA的使用
SPI总线
SPI信号线说明
通常SPI通过4个引脚与外部器件相连:
- MISO:主设备输入/从设备输出引脚。该引脚在从模式下发送数据,在主模式下接收数据。
- MOSI:主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。
- SCK:串口时钟,作为主设备的输出,从设备的输入
- NSS:从设备选择。这是一个可选的引脚,用来选择主/从设备。它的功能是用来作为“片选引脚”,让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。
原理
MOSI脚相互连接,MISO脚相互连接。这样,数据在主和从之间串行地传输(MSB位在前)。
通信总是由主设备发起。主设备通过MOSI脚把数据发送给从设备,从设备通过MISO引脚回传数据。这意味全双工通信的数据输出和数据输入是用同一个时钟信号同步的;时钟信号由主设备通过SCK脚提供。
NSS说明与注意点
NSS分为内部引脚和外部引脚。
NSS外部引脚可以作为输入信号或者输出信号,输入信号一般用作硬件方式从机的片选,而输出信号一般用于主SPI去片选与之相连的从SPI。
NSS从设备选择有两种模式:
1、软件模式
可以通过设置SPI_CR1寄存器的SSM位来使能这种模式,当它为1时,NSS引脚上的电平由SSI决定。在这种模式下NSS外部引脚可以用作它用,而内部NSS信号电平可以通过写SPI_CR1的SSI位来驱动。
2、硬件模式
两种方式:
(1)对于主SPI,NSS可以直接接高电平,对于从SPI,可以直接接低电平。
(2)当STM32F10xxx工作为主SPI,并且NSS输出已经通过SPI_CR2寄存器的SSOE位使能,这时主机的NSS讲作为输出信号,引脚信号被拉低,所有NSS引脚与这个主SPI的NSS引脚相连并配置为硬件NSS的SPI设备,将自动变成从SPI设备。
此时两个的NSS信号线可以接个上拉电阻直连。
DMA说明
DMA是AMBA的先进高性能总线(AHB)上的设备,它有2个AHB端口:一个是从端口,用于配置DMA,另一个是主端口,使得DMA可以在不同的从设备之间传输数据。
DMA的作用是在没有Cortex-M3核心的干预下,在后台完成数据传输。在传输数据的过程中,主处理器可以执行其它任务,只有在整个数据块传输结束后,需要处理这些数据时才会中断主处理器的操作。它可以在对系统性能产生较小影响的情况下,实现大量数据的传输。
DMA原理
1.CPU配置好DMA。
2.SPI发出DMA请求。(在DMA_Mode_Normal模式下,该请求实际上需要CPU命令SPI发出请求)
3.若该通道有多个请求,DMA控制器通过仲裁器判断,根据配置的优先级,选择先回应该通道高优先级的请求,再回应低优先级的请求。(此过程不需要CPU参与)
4.DMA控制器回应请求后,自动根据配置,进行数据传输。(此过程不需要CPU参与)
DMA通道资源分配
DMA1通道
DMA2通道
SPI_DMA的通信过程
相关配置代码(方法一)
这里使用的是SPI1
SPI_DMA配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
|
void SPI1_DMA_Configuration( void ) { DMA_InitTypeDef DMA_InitStructure; DMA_DeInit(DMA1_Channel2); DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_Addr; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI1_RX_Buff; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = SPI1_ReciveBufferSize; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel2, &DMA_InitStructure); DMA_ITConfig(DMA1_Channel2, DMA_IT_TC, ENABLE); SPI1->CR2 |= 1<<0; DMA_Cmd(DMA1_Channel2, ENABLE); DMA_DeInit(DMA1_Channel3); DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_Addr; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI1_TX_Buff; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = SPI1_SendBufferSize; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel3, &DMA_InitStructure); DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE); DMA_ITConfig(DMA1_Channel3, DMA_IT_TE, ENABLE); SPI1->CR2 |= 1<<1; DMA_Cmd(DMA1_Channel3, DISABLE); }
|
SPI发送
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
void SPI1_Send( u8 *buff, u32 len ) { DMA1_Channel3->CPAR = SPI1_DR_Addr; DMA1_Channel3->CMAR = (u32) buff; DMA1_Channel3->CNDTR = len ; DMA1_Channel3->CCR = (0 << 14) | (2 << 12) | (0 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (1 << 7) | (0 << 6) | (0 << 5) | (1 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (1); }
|
SPI接收
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
void SPI1_Recive( u8 *buff, u32 len ) { DMA1_Channel2->CCR &= ~( 1 << 0 ); DMA1_Channel2->CPAR = SPI1_DR_Addr; DMA1_Channel2->CMAR = (uint32_t)buff; DMA1_Channel2->CNDTR = len ; DMA1_Channel2->CCR = (0 << 14) | (2 << 12) | (0 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (1 << 7) | (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (1 << 1) | (1); }
|
相关配置代码(方法二)
既然使用的是SPI+DMA,必定要使能SPI,详细请参考
SPI配置
配置完SPI后,配置DMA,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| void dma_init(unsigned int SendBuff,unsigned int buffer_size) { DMA_InitTypeDef DMA_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); DMA_DeInit(DMA1_Channel3);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SendBuff;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = buffer_size;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte ;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte ;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium ;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel3, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel3, ENABLE); }
|
注意:DMA_PeripheralBaseAddr不是SPI外设的地址,而是SPI1->DR即SPI数据寄存器的地址
因为DMA_Mode_Normal模式使用一次后DMA_BufferSize会清零,如下代码用于重置DMA_BufferSize
1 2 3 4 5 6 7 8 9 10
| void DMA_Buffercounter_reset(unsigned int buffer_size) { DMA_Cmd(DMA1_Channel3, DISABLE );
DMA_SetCurrDataCounter(DMA1_Channel3,buffer_size);
DMA_Cmd(DMA1_Channel3, ENABLE);
}
|
主函数编写如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #define buff_size 256 unsigned char sendbuff[buff_size]; int mian(coid) { dma_init((unsigned int)sendbuff,buff_size); while(1) { for(i=0 ;i<buff_size;i++) { sendbuff[i]=color; } for(i = 0 ; i<(32768/buff_size); i++) { SPI_I2S_DMACmd(SPI1,SPI_I2S_DMAReq_Tx,ENABLE); DMA_Buffercounter_reset(buff_size); while(1) { if(DMA_GetFlagStatus(DMA1_FLAG_TC3)!=RESET) { DMA_ClearFlag(DMA1_FLAG_TC3); break; } } } } }
|
相关链接(侵删)
- STM32 SPI DMA 的使用
=================我是分割线=================
欢迎到公众号来唠嗑: