OpenEdv-开源电子网

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

SPI读和写之间需不需要间隔

[复制链接]

18

主题

72

帖子

0

精华

初级会员

Rank: 2

积分
158
金钱
158
注册时间
2017-4-24
在线时间
29 小时
发表于 2017-9-24 22:48:17 | 显示全部楼层 |阅读模式
10金钱
如题,SPI的发和收是同步进行的,正点的程序如下,比如在前8个周期写SPI,然后在8个时钟周期后进行读操作,那会不会因为还没有到后8个周期就进行了读操作导致读到错误数据?

u8 SPI1_ReadWriteByte(u8 TxData)
{               
        u8 retry=0;                                        
        while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET)
                {
                retry++;
                if(retry>200)return 0;
                }                          
        SPI_I2S_SendData(SPI1, TxData);
        retry=0;

        while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)
                {
                retry++;
                if(retry>200)return 0;
                }                                                              
        return SPI_I2S_ReceiveData(SPI1);                                     
}


最佳答案

查看完整内容[请看2#楼]

可以肯定地说:不可能!除非你没有按照规则操作。spi是串行通信,看时序图就知道,一个时钟脉冲传送/接收一位,对于你上面说的模式,只有发送完,才会进入接收状态;如果用硬件spi,发送完/接受完会有相应的状态寄存器指示,如果是模拟spi,则需要自己计数时钟个数进行一位一位的发送/接收。还有其他一些非标准spi,就是真正的收发同时进行,发送一位同时接收一位,发送完一字节也就接收完一字节。 综上,硬件spi你根 ...
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

2

主题

685

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3448
金钱
3448
注册时间
2017-7-4
在线时间
869 小时
发表于 2017-9-24 22:48:18 | 显示全部楼层
      可以肯定地说:不可能!除非你没有按照规则操作。spi是串行通信,看时序图就知道,一个时钟脉冲传送/接收一位,对于你上面说的模式,只有发送完,才会进入接收状态;如果用硬件spi,发送完/接受完会有相应的状态寄存器指示,如果是模拟spi,则需要自己计数时钟个数进行一位一位的发送/接收。还有其他一些非标准spi,就是真正的收发同时进行,发送一位同时接收一位,发送完一字节也就接收完一字节。
      综上,硬件spi你根据状态标志去读写,模拟spi自行计算脉冲数,根本不会出现你担心的。
回复

使用道具 举报

18

主题

72

帖子

0

精华

初级会员

Rank: 2

积分
158
金钱
158
注册时间
2017-4-24
在线时间
29 小时
 楼主| 发表于 2017-9-25 15:57:09 | 显示全部楼层
Acuity 发表于 2017-9-24 23:55
可以肯定地说:不可能!除非你没有按照规则操作。spi是串行通信,看时序图就知道,一个时钟脉冲传送/ ...

谢谢你,还有一个问题就是我读W25Q16的ID本来应该是EF14,但是有时候读出来是14EF刚好反了时好时坏,这是为什么
回复

使用道具 举报

18

主题

72

帖子

0

精华

初级会员

Rank: 2

积分
158
金钱
158
注册时间
2017-4-24
在线时间
29 小时
 楼主| 发表于 2017-9-25 17:03:03 | 显示全部楼层
而且复位芯片后用LED来指示状态,10次有三四次失败

        GPIO_InitTypeDef GPIO_InitStructure;
  SPI_InitTypeDef  SPI_InitStructure;

        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//PORTB时钟使能
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);//SPI2时钟使能        

        GPIO_InitStructure.GPIO_Pin = SCK | MISO | MOSI;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //PB13/14/15复用推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(SPI_Port, &GPIO_InitStructure);//初始化GPIOB

        GPIO_InitStructure.GPIO_Pin = NSS;  // PB12 推挽
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(SPI_Port, &GPIO_InitStructure);
       
        GPIO_SetBits(SPI_Port,NSS);       
        GPIO_SetBits(SPI_Port,SCK | MISO | MOSI);  //PB13/14/15上拉

        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_8b;                //设置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_256;                //定义波特率预分频的值:波特率预分频值为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外设
        SPI2_SetSpeed(SPI_BaudRatePrescaler_32);
        SPI2_ReadWriteByte(0xff);//启动传输
回复

使用道具 举报

18

主题

72

帖子

0

精华

初级会员

Rank: 2

积分
158
金钱
158
注册时间
2017-4-24
在线时间
29 小时
 楼主| 发表于 2017-9-25 17:03:14 | 显示全部楼层
用的是正点的代码
回复

使用道具 举报

2

主题

685

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3448
金钱
3448
注册时间
2017-7-4
在线时间
869 小时
发表于 2017-9-25 18:10:08 | 显示全部楼层
爱学习的猫 发表于 2017-9-25 15:57
谢谢你,还有一个问题就是我读W25Q16的ID本来应该是EF14,但是有时候读出来是14EF刚好反了时好时坏,这是 ...

没遇到过这问题,spi还是蛮稳定的。硬件走线问题?配置不对?把操作w25q16代码发上来
回复

使用道具 举报

18

主题

72

帖子

0

精华

初级会员

Rank: 2

积分
158
金钱
158
注册时间
2017-4-24
在线时间
29 小时
 楼主| 发表于 2017-9-26 10:22:13 | 显示全部楼层
Acuity 发表于 2017-9-25 18:10
没遇到过这问题,spi还是蛮稳定的。硬件走线问题?配置不对?把操作w25q16代码发上来

#include "SPI.h"

void SPI2_Init(void){
        GPIO_InitTypeDef GPIO_InitStructure;
  SPI_InitTypeDef  SPI_InitStructure;

        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//PORTB时钟使能
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);//SPI2时钟使能        

        GPIO_InitStructure.GPIO_Pin = SCK | MISO | MOSI;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //PB13/14/15复用推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(SPI_Port, &GPIO_InitStructure);//初始化GPIOB

        GPIO_InitStructure.GPIO_Pin = NSS;  // PB12 推挽
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(SPI_Port, &GPIO_InitStructure);
       
        GPIO_SetBits(SPI_Port,NSS);       
        GPIO_SetBits(SPI_Port,SCK | MISO | MOSI);  //PB13/14/15上拉

        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_8b;                //设置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_256;                //定义波特率预分频的值:波特率预分频值为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外设
        SPI2_SetSpeed(SPI_BaudRatePrescaler_32);
        SPI2_ReadWriteByte(0xff);//启动传输                 
}

void CS_ON(void){
  GPIO_ResetBits(SPI_Port,NSS);
}

void CS_OFF(void){
  GPIO_SetBits(SPI_Port,NSS);
}

void SPI2_SetSpeed(u8 SpeedSet)
{
  assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
        SPI2->CR1&=0XFFC7;
        SPI2->CR1|=SpeedSet;       
        SPI_Cmd(SPI2,ENABLE);
}

u8 SPI2_ReadWriteByte(u8 TxData)
{
        u16 retry=0;                                        
        while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
        {
                retry++;
                if(retry>500)return 0;
        }                          
        SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个数据
        retry=0;
        while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //检查指定的SPI标志位设置与否:接受缓存非空标志位
        {
                retry++;
                if(retry>500)return 0;
        }                                                              
        return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据                                            
}

这个是初始化代码
回复

使用道具 举报

18

主题

72

帖子

0

精华

初级会员

Rank: 2

积分
158
金钱
158
注册时间
2017-4-24
在线时间
29 小时
 楼主| 发表于 2017-9-26 10:22:41 | 显示全部楼层
其余的直接是用正点的代码,我就是把SPI1改成了2
回复

使用道具 举报

86

主题

567

帖子

0

精华

高级会员

Rank: 4

积分
825
金钱
825
注册时间
2014-9-30
在线时间
139 小时
发表于 2017-9-26 17:07:52 | 显示全部楼层
本帖最后由 caosix 于 2017-9-26 17:13 编辑

SPI 是 全双工 的 你发送的 同时,
——是可以 接收 数据的 呀 。。。

关键是:你的代码 要采用 任务驱动模式

不要有任何的 循环死等 或者 死等某个
标志 完成,,这样的延迟 。。。
————————————————————
任务驱动模式:接收 和 发送 都要做成 队列
有数据发送就以最快速度 “扔到” 发送队列里,
然后就运行后面该运行的程序段落。
回复

使用道具 举报

39

主题

212

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
303
金钱
303
注册时间
2016-4-21
在线时间
129 小时
发表于 2017-9-27 13:45:54 | 显示全部楼层
caosix 发表于 2017-9-26 17:07
SPI 是 全双工 的 你发送的 同时,
——是可以 接收 数据的 呀 。。。

看了Linux内核源码,才发现原来也是丢入队列来处理的,
不过不是很清楚,为什么不要有延迟?只要控制好时钟,让他等着不也可以吗
本人交流群:136045527,欢迎各位大佬和萌新
回复

使用道具 举报

64

主题

446

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1032
金钱
1032
注册时间
2017-7-26
在线时间
275 小时
发表于 2017-9-27 17:43:30 | 显示全部楼层
我用SPI1,为了提高速度直接传送完接着读取,中间没有任何处理,当然,速度开到了最快(36M)
回复

使用道具 举报

86

主题

567

帖子

0

精华

高级会员

Rank: 4

积分
825
金钱
825
注册时间
2014-9-30
在线时间
139 小时
发表于 2017-9-28 13:14:00 | 显示全部楼层
一只风筝 发表于 2017-9-27 13:45
看了Linux内核源码,才发现原来也是丢入队列来处理的,
不过不是很清楚,为什么不要有延迟?只要控制好 ...

多任务 、多线程 的 软件系统,

就是切忌:“原地等待某个事件”,
一定要有 动态、全局 观念 哦。。

道理很简单,需要你自己 【悟】。
回复

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

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

GMT+8, 2025-6-17 10:04

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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