OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 3249|回复: 17

原创SPI双机通信,分频系数受限求助

[复制链接]

2

主题

10

帖子

0

精华

新手上路

积分
23
金钱
23
注册时间
2019-4-28
在线时间
7 小时
发表于 2019-4-28 20:13:02 | 显示全部楼层 |阅读模式
2金钱
最近自己写了一个SPI双机通信的工程,遇到了点问题。
需求:两块stm32f4的板子通过SPI进行全双工通信,主机可以向从机写入数据或者读取数据。设计方案:
主机写入从机:发送包含实际数据的完整帧,发送完后等待从机的确认字节,完成任务;
主机读取从机:发送包含所要数据的信息的指令帧,从机接收到后准备好包含实际数据的完整帧,在主机的驱动下返回;
主机采用软件片选,从机采用硬件片选。(这一块花了蛮久时间搞懂)
整个机制建立在中断基础上:主机初始化时仅使能接收中断,从机初始化时使能发送接收中断,所以从机初始化后会立即向发送缓冲区填入一个字节。当主机想要发送一帧时,在准备好实际的数据后,使能发送中断就可以将整个帧数据逐字节的发送出去。
问题描述:
初始化时分频系数最低到16可以正常工作,但4和8均会出现丢失数据导致无法正常通信。
主从机代码采用预编译的方式写在同一个工程中,下面贴代码

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

2

主题

10

帖子

0

精华

新手上路

积分
23
金钱
23
注册时间
2019-4-28
在线时间
7 小时
 楼主| 发表于 2019-4-28 20:14:45 | 显示全部楼层
本帖最后由 bleemcs 于 2019-4-28 20:18 编辑

void SPI_INIT()
{
  GPIO_InitTypeDef  GPIO_InitStructure;
        SPI_InitTypeDef   SPI_InitStructure;
        NVIC_InitTypeDef   NVIC_InitStructure;
        SPI_RCC_ENABLE;
        SPI_GPIO_RCC_ENABLE;                       
#ifdef Master                        //主机软件片选
        GPIO_InitStructure.GPIO_Pin = SPI_SCLK|SPI_MISO|SPI_MOSI;        //复用初始化
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;                                                                                       
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
       
        GPIO_PinAFConfig(SPI_GPIO,SPI_MOSI_AF,SPI_AF);                                                       
        GPIO_PinAFConfig(SPI_GPIO,SPI_MISO_AF,SPI_AF);
        GPIO_PinAFConfig(SPI_GPIO,SPI_SCLK_AF,SPI_AF);
        GPIO_Init(SPI_GPIO, &GPIO_InitStructure);
       
        SOFT_NSS_GPIO_RCC_ENABLE;                                                        //软件片选另外指定一个GPIO口初始化为输出
        GPIO_InitStructure.GPIO_Pin = SOFT_NSS_PIN;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
        GPIO_Init(SOFT_NSS_GPIO, &GPIO_InitStructure);
#endif
#ifdef Slave                                //从机硬件片选
        GPIO_InitStructure.GPIO_Pin = SPI_HARD_NSS|SPI_SCLK|SPI_MISO|SPI_MOSI;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;                                                                                       
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
        GPIO_Init(SPI_GPIO, &GPIO_InitStructure);
        GPIO_PinAFConfig(SPI_GPIO,SPI_MOSI_AF,SPI_AF);                                                       
        GPIO_PinAFConfig(SPI_GPIO,SPI_MISO_AF,SPI_AF);
        GPIO_PinAFConfig(SPI_GPIO,SPI_SCLK_AF,SPI_AF);
        GPIO_PinAFConfig(SPI_GPIO,SPI_NSS_AF,SPI_AF);
#endif
        RCC_APB2PeriphResetCmd(User_SPI_RCC, ENABLE);                                                       
        RCC_APB2PeriphResetCmd(User_SPI_RCC, DISABLE);                                                       

        SPI_InitStructure.SPI_Direction= SPI_Direction_2Lines_FullDuplex;       
        SPI_InitStructure.SPI_DataSize= SPI_DataSize_8b;                                                       
        SPI_InitStructure.SPI_CPOL= SPI_CPOL_High;                                                                       
        SPI_InitStructure.SPI_CPHA= SPI_CPHA_1Edge;                                                                       
        SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;                        //8分频会出现丢字节
        SPI_InitStructure.SPI_FirstBit= SPI_FirstBit_MSB;                                                       
        SPI_InitStructure.SPI_CRCPolynomial= 7;                                                                               
#ifdef Master
        SPI_InitStructure.SPI_Mode= SPI_Mode_Master;       
        SPI_InitStructure.SPI_NSS= SPI_NSS_Soft;               
        SPI_SSOutputCmd(User_SPI, ENABLE);               
#endif               
#ifdef Slave               
        SPI_InitStructure.SPI_Mode= SPI_Mode_Slave;               
        SPI_InitStructure.SPI_NSS= SPI_NSS_Hard;               
        SPI_SSOutputCmd(User_SPI, DISABLE);               
#endif               
        SPI_Init(User_SPI, &SPI_InitStructure);               
                       
        NVIC_InitStructure.NVIC_IRQChannel = User_SPI_IRQ;                                       
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;               
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;               
        NVIC_Init(&NVIC_InitStructure);               
                       
        SPI_I2S_ITConfig(User_SPI,SPI_I2S_IT_RXNE, ENABLE);        //主机只使能接收中断
#ifdef Slave               
        SPI_I2S_ITConfig(User_SPI,SPI_I2S_IT_TXE, ENABLE);        //从机同时使能接收中断
#endif

        SPI_Cmd(User_SPI, ENABLE);       
        SPI_StructureInit();                //代码中结构体的初始化
#ifdef Master       
        SOFT_NSS_OUTPUT=0;                        //主机片选拉低
#endif
}

回复

使用道具 举报

2

主题

10

帖子

0

精华

新手上路

积分
23
金钱
23
注册时间
2019-4-28
在线时间
7 小时
 楼主| 发表于 2019-4-28 20:23:30 | 显示全部楼层
//SPI中断
void SPI1_IRQHandler(void)
{
        //发送中断
  if(SPI_I2S_GetFlagStatus(User_SPI, SPI_I2S_FLAG_TXE) != RESET)
        {
                switch(SPI_Structure.state)
                {
                        case READY:
                                SPI_I2S_SendData(User_SPI, SPI_Structure.response);
                                break;
                        case COMMANDING:
                                #ifdef Master                                               
                                        SPI_SendCmdM();                        //主机逐字节发送指令帧
                                #endif
                                #ifdef Slave
                                        SPI_SendCmdReplyS();        //从机在接收主机指令时发送的无效字节
                                #endif
                                break;
                        case COMMANDED:
                                #ifdef Master
                                        SPI_RecvFrameReplyM();        //主机在读取从机数据时发送的无效字节
                                #endif
                                #ifdef Slave       
                                        SPI_SendFrameS();                //从机逐字节发送数据帧       
                                #endif                       
                                break;
                }
        }
        //接收中断
        if(SPI_I2S_GetFlagStatus(User_SPI, SPI_I2S_FLAG_RXNE) != RESET)
        {
                u8 data = SPI_I2S_ReceiveData(User_SPI);
                #ifdef Master
                        SPI_RecvDataM(data);                //主机接收从机的字节并解包
                #endif
                #ifdef Slave
                        SPI_RecvDataS(data);                //从机接收主机的字节并解包
                #endif
        }
}
回复

使用道具 举报

2

主题

10

帖子

0

精华

新手上路

积分
23
金钱
23
注册时间
2019-4-28
在线时间
7 小时
 楼主| 发表于 2019-4-28 20:24:16 | 显示全部楼层
主机供外部调用的函数

//供外部调用的主机发送函数
void SPI_MasterSend(u8 ms,SPI_COMState func,u16 base,u8 len)
{
        SPI_ExportData(ms,func,base,len);                //准备实际帧
        SPI_SendStartUp();                        //开始发送
}
//开始发送函数
void SPI_SendStartUp()
{
        if(!SPI_Structure.SPI_SendCmdHolder.valid) return;       
        SPI_Structure.state=COMMANDING;               
        SPI_Structure.com=SPI_Structure.SPI_SendCmdHolder.func;       
        SPI_Structure.sendIndex=0;               
        //以上为更新SPI结构体状态
        SPI_I2S_ITConfig(User_SPI,SPI_I2S_IT_TXE, ENABLE);        //使能发送中断后立即开始发送数据
}
回复

使用道具 举报

2

主题

10

帖子

0

精华

新手上路

积分
23
金钱
23
注册时间
2019-4-28
在线时间
7 小时
 楼主| 发表于 2019-4-28 20:31:22 | 显示全部楼层
关于片选我也是看了很久才搞明白是什么意思。之前为了让自己印象深一些写了个博客:https://blog.csdn.net/weixin_40185666/article/details/89297107。目前双机通信,主机读写我都已经实现了,问题就在于使用8分频或者4分频会出错,按道理SPI的通信在4分频21Mhz下应当是可以的,请问大家有什么修改的建议么?
回复

使用道具 举报

2

主题

10

帖子

0

精华

新手上路

积分
23
金钱
23
注册时间
2019-4-28
在线时间
7 小时
 楼主| 发表于 2019-4-28 21:06:05 | 显示全部楼层
这是硬件连接图,会不会和这个也有关系
WeChat Image_20190428205049.jpg
回复

使用道具 举报

109

主题

5564

帖子

0

精华

资深版主

Rank: 8Rank: 8

积分
10569
金钱
10569
注册时间
2017-2-18
在线时间
1913 小时
发表于 2019-4-28 22:00:15 | 显示全部楼层
帮顶~~
回复

使用道具 举报

2

主题

10

帖子

0

精华

新手上路

积分
23
金钱
23
注册时间
2019-4-28
在线时间
7 小时
 楼主| 发表于 2019-4-29 11:16:52 | 显示全部楼层
本帖最后由 bleemcs 于 2019-4-29 11:19 编辑

这是我的工程,默认使用stm32f407zg,主从切换在spi.h中调整宏定义即可,led和key中可能有一些宏定义需要修改;主机默认使用的是正点原子探索者,按键中断开启一次主机读写任务。经过简单的测试在16分频下能够正常工作。其中有关于通信解包的内容,我使用的通信协议如下:
帧头 1Byte:0x24
收发方 1Byte:0x48/0x84代表是主机给从机或从机给主机
功能码 1Byte:0xA1主机读,0xA2主机写
数据长度 1Byte:主机写时代表帧中的payload长度,主机读时代表想要读回的字节长度
基地址 2Byte:主从机背后有一个相同的数据结构体,数据读取主要是在这两个结构体之间的数据交换
实际数据(主机写时会有,主机读时无此项)
待定功能字节 1Byte
校验和 1Byte

目前测试过16分频下读写正常,但8分频开始就会丢字节,不稳定,会不会是我本身的中断机制设计的不行或者是我的硬件连接太简陋?中断机制在上面有简单的介绍,硬件连接发了图.感谢大家!

SPITest0429.rar

8.56 MB, 下载次数: 36

回复

使用道具 举报

2

主题

10

帖子

0

精华

新手上路

积分
23
金钱
23
注册时间
2019-4-28
在线时间
7 小时
 楼主| 发表于 2019-4-29 11:19:32 | 显示全部楼层
回复

使用道具 举报

0

主题

19

帖子

0

精华

初级会员

Rank: 2

积分
148
金钱
148
注册时间
2018-12-5
在线时间
39 小时
发表于 2019-10-1 21:32:51 | 显示全部楼层
请问使用的什么工具观察波形的?
回复

使用道具 举报

0

主题

19

帖子

0

精华

初级会员

Rank: 2

积分
148
金钱
148
注册时间
2018-12-5
在线时间
39 小时
发表于 2019-10-14 21:01:54 | 显示全部楼层
请问发送的数据是多少。本人使用逻辑分析仪进行观察,但是每次数据都不一样。
回复

使用道具 举报

0

主题

19

帖子

0

精华

初级会员

Rank: 2

积分
148
金钱
148
注册时间
2018-12-5
在线时间
39 小时
发表于 2019-10-16 09:30:54 | 显示全部楼层
本帖最后由 林深见鹿 于 2020-7-1 16:19 编辑

已解决,....
回复

使用道具 举报

7

主题

237

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1111
金钱
1111
注册时间
2019-5-6
在线时间
128 小时
发表于 2019-10-16 13:48:15 | 显示全部楼层
林深见鹿 发表于 2019-10-16 09:30
已解决,从CSDN来的

怎么解决的亲
回复

使用道具 举报

7

主题

237

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1111
金钱
1111
注册时间
2019-5-6
在线时间
128 小时
发表于 2019-10-16 13:49:00 | 显示全部楼层
帮楼主顶顶,SPI这块问题一直层出不穷。
回复

使用道具 举报

0

主题

19

帖子

0

精华

初级会员

Rank: 2

积分
148
金钱
148
注册时间
2018-12-5
在线时间
39 小时
发表于 2019-10-21 15:23:54 | 显示全部楼层

你这一问,没法回答的。亲,你要描述自己遇到的问题,才能帮你看看啊。
回复

使用道具 举报

0

主题

3

帖子

0

精华

新手上路

积分
29
金钱
29
注册时间
2019-7-5
在线时间
9 小时
发表于 2019-11-5 11:50:38 | 显示全部楼层
林深见鹿 发表于 2019-10-21 15:23
你这一问,没法回答的。亲,你要描述自己遇到的问题,才能帮你看看啊。

你好,我现在SPI双机通信,主机每5MS向从机发送32个字节的数据,从机在接收20个字节的数据后就无法进入接收中断服务函数,并且从机接收到的数据会错位,类似会漏掉几个字节这样。请问有什么方法么。
回复

使用道具 举报

0

主题

19

帖子

0

精华

初级会员

Rank: 2

积分
148
金钱
148
注册时间
2018-12-5
在线时间
39 小时
发表于 2019-12-16 10:56:04 | 显示全部楼层
王鲁豫 发表于 2019-11-5 11:50
你好,我现在SPI双机通信,主机每5MS向从机发送32个字节的数据,从机在接收20个字节的数据后就无法进入接 ...

你是用什么观察波形的?采样频率设置正确吗?
回复

使用道具 举报

233

主题

961

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1814
金钱
1814
注册时间
2011-10-9
在线时间
230 小时
发表于 2019-12-16 11:21:10 | 显示全部楼层
本帖最后由 simms01 于 2019-12-16 13:15 编辑

估计你把杜邦线 屏蔽一下 或者弄短2/3  就有改善  SCK和SI 高速通讯下面的波形传输同步问题   用带屏蔽的三芯线 弄短一些 直接焊接在排针上应该也会有改善


刚中午又看了下程序,发现处理判断回复都是在中断干了。速度快了肯定丢数据。 中断不要干活 只设置标志。收到一个存一个到内存,收完了设置完成标记 大循环再判断收到啥东西了 该不该回复。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-5-21 17:26

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表