• 学习STM32第十五天


    SPI外设

    一、简介

    STM32F4XX内部集成硬件SPI收发电路,可以由硬件自动执行时钟生成、数据收发等功能,减轻CPU负担,可配置8位/16位数据帧,高位(最常用)/低位先行,三组SPI接口,支持DMA

    SPI框图
    由上图可知SPI是通过接收/发送缓冲区移位寄存器进行通信,其中SPI1是在APB2总线,SPI2、SPI3在APB1总线。发送和接收共用一个SR,即SPI是同步通信接口。SS引脚一般用GPIO口指定从机,硬件NSS引脚一般是用来配置多主机模式。
    发送:数据先进入TDR,经SR通过MOSI向从机输出
    接收:数据由MISO进入SR,然后经过RDR向地址数据总线输出
    由此可对上面SPI框图进行简化,基本结构如下
    SPI基本结构
    这里给出SPI主模式全双工连续传输模式下的时序图,SPI主模式全双工
    上图选择的是模式3,SCK高电平为空闲状态,在SCK第一个边沿移出数据,第二个编译移入数据。上面时序图采用小端模式,低位先行,这里对进行分析
    发送:

    • SS置低电平,开始时序,选中从机。此时TXE = 1,TDR为空;RXNE = 0,RDR为空。BSY = 1
    • 软件写入0xF1到SPI_DR,即要发送的第一个数据,此时TXE = 0,RXNE = 0,TDR非空
    • TDR中的0xF1会立刻转入到SR中,TDR清空,MOSI开始发送同时TXE = 1
    • 软件等待TXE = 1,然后写入0xF2到SPI_DR,即要发送的第二个数据,此时TXE = 0,RXNE = 0
    • TDR中的0xF2会随后自动进入SR,MOSI在发送完第一个数据会自动发送第二个数据
    • TDR发送完所有数据,TXE会自动置1,SR发送完所有数据后,BSY = 0

    接收:

    • SS置低电平,开始时序,选中从机。此时TXE = 1,TDR为空;RXNE = 0,RDR为空。BSY = 1
    • MISO依次接收从机的数据,输出到SR
    • SR中的数据以小端模式进入到SPI_DR中
    • 软件等待RXNE = 1,然后数据总线读取RDR中的数据0xA1,同时RXNE = 0,RDR变为空
    • MISO接收第二个数据,输出到SR
    • SR中的第二个数据以小端模式进入到SPI_DR中
    • 软件等待RXNE = 1,然后数据总线读取RDR中的第二个数据,同时RXNE = 0,RDR变为空
    • RDR接收完所有数据,RXNE = 0

    由上图可知,SPI全双工连续通信是交叉进行的,发送数据1,发送数据2,再接收数据1;发送数据3,再接收数据2;在时序上要求操作之间的间隙非常小。
    SPI非连续全双工通信
    非连续传输模式,只需要四行代码。上图是SPI模式3,SCK高电平为空闲状态,分析如下

    • SS置低电平,选中从机,开始时序此时TXE = 1,RXNE = 0,TDR为空
    • 软件写入0xF1到SPI_DR,此时TDR = 0xF1,TXE = 0
    • TDR中的0xF1立即进入SR中,MOSI开始发送0xF1,TDR清空,TXE = 1
    • 等待MOSI将第一个字节数据发送完毕,此时接收第一个字节数据的时序也完成,即RXNE = 1
    • 读取接收到的第一个字节数据,然后将第二个字节数据写入TDR,开始发送第二个数据
    • 等待MOSI发送完第二个字节数据,此时接收到了第二个字节数据
    • 读取完第二个字节数据,然后将第三个字节数据写入TDR,开始发送第三个数据

    整体流程就是:等待TXE = 1,写入数据到TDR,等待RXNE = 1,读取RDR数据。这样实现发送数据1接收数据1,发送数据2接收数据2。但是字节之间存在一定的间隙,降低传输效率。

    二、实验案例

    进行STM32F4XX对板载W25Q16读写,代码如下

    #include "stm32f4xx.h"                  // Device header
    
    //硬件SPI通信,采用非连续传输方案
    /*PB0引脚模拟SS输出*/
    void MySPI_W_SS(uint8_t BitValue)
    {
    	GPIO_WriteBit(GPIOB, GPIO_Pin_0, (BitAction)BitValue);//片选引脚输出
    }
    
    /*SS->PB0,MISO->PB4,MOSI->PB5,SCK->PB3,板载W25Q16支持SPI模式0和模式3*/
    /*
    *	SPI1是在APB2总线,SPI2、SPI3在APB1总线
    *	PB3: SPI1_SCK、SPI3_SCK
    *	PB4: SPI1_MISO、SPI3_MISO
    *	PB5: SPI1_MOSI、SPI3_MOSI
    *	PB0: 使用GPIO模拟SS
    */
    void MySPI_Init()
    {
    	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
    	
    	GPIO_InitTypeDef GPIO_InitStructure;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;	//PB3复用为SPI1_SCK,PB4复用为SPI1_MISO,PB5复用为SPI1_MOSI
    	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOB, &GPIO_InitStructure);
    	
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;				//使用GPIO模拟片选信号SS
    	GPIO_Init(GPIOB, &GPIO_InitStructure);
    	
    	GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_SPI1);	//GPIO引脚复用
    	GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_SPI1);
    	GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_SPI1);
    	
    	//SPI配置
    	SPI_InitTypeDef SPI_InitStructure;
    	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
    	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
    	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
    	SPI_InitStructure.SPI_CRCPolynomial = 7;				//CRC校验根据实际需求填写
    	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//数据帧大小
    	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//全双工
    	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;		//高位先行
    	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;			//这里选择主机
    	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;				//软件模拟片选SS
    	SPI_Cmd(SPI1, ENABLE);
    
    //	
    	MySPI_W_SS(1);											//默认是终止条件
    }
    
    /*起始条件*/
    void MySPI_Start()
    {
    	MySPI_W_SS(0);
    }
    /*终止条件*/
    void MySPI_End()
    {
    	MySPI_W_SS(1);
    }
    /*交换一个字节,这里选择模式0*/
    /*			SCK低电平为空闲状态
    *	SS下降沿启动,主机移出高位数据到MOSI
    *	SCK上升沿,主机移入高位数据MISO
    *	SCK下降沿,主机移出高位数据MOSI
    *			非连续传输需要四步
    */
    uint8_t MySPI_SwapByte_Mode0(uint8_t ByteSend)
    {
    	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET);	//等待TXE = 1
     	SPI_I2S_SendData(SPI1, ByteSend);								//将数据写入到DR中
    	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);	//等待RXNE = 1
    	return SPI_I2S_ReceiveData(SPI1);								//读取RDR的数据
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
  • 相关阅读:
    xgboost early_stop_rounds是如何生效的?
    深度学习——池化层笔记+代码
    java计算机毕业设计bs架构实习管理系统源码+mysql数据库+系统+lw文档+部署
    JBoss漏洞:JBOSS反序列化漏洞合集
    Java中JCP, JEP, JLS, JSR是什么
    docker部署nginx+反向代理配置/代理宿主机网段服务器
    基于SSM的鲜花商城系统
    Java中常用指令和工具
    【前端】CSS(1) —— CSS的基本语法和一些简单的选择器
    谁不想要一个自己的博客网站呢 - 搭建博客网站wordpress
  • 原文地址:https://blog.csdn.net/dabuding666/article/details/137867064