发新帖本帖赏金 12.20元(功能说明)我要提问
123下一页
返回列表
打印
[STM32F1]

浅谈SPI双机通讯调试

[复制链接]
9606|58
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
yunyoushuaiqi|  楼主 | 2016-7-21 19:34 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
由于项目的需要,需要使用两个STM32的单片机进行双机通讯。以前使用SPI都是使用spi外挂类似FLASH或者显示屏之类的片子。不需要从机进行反数据。本来以为会很简单。没想到在实际调试中却发现了一些问题,耽搁了两天才调试完成。现在谈谈我的一些经过。也为有类似问题的同学提供一些参考。

打赏榜单

21ic小喇叭 打赏了 10.00 元 2016-08-01
理由:奖励一下 希望楼主以后还能继续分享~

皈依 打赏了 1.00 元 2016-07-22
理由:希望楼主永远保有这种分享的精神~

来自 2楼
yunyoushuaiqi|  楼主 | 2016-7-22 09:12 | 只看该作者
代码是在原来别的程序上直接改的。没有使用的函数请自动忽略。

STM32双机通讯.zip

4.36 MB

使用特权

评论回复
板凳
yunyoushuaiqi|  楼主 | 2016-7-21 19:38 | 只看该作者
我使用的是stm32f103单片机的SPI1。PA5,PA6,PA7三个引脚的硬件spi。下面我把程序拆分粘贴上来。谈谈自己调试中遇到的问题,跟解决方案。

使用特权

评论回复
地板
yunyoushuaiqi|  楼主 | 2016-7-21 19:40 | 只看该作者
首先要说明的一个就是硬件的接线。SPI接线不能跟串口一样交叉。这点一定要注意。
MISO---------MISO
MOSI---------MOSI
SCK------------SCK
主机片选IO可自己选----------从机NSS引脚PA4

使用特权

评论回复

打赏榜单

逍遥李 打赏了 1.00 元 2016-07-24

icecut 打赏了 0.20 元 2016-07-22
理由:这个值2毛

5
yunyoushuaiqi|  楼主 | 2016-7-21 19:44 | 只看该作者
本帖最后由 yunyoushuaiqi 于 2016-7-22 11:32 编辑

首先在IO引脚这里的配置一定要注意。有些人直接都配置成AF_PP,这样在主机通讯的时候是没有问题的,但是在从机也这样配置就会出现错误。因为从机是不能发送时钟信号的需要接受主机产生的时钟,所以需要配置成浮空输入。具体程序如下
主机*******************************************************************************
//**********************配置GPIO管脚*********************************
void GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
          GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //推挽复用
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7;//sck mosi
    GPIO_Init(GPIOA,&GPIO_InitStructure);
   
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;//miso
    GPIO_Init(GPIOA,&GPIO_InitStructure);
   
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;   
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;//cs
    GPIO_Init(GPIOA,&GPIO_InitStructure);
        
          GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//cs
    GPIO_Init(GPIOB,&GPIO_InitStructure);
}        






从机**********************************************************************************
//**********************配置GPIO管脚*********************************
void GPIO_Configuration(void)
{
          GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7;//sck mosi
    GPIO_Init(GPIOA,&GPIO_InitStructure);
   
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;   
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;//miso
    GPIO_Init(GPIOA,&GPIO_InitStructure);
   
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;//cs
    GPIO_Init(GPIOA,&GPIO_InitStructure);
}        

使用特权

评论回复
6
yunyoushuaiqi|  楼主 | 2016-7-21 19:49 | 只看该作者
配置完IO接下来就要配置spi的初始化函数了。这里我在网上看到有人说CPOL,CPHA的配置不能完全一样。说会产生数据位的错误。但是官方的数据手册说是应该配置成一样的。通过我自己试验发现配置成一样的并没有发生数据位错误的现象。也许是单片机的原因吧。反正我是配置成一样的 。
主机***************************************************************************************
//**********************SPI初始化函数*********************************
void SPI_Configuration(void)
{
  SPI_InitTypeDef  SPI_InitStructure;
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;      
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//SPI_FirstBit_MSB;
  SPI_InitStructure.SPI_CRCPolynomial = 7;
  SPI_Init(SPI1, &SPI_InitStructure);
  SPI_Cmd(SPI1, ENABLE);
}



从机**********************************************************************************************
//**********************SPI初始化函数*********************************
void SPI_Configuration(void)
{
  SPI_InitTypeDef  SPI_InitStructure;
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
  SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;      
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//SPI_FirstBit_MSB;
  SPI_InitStructure.SPI_CRCPolynomial = 7;
  SPI_Init(SPI1, &SPI_InitStructure);
  SPI_Cmd(SPI1, ENABLE);
}


因为从机的片选需要用主机进行控制。(实际上我是6片单片机进行的SPI通讯。)所以 SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;  这里从机一定要配置成hard硬件片选模式。只有这样才能用主机的IO才可以控制从机

使用特权

评论回复
7
yunyoushuaiqi|  楼主 | 2016-7-21 19:55 | 只看该作者
接下来其实就是最关键的地方了也是我出现了很多问题的地方。我先说我最开始直接用官方的库函数出现的问题。主机从机的代码这里是一样的。我先发一个有问题的。
u8 SPI_Send_Byte(u8 byte)
{
  while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)==RESET);        //判断是否忙,不忙的时候才发送数据
        SPI_I2S_SendData(SPI1, byte);                                      //发送数据
  while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)==RESET);       //判断接收标志
  return         SPI_I2S_ReceiveData(SPI1);                                 //返回接收数据              
}


这里使用这个函数用主机给从机发送数据的时候是完全没有问题的。数据可以很稳定的发送和接收。但是当从机给主机发送的时候就会发生数据错乱的问题。所以从机反数据的时候我浪费了很长的时间去测试。包括使用示波器观察。因为spi是全双工通信的。所以无论你怎样工作数据收发都是同步进行的。而芯片内部寄存器实际上只有一个来存储这些数据。如果没有及时的清空就会对数据产生影响。所以用这个函数就会发生紊乱。

使用特权

评论回复
8
yunyoushuaiqi|  楼主 | 2016-7-21 20:01 | 只看该作者
那么要解决这个函数的问题,实际上就要把数据分开发送,就是一个字节一个字节的发送。发一个的同时接受一个。一发一收永远同步进行。因此我这里套用了一个别人的代码。不过当时这个代码还是有问题的。一会再说。先发一个最原始的代码。
网上的原始代码*************************************************************************************************
u8 SPI_Send_Byte(u8 byte)
{
  while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)==RESET);        //判断是否忙,不忙的时候才发送数据
        SPI_I2S_SendData(SPI1, byte);                                      //发送数据
  while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)==RESET);       //判断接收标志
  return         SPI_I2S_ReceiveData(SPI1);                                 //返回接收数据              
}



u8 SPI1_ReadWriteByte(u8 TxData)     //这个代码是抄的原子大哥的,我就不BB了  
{
u16 retry=0;
  u8 data;
while((SPI1->SR&1<<1)==0)//等待发送区空
{
retry++;
if(retry>0XFFFE)return 0;
}         
SPI1->DR=TxData;         //发送一个byte
retry=0;
while((SPI1->SR&1<<0)==0) //等待接收完一个byte  
{
retry++;
if(retry>0XFFFE)return 0;
}         

   data=SPI1->DR;
return SPI1->DR;       //返回收到的数据          
}


这段代码表面看起来天衣无缝。并且使用主机发送的时候也是没有问题的。当我用从机返回数据的时候我还是出现了之前数据错误的问题。这下我纠结了。理论上是不应该有问题。因此还是只能从程序上找问题。其实这个问题就是最后一句话
return SPI1->DR;       //返回收到的数据       

使用特权

评论回复
9
yunyoushuaiqi|  楼主 | 2016-7-21 20:06 | 只看该作者
因为从机需要及时的把数据发送出去,如果还是返回SPI1->DR; 这个寄存器的数据其实已经不是刚刚的数据了,因为只有一个寄存器,而且全双工一直在工作。这时的寄存器已经是一个不稳定状态了,数据当然会发生紊乱。所以这里需要直接返回的是这个变量的值。return SPI1->DR;     改成return data;     这样就可以了,因为变量的值是不会被刷新的。最后实际测试也是完全正确的。从机也可以很稳定的发送数据。

使用特权

评论回复
10
yunyoushuaiqi|  楼主 | 2016-7-21 20:12 | 只看该作者
下面我发送一下我的主函数。测试程序。
主机***************************************************************************************
#include "pbdata.h"
u8 a;
u8 b;
int main(void)
{       
        RCC_Configuration();
        GPIO_Configuration();
  SPI_Configuration();
        USART_Configration();
  NVIC_Configuration();            //中断优先级配置
//        GPIO_ResetBits(GPIOB,GPIO_Pin_5);//从机开始工作
//        GPIO_SetBits(GPIOB,GPIO_Pin_5);//从机停止工作
        while(1)
        {
                if(a==1)
                {
                        GPIO_ResetBits(GPIOB,GPIO_Pin_5);//从机开始工作
                        if(b==1)
                        {
//                          while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)==RESET);        //判断是否忙,不忙的时候才发送数据               
//             // SPI1->DR=spisend1;//发送
//                                SPI_I2S_SendData(SPI1, spisend1);                                      //发送数据
//                                while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)==RESET);       //判断接收标志
//       // spivalue1=SPI1->DR;//接收
//                                spivalue1=SPI_I2S_ReceiveData(SPI1);
                                 u8 a=8;                    
                while(a--)
                                        {
                                    spivalue1=SPI1_ReadWriteByte( spisend1);
                                        }
                 }       

                          
                       
                }
                                if(a==0)
                {
                  GPIO_SetBits(GPIOB,GPIO_Pin_5);//从机停止工作
                }
    delay_ms(2);
         
        }
}




这里的a,b是为了在线调试的时候对片选和收发开关进行控制。



从机***********************************************************************************************

#include "pbdata.h"
u8 a;
int main(void)
{
        RCC_Configuration();
        GPIO_Configuration();
  SPI_Configuration();
        USART_Configration();
  NVIC_Configuration();            //中断优先级配置
        while(1)
        {              u8 a=8;                    
               if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4)==0)
                                 {
                                 while(a--)
                                        {
                                        spivalue2= SPI1_ReadWriteByte(spisend2);     //这个代码是抄的原子大哥的,我就不BB了
                                        }
                                 
                                 }




               

      
        }
}


while(a--)这个是因为我发送的数据是8帧的。所以分8次发送。
spisend和spivalue是两个全局变量。在线调试的时候直接观察自己收发的数据。







使用特权

评论回复
11
yunyoushuaiqi|  楼主 | 2016-7-21 20:17 | 只看该作者
spi一般用作片间通讯较多。很少使用板间通讯。因此我也在担心不稳定的问题。所以我特地亲自只做了3根长度2米5左右的线进行了测试。没有添加上拉电阻等原件。实际测试发现数据还是很稳定的。因此可以判定在这个距离以内是没有必要去担心的。另外提醒大家在做这个实验的时候一定要记得两个电路板要共地不要忽略了这个。下面上几个图片

使用特权

评论回复
12
yunyoushuaiqi|  楼主 | 2016-7-21 20:18 | 只看该作者
两个测试电路板

使用特权

评论回复
13
582981826| | 2016-7-21 21:44 | 只看该作者
还没来得及看,回复帮顶下,lz把代码贴上去可能看起来要好点

使用特权

评论回复
14
tjlwl| | 2016-7-22 00:06 | 只看该作者
顶一个

使用特权

评论回复
15
icefox225| | 2016-7-22 07:22 | 只看该作者
MARK

使用特权

评论回复
16
ticomi| | 2016-7-22 08:35 | 只看该作者
赞一个

使用特权

评论回复
17
feiban001| | 2016-7-22 08:49 | 只看该作者
感谢分享

使用特权

评论回复
18
jmpw| | 2016-7-22 08:51 | 只看该作者
很好

使用特权

评论回复
19
yunyoushuaiqi|  楼主 | 2016-7-22 08:56 | 只看该作者
稍后把源码文件发上去。感谢大家支持。

使用特权

评论回复
20
pener| | 2016-7-22 09:05 | 只看该作者
这个挺好

使用特权

评论回复
21
yunyoushuaiqi|  楼主 | 2016-7-22 09:10 | 只看该作者
为什么我上传不了图片。老是提示失败?

使用特权

评论回复
发新帖 本帖赏金 12.20元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

21

主题

185

帖子

12

粉丝