月y 2022-08-10 18:59 采纳率: 100%
浏览 247
已结题

两个STM32用SPI通信遇到的问题

在使用两个STM32使用SPI协议通信时 主机设置时钟分频因子为2 从机设置分频因子也为2。从机可以正确收到主机发来的数据,但是主机收到从机的数据不是00就是FF。 而我把主机设置时钟分频因子为256 从机设置分频因子也为256。这样双方都可正确收发数据,我想问这是为什么呢。

主机SPI2设置

void SPI2_Init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    SPI_InitTypeDef  SPI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    
    /* SPI的IO口和SPI外设打开时钟 */
    RCC_APB2PeriphClockCmd(    RCC_APB2Periph_GPIOB, ENABLE );
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;        //PB12推挽输出
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    /* SPI的IO口设置 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //PB13/14/15复用推挽输出 
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    GPIO_SetBits(GPIOB,GPIO_Pin_12);
    
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;        //设置SPI工作模式:设置为主SPI
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;        //设置SPI的数据大小:SPI发送接收8位帧结构
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;        //串行同步时钟的空闲状态为高电平
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;    //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;        //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;        //定义波特率预分频的值: 
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;    //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
    SPI_InitStructure.SPI_CRCPolynomial = 7;    //CRC值计算的多项式
    SPI_Init(SPI2, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
    
    SPI_Cmd(SPI2, ENABLE); //使能SPI外设
    
    
    SPI_I2S_ITConfig(SPI2,SPI_I2S_IT_RXNE,ENABLE);//开启相关中断
    //Usart1 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = SPI2_IRQn;//SPI2中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级0
    NVIC_InitStructure.NVIC_IRQChannelSubPriority =2;        //子优先级2
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);    //根据指定的参数初始化VIC寄存器、    
    
}
u16 t=0xDD; //中断接收数据
void SPI2_IRQHandler(void) 
{ 
            if(SPI_I2S_GetITStatus(SPI2,SPI_I2S_IT_RXNE) != RESET)
    {
        while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
        t = SPI_I2S_ReceiveData(SPI2);
        GPIO_SetBits(GPIOB,GPIO_Pin_12);//关闭片选
        USART_senddate(USART1,t) ;
    }
}

主机使用串口1发数据之后再用SPI发给从机

void USART1_IRQHandler(void)                    //串口1中断服务程序
{
    u16 r;
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断
    {
        GPIO_ResetBits(GPIOB,GPIO_Pin_12);
        r =USART_ReceiveData(USART1);//(USART1->DR);    //读取接收到的数据
        while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);//等待发送区空  
        SPI_I2S_SendData(SPI2,r); //通过外设SPIx发送一个byte  数据
    } 
}     

从机SPI设置,用中断接收主机的数据再把接收的数据发回主机。

void SPI2_Init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    SPI_InitTypeDef  SPI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    
    /* SPI的IO口和SPI外设打开时钟 */
    RCC_APB2PeriphClockCmd(    RCC_APB2Periph_GPIOB, ENABLE );
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;        //PB12推挽输出
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //下拉输入
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    /* SPI的IO口设置 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //PB13/14/15复用推挽输出 
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
    SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;        //设置SPI工作模式:设置为主SPI
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;        //设置SPI的数据大小:SPI发送接收8位帧结构
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;        //串行同步时钟的空闲状态为高电平
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;    //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
    SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;        //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;        //定义波特率预分频的值:波特率预分频值为256
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;    //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
    SPI_InitStructure.SPI_CRCPolynomial = 7;    //CRC值计算的多项式
    SPI_Init(SPI2, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
    
    SPI_Cmd(SPI2, ENABLE); //使能SPI外设
    
    SPI_I2S_ITConfig(SPI2,SPI_I2S_IT_RXNE,ENABLE);//开启相关中断
    //Usart1 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = SPI2_IRQn;//SPI2中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级0
    NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;        //子优先级2
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);    //根据指定的参数初始化VIC寄存器、    
    
}
u16 i;
void SPI2_IRQHandler(void) 
{ 
    //接收数据 
    if(SPI_I2S_GetITStatus(SPI2,SPI_I2S_IT_RXNE) != RESET)
    {
        while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET); //等待接收完一个byte
        i = SPI_I2S_ReceiveData(SPI2);
        while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
        SPI_I2S_SendData(SPI2,i);
        USART_senddate(USART1, i);    
    }

左边是主机,显示收到的数据,结果收到的全是FF, 右边是从机,显示的是从主机发过去的g

img

  • 写回答

4条回答 默认 最新

  • 柒壹漆 2022-08-11 14:52
    关注

    跟硬件的限制有点关系,理论上能达到18MHz,实际上当你设置成2分频的时候属于超频了,不太稳定,有可能会出现通讯异常或者丢数据的情况。这就好比stm32的usb接口,USB2.0全速理论上能达到12M,但实际上受限于硬件,连一半的速度都达不到。
    可以用逻辑分析仪或者示波器观察一下通讯时的波形,可能存在变形失真的情况。
    还有跟代码的处理也有关系,比如软件模拟SPI和硬件SPI会有差别,用HAL库和直接操作寄存器也有一个运行效率的差别,HAL库里面有些判断和处理也会对传输造成影响,虽然代码的执行速度很快,但是如果传输的数据量较大的时候,这个累计消耗的时间就长了。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(3条)

报告相同问题?

问题事件

  • 系统已结题 9月2日
  • 已采纳回答 8月25日
  • 创建了问题 8月10日

悬赏问题

  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题
  • ¥15 请完成下列相关问题!
  • ¥15 drone 推送镜像时候 purge: true 推送完毕后没有删除对应的镜像,手动拷贝到服务器执行结果正确在样才能让指令自动执行成功删除对应镜像,如何解决?