第一次搞SPI

经过了痛苦的两天我终于算是搞懂了SPI了,看了网络上排名很高但是及其不负责任的例程踩了很多坑,终于静下心来分析寄存器得出了正确的结论。赶紧写下来以防忘记。
SPI的原理是,主机发送时钟信号给从机,然后主从机随着时钟信号,一个一个地交换在DR寄存器里面的值,过程如下图所示(图二右键在新标签中打开能看到动图)
这里写图片描述

这里写图片描述

这里写图片描述
所以我们常常可以看到SPI的代码是这么写的,发和收是一样的,这是因为SPI的发送和接受是一个环。不想看也行,就是示例代码看到明明是发数据却还要收不要觉得奇怪。

unsigned char SPI_SendByte(unsigned char Byte)
{
    //如果发送寄存器数据没有发送完,循环等待
    while(SPI_T1S_GetFlagStatus(SPI1,SPI_T1S_FLAG_TXE) == RESET);
    //往发送寄存器写入要发送的数据
    SPI_T1S_SendData(SPI2,Byte);
    //如果接受寄存器没有收到数据,循环
    while(SPI_T1S_GetFlagStatus(SPI1,SPI_T1S_FLAG_RXNE) == RESET);
    return SPI_T1S_ReceiveData(SPI1);
}

第一次弄SPI往往不知道怎么开始,其实最好的方法就是自己发送自己接受,我将SPI1设置为主机,SPI2设置为从机。我用的是STM32F103一般引脚都是一样的所以下面的代码其实能够直接抄过去,部分内容我会在代码中讲解。我这里贴出来的是全部的代码,没有一句代码是浪费的。顺便附上我的接线
A4-3V3
B12-GND
A5-B13
A6-B14
A7-B15

#include "stm32f10x.h"  

void SPI_Writebyte(u8 data);
u8 SPI_Slv_Sendbyte(u8 data);
u8 SPI_Slv_Readbyte();

int main(void)
{
    SPI_InitTypeDef SPI_InitStruct;
    GPIO_InitTypeDef GPIO_InitStruct;


    //enable clock
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 ,ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2 ,ENABLE);
    //很多教程都会忽视初始化GPIO,但是这个是必要的
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB ,ENABLE);

    //这里的GPIO初始化大部分都要的,除了主机的CS设置不设置无所谓,别的都是必要的,特别是从机的CS要是不设置根本跑不起来的。
    //SPI1 MOSI(PA7),CLK(PA5) output
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA,&GPIO_InitStruct);
    //SPI1 MISO(PA6),CS(PA4)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_4;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA,&GPIO_InitStruct);
    //SPI2 MOSI(PB15),CLK(PB13) input
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_15;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOB,&GPIO_InitStruct);
    //SPI2 MISO(PB14),CS(PB12)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_14;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOB,&GPIO_InitStruct);

    //SPI1 init Mater output
    SPI_InitStruct.SPI_Direction=SPI_Direction_2Lines_FullDuplex;
    SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
    SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;
    SPI_InitStruct.SPI_NSS = SPI_NSS_Hard;//作为初学者还是老老实实用硬件判断CS,简单暴力
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStruct.SPI_CRCPolynomial = 7;
    SPI_Init(SPI1, &SPI_InitStruct); 

    //SPI2 init Slave input
    SPI_InitStruct.SPI_Mode = SPI_Mode_Slave;
    //因为stm32的两个时钟不同,需要这样设置才能保证两个SPI的时钟相同
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
    SPI_Init(SPI2, &SPI_InitStruct);

    SPI_Cmd(SPI1,ENABLE);
    SPI_Cmd(SPI2,ENABLE);
    while(1)
    {
        SPI_Slv_Sendbyte(0x66);
        SPI_Writebyte(0x11);
        SPI_Slv_Readbyte();
    }

}

void SPI_Writebyte(u8 data)
{
    while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) == RESET);
    SPI_I2S_SendData(SPI1,data);
    while((SPI1->SR&1<<0)==0);
    //while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE) == RESET);
    SPI_I2S_ReceiveData(SPI1);

}

void SPI_Writebyte(u8 data)
{
    while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) == RESET);
    SPI_I2S_SendData(SPI1,data);
    while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE) == RESET);
    SPI_I2S_ReceiveData(SPI1);

}

u8 SPI_Slv_Sendbyte(u8 data)
{
    while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE) == RESET);
    SPI_I2S_SendData(SPI2,data);
}

u8 SPI_Slv_Readbyte()
{ 
    u8 temp;
    while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE) == RESET);
    temp = SPI_I2S_ReceiveData(SPI2);
    return temp;
}

如果上面的代码无法运行,则打开看看寄存器配置对了没,我的寄存器配置如下图所示,如果依旧有问题,我这里推荐一个讲解寄存器很不错的网页
SPI寄存器详解
这里写图片描述这里写图片描述

在搞SPI时踩过的坑
1.能够成功设置SPI1中的DR寄存器(最开始提过这个寄存器)的值,然后传输成功,但是就是无法设置成功SPI2中的值。我们可以在初始化后加一个断点来设置SPI2中DR的值。我们会发现无法设置,不管设置什么都会变成原来的值,但是SPI1的DR值原封不动。这是因为时钟是主机发出的,所以除非主机主动交换DR寄存器里的值,否则是无法交换的。所以我们可以在SPI2的DR寄存器里面输入一个值(然后会变回来,但是不要管),然后在SPI1的寄存器里再输入一个值然后回车,你会发现你原本想输入SPI2的DR值成功换到了SPI1的DR里面了。
2.在下面这个代码前加了断点
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE) == RESET);始终进不去,这个问题的原因在
这个链接里面的坑1里面有讲,主要是调试模式造成的祸
调试模式的坑

  • 9
    点赞
  • 75
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值