解决STM32 SPI 半残废 NSS无法拉高
2019-07-01 来源:eefocus
众所周知,STM32 SPI是个半残废,NSS无法自动拉高,所以使用SPI 从机会一直使能,当主机是一对多的时候,就会出现SPI从机互相干扰的问题。
我利用GPIO中断,代替NSS引脚,使用过程如下代码所示
1.初始化SPI 的IO口,其中NSS引脚先不管。
void GPIO_SPI12_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//----- 第1步:打开SPI部件的时钟 --------------------------------------------------------------------------------------------
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
//----- 第2步:打开SPI相关的引脚为复用推挽输出 -----------------------------------------------------------------------------
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_3;
/* SPI SCK pin configuration */
GPIO_InitStructure.GPIO_Pin = SPI12_SCK_PIN;
GPIO_Init(SPI12_SCK_GPIO_PORT, &GPIO_InitStructure);
/* SPI MOSI pin configuration */
GPIO_InitStructure.GPIO_Pin = SPI12_MOSI_PIN;
GPIO_Init(SPI12_MOSI_GPIO_PORT, &GPIO_InitStructure);
/* SPI MISO pin configuration */
GPIO_InitStructure.GPIO_Pin = SPI12_MISO_PIN;
GPIO_Init(SPI12_MISO_GPIO_PORT, &GPIO_InitStructure);
// STM32 SPI 的CS引脚不会自动拉高,所以用GPIO中断代替
// GPIO_InitStructure.GPIO_Pin = SPI12_CS_PIN;
// GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
// GPIO_Init(SPI12_CS_GPIO_PORT, &GPIO_InitStructure);
GPIO_PinAFConfig(SPI12_SCK_GPIO_PORT, SPI12_SCK_SOURCE, SPI12_SCK_AF);
GPIO_PinAFConfig(SPI12_MOSI_GPIO_PORT, SPI12_MOSI_SOURCE, SPI12_MOSI_AF);
GPIO_PinAFConfig(SPI12_MISO_GPIO_PORT, SPI12_MISO_SOURCE, SPI12_MISO_AF);
// GPIO_PinAFConfig(SPI12_CS_GPIO_PORT, SPI12_CS_SOURCE, SPI12_CS_AF);
}
2.再初始化SPI,初始化方式是从机子模式,这时候先不要使能SPI。
void SPI12_Config(void)
{
SPI_InitTypeDef SPI_InitStructure;
SPI_RxFIFOThresholdConfig(SPI1,SPI_RxFIFOThreshold_QF);
//----- 第3步:配置SPI的参数设定 --------------------------------------------------------------------------------------------
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Slave; //设置为主模式SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //发送接收8 位数据
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //时钟悬空为低
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //数据捕获于第二个时钟沿
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS 信号由软件管理
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //设置波特率为去64 分频
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从MSB 位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //校验
SPI_Init(SPI1, &SPI_InitStructure);
SPI_I2S_DMACmd(SPI1,SPI_I2S_DMAReq_Tx,DISABLE);
SPI_I2S_DMACmd(SPI1,SPI_I2S_DMAReq_Rx,DISABLE);
SPI_ITConfig(SPI1, SPI_IT_RXNE,DISABLE); //接收中断失能
SPI_ITConfig(SPI1, SPI_IT_TXE, DISABLE); //发送中断失能
//----- 第4步:使能SPI1, 配置完毕 ------------------------------------------------------------------------------------------
SPI_Cmd(SPI1, DISABLE);
}
3. 将NSS引脚配置成GPIO中断,这里NSS 引脚为PA15,配置过程如下代码
void Exti_15_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); // 配置中断时钟
//----- 设置外部中断引脚,用户根据实际情况自行修改 ------------------------------------------------------------------------------
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; // 输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_2; // 快速
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; // 上拉
GPIO_Init(GPIOA, &GPIO_InitStructure);
//----- 设置NVIC初始化 ------------------------------------------------------------------------------
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_15_IRQn; // 中断通道
NVIC_InitStructure.NVIC_IRQChannelPriority = 0; // 中断优先级(0-3)
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能
NVIC_Init(&NVIC_InitStructure);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, GPIO_PinSource15); //连接中断线路
EXTI_InitStructure.EXTI_Line = EXTI_Line15; //设置中断线路
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; //双沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //中断线使能
EXTI_Init(&EXTI_InitStructure); //初始化中断
}
4. 写GPIO中断服务函数,该函数的主要工作判断NSS引脚的电平,若NSS为低电平则使能SPI,否则失能SPI。
void SPI1_NSS_Handler(void)
{
if(GPIO_ReadInputDataBit(SPI12_CS_GPIO_PORT,SPI12_CS_PIN)!=RESET)
{
SPI_Cmd(SPI1,DISABLE);
}
else
{
SPI_Cmd(SPI1,ENABLE);
}
}
void EXTI4_15_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line15) != RESET)
{
EXTI_ClearITPendingBit(EXTI_Line15);
SPI1_NSS_Handler();
}
}