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 }
关闭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); // 通道开启 }