SPI主机负责产生时钟,决定了SPI通信速率。可以同时收发数据;
时钟极性(CPOL)决定同步时钟空闲状态;
时钟相位(CPHA):0:CLK的第一个跳变沿(上升沿或下降沿)数据被采样;
1:CLK的第二个跳变沿数据被采样;
写:主机对SPIX->DR写数据,CLK产生时钟,同时数据从MOSI -> MISO
读:主机对SPIX->DR写DUMMY(空字节),CLK产生时钟,DUMMY信号从MOSI -> MISO (该数据对从机无意义),同时要读取的数据从 MISO -> MOSI,主机从SPIX->DR读取数据。然后清除DR,以便接收下一个数据。
读写SPIX -> DR是两个不同的寄存器。
SPI中断事件:
中断事件 | 事件标志 | 使能控制位 |
发送缓冲器空标志 | TXE | TXEIE |
接收缓冲器非空标志 | RXNE | RXNEIE |
主模式错误事件 | MODF | ERRIE |
溢出错误 | OVR | |
CRC错误标志 | CRCERR |
案例1:简单SPI对接通信,SPI1主机,SPI3从机,中断方式,主机->从机
初始化代码
//步骤1 使能时钟
void RCC_Configuration(void)
{
RCC_APB2PeriphClock_Enable(RCC_APB2PERIPH_GPIOA| RCC_APB2PERIPH_GPIOB | RCC_APB2PERIPH_GPIOC| RCC_APB2PERIPH_AF , ENABLE);
RCC_APB2PeriphClock_Enable(RCC_APB2PERIPH_SPI1,ENABLE);
RCC_APB1PeriphClock_Enable( RCC_APB1PERIPH_SPI3 ,ENABLE );
}
//步骤2 配置GPIO
void GPIO_Configuration(void)
{
GPIO_InitPara GPIO_InitStructure;
GPIO_PinRemapConfig(GPIO_REMAP_SPI3, ENABLE);
/* Configure SPI_MASTER pins: SCK and MOSI */
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_5| GPIO_PIN_7;
GPIO_InitStructure.GPIO_Speed = GPIO_SPEED_50MHZ;
GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure SPI_SLAVE pins: SCK and MISO */
/* Configure SCK pin as Alternate Function */
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_10 ;
GPIO_InitStructure.GPIO_Mode = GPIO_MODE_IN_FLOATING;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* Configure MISO pin as Alternate Function Push Pull */
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_11;
GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
//步骤3 配置SPI,SPI1主机 SPI3从机
void SPI_Configuration(void)
{
SPI_InitPara SPI_InitStructure;
SPI_InitStructure.SPI_TransType = SPI_TRANSTYPE_BDMTX;
SPI_InitStructure.SPI_Mode = SPI_MODE_MASTER;
SPI_InitStructure.SPI_FrameFormat = SPI_FRAMEFORMAT_8BIT;
SPI_InitStructure.SPI_SCKPL = SPI_SCKPL_LOW;
SPI_InitStructure.SPI_SCKPH = SPI_SCKPH_2EDGE;
SPI_InitStructure.SPI_SWNSSEN = SPI_SWNSS_SOFT;
SPI_InitStructure.SPI_PSC = SPI_PSC_16;
SPI_InitStructure.SPI_FirstBit = SPI_FIRSTBIT_MSB;
SPI_InitStructure.SPI_CRCPOL = 7;
SPI_Init(SPI1, &SPI_InitStructure);
/* SPI_SLAVE configuration */
SPI_InitStructure.SPI_TransType = SPI_TRANSTYPE_BDMRX;
SPI_InitStructure.SPI_Mode = SPI_MODE_SLAVE;
SPI_Init(SPI3, &SPI_InitStructure);
/* Enable SPI_MASTER TBE interrupt */
SPI_I2S_INTConfig(SPI1, SPI_I2S_INT_TBE, ENABLE);
/* Enable SPI_SLAVE RBNE interrupt */
SPI_I2S_INTConfig(SPI3, SPI_I2S_INT_RBNE, ENABLE);
/* Enable SPI1 AND SPI3 */
SPI_Enable(SPI3, ENABLE);
SPI_Enable(SPI1, ENABLE);
}
//步骤4 配置中断
void NVIC_Configuration(void)
{
NVIC_InitPara NVIC_InitStructure;
/* 1 bit for pre-emption priority, 3 bits for subpriority */
NVIC_PRIGroup_Enable(NVIC_PRIGROUP_1);
/* Configure and enable SPI_MASTER interrupt */
NVIC_InitStructure.NVIC_IRQ = SPI1_IRQn;
NVIC_InitStructure.NVIC_IRQPreemptPriority = 1;
NVIC_InitStructure.NVIC_IRQSubPriority = 2;
NVIC_InitStructure.NVIC_IRQEnable = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Configure and enable SPI_SLAVE interrupt */
NVIC_InitStructure.NVIC_IRQ = SPI3_IRQn;
NVIC_InitStructure.NVIC_IRQPreemptPriority = 0;
NVIC_InitStructure.NVIC_IRQSubPriority = 1;
NVIC_InitStructure.NVIC_IRQEnable = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
中断函数:
void SPI1_IRQHandler(void)
{
if (SPI_I2S_GetIntBitState(SPI1, SPI_I2S_INT_TBE) != RESET)
{
/* Send SPI_MASTER data */
while (SPI_I2S_GetBitState(SPI1, SPI_FLAG_TBE) == RESET);
SPI_I2S_SendData(SPI1, SPI_MASTER_Buffer_Tx[ TxIdx++ ]);
if (TxIdx == BufferSize)
{
/* Disable SPI_MASTER TBE interrupt */
SPI_I2S_INTConfig(SPI1, SPI_I2S_INT_TBE, DISABLE);
}
}
}
void SPI3_IRQHandler(void)
{
/* Store SPI_SLAVE received data */
if (SPI_I2S_GetIntBitState(SPI3, SPI_I2S_INT_RBNE) == SET)
SPI_SLAVE_Buffer_Rx[RxIdx++] = SPI_I2S_ReceiveData(SPI3);
}
案例2:DMA_SPI收发
发送时,在每次TXE被设置为’1’时发出DMA请求,DMA控制器则写数据至SPI_DR寄存器,TXE标志因此而被清除。
接收时,在每次RXNE被设置为’1’时发出DMA请求,DMA控制器则从SPI_DR寄存器读出数据,RXNE标志因此而被清除。
DMA1_CHANNEL2:SPI1_RX
DMA1_CHANNEL3:SPI1_TX
配置DMA代码:
void SPI1_DMA_Configuration( void )
{
DMA_InitTypeDef DMA_InitStructure;
/* DMA1 Channel2 (triggered by SPI1 Rx event) Config */
DMA_DeInit(DMA1_Channel2);
DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_Addr; //设置 SPI1 发送外设(0x4001300C) 地址(目的地址)
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI1_RX_Buff; //设置 SRAM 存储地址(目的地址)
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //传输方向 外设-内存
DMA_InitStructure.DMA_BufferSize = SPI1_ReciveBufferSize; //设置 SPI1 发送长度
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);
/* Enable SPI1 DMA RX request */
SPI1->CR2 |= 1<<0; //接收缓冲区DMA使能
DMA_Cmd(DMA1_Channel2, ENABLE);
/* DMA1 Channel3 (triggered by SPI1 Tx event) Config */
DMA_DeInit(DMA1_Channel3);
DMA_InitStructure.DMA_PeripheralBaseAddr = SPI1_DR_Addr; //设置 接收外设(0x4001300C) 地址(源地址)
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI1_TX_Buff; //设置 SRAM 存储地址(源地址)
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //传输方向 内存-外设
DMA_InitStructure.DMA_BufferSize = SPI1_SendBufferSize; //设置 SPI1 接收长度
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); //开启 DMA1_Channel3 传输完成中断
DMA_ITConfig(DMA1_Channel3, DMA_IT_TE, ENABLE); //开启 DMA1_Channel3 传输错误中断
/* Enable SPI1 DMA TX request */
SPI1->CR2 |= 1<<1; //发送缓冲区DMA使能
DMA_Cmd(DMA1_Channel3, DISABLE); //开启 DMA 通道 DMA1_Channel3
}
SPI发送
关闭DMA通道3之前必须等待TXE为1,等待忙标志为0
void SPI1_Send( u8 *buff, u32 len )
{
DMA1_Channel3->CPAR = SPI1_DR_Addr; //外设地址
DMA1_Channel3->CMAR = (u32) buff; //mem地址
DMA1_Channel3->CNDTR = len ; //传输长度
DMA1_Channel3->CCR = (0 << 14) | // 非存储器到存储器模式
(2 << 12) | // 通道优先级高
(0 << 11) | // 存储器数据宽度8bit
(0 << 10) | // 存储器数据宽度8bit
(0 << 9) | // 外设数据宽度8bit
(0 << 8) | // 外设数据宽度8bit
(1 << 7) | // 存储器地址增量模式
(0 << 6) | // 外设地址增量模式(不增)
(0 << 5) | // 非循环模式
(1 << 4) | // 从存储器读
(1 << 3) | // 允许传输错误中断
(0 << 2) | // 允许半传输中断
(1 << 1) | // 允许传输完成中断
(1); // 通道开启
}
必须要先关闭通道2,然后再配置通道2的参数
void SPI1_Recive( u8 *buff, u32 len )
{
DMA1_Channel2->CCR &= ~( 1 << 0 ); //关闭DMA通道2
DMA1_Channel2->CPAR = SPI1_DR_Addr; //外设地址
DMA1_Channel2->CMAR = (uint32_t)buff; //mem地址
DMA1_Channel2->CNDTR = len ; //传输长度
DMA1_Channel2->CCR = (0 << 14) | // 非存储器到存储器模式
(2 << 12) | // 通道优先级高
(0 << 11) | // 存储器数据宽度8bit
(0 << 10) | // 存储器数据宽度8bit
(0 << 9) | // 外设数据宽度8bit
(0 << 8) | // 外设数据宽度8bit
(1 << 7) | // 存储器地址增量模式
(0 << 6) | // 外设地址增量模式(不增)
(0 << 5) | // 非循环模式
(0 << 4) | // 传输方向 外设-内存
(0 << 3) | // 允许传输错误中断
(0 << 2) | // 允许半传输中断
(1 << 1) | // 允许传输完成中断
(1); // 通道开启
}