17. STM32——SPI硬件
STM32——SPI
- SPI协议
- SPI接口
- SPI接口框图
- SPI工作原理
- 时钟信号的相位和极性
- CPHA = 0
- CPHA = 1
- SPI中断
- 状态标志
- 发送缓存器空闲标志(TXE)
- 接收缓冲器非空(RXNE)
- 忙BUSY标志
- SPI引脚配置
- 从设备引脚管理(NSS)
- ①软件模式:
- ②硬件模式:
- SPI结构体
- SPI配置过程
- ①配置引脚,使能时钟
- ②初始化SPI,设置工作模式
- ③使能SPIx
- ④SPI传输数据
- ⑤查看SPI传输状态
- 整合
- 初始化SPI
- 读写字节
- 设置SPI的波特率
- spi.h
- spi.c
SPI协议
SPI是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。
SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省了空间,提供方便,主要应用在EEPROM,FLASH,实时时钟,AD转换器还有数字信号处理直接。
SPI接口
SPI总线包括4条逻辑线,定义如下:
MISO:Master input slave output 主机输入,从机输出(数据来自从机);
MOSI:Master output slave input 主机输出,从机输入(数据来自主机);
SCLK:Serial Clock 串行时钟信号,由主机产生发送给从机;SCLK也可以是SCK
SS:Slave Select 片选信号,由主机发送,以控制与哪个从机通信,通常是低电平有效信号。
SPI接口框图
SPI工作原理
①硬件为四根线。
②主机和从机都有一个串行移位寄存器,主机通过SPI的串行移位寄存器写入一个字节发送一次传输。
③串行移位寄存器通过MOSI信号线将字节发送给从机,从机将自己的串行移位寄存器的内容通过MISO返回给主机,这样两个移位寄存器的内容被交换。
④外设的读写操作同步完成,只进行写操作,则忽略读操作;主机只进行从机的读操作,则主机须发送一个空字节给从机引发传输。
时钟信号的相位和极性
SPI_CR寄存器的CPOL和CPHA位,能够组合成四种可能的时序关系,CPOL(时钟极性)位控制在没有数据传输时时钟的空闲状态电平,此位对主模式和从模式下的设备都有效。如果CPOL被清’0’,SCK引脚在空闲状态下保持低电平;如果CPOL被置‘1’,SCK引脚在空闲状态保持高电平。
CPHA = 0
如果CPHA位被清0,SCK时钟的第一个边沿(0为下降沿,1为上升沿)进行数据位采集,数据在第一个时钟边沿被锁存。
CPHA = 1
如果CPHA(时钟相位)位被置‘1’,SCK时钟的第二个边沿(0为下降沿,1为上升沿)进行数据位的采样,数据在第二个时钟边沿被锁存.
SPI中断
状态标志
通过三个标志可以完全监控SPI总线的状态
发送缓存器空闲标志(TXE)
此标志为1的时候,表示发送缓冲寄存器为空,可以写入下一个待发送数据进入缓冲器中,当写入SPI_DR(数据寄存器)时,TXE标志被清除。
接收缓冲器非空(RXNE)
此标志为1表明接收缓冲器中包含有效数据,读SPI数据寄存器可以清楚此标志。
忙BUSY标志
BSY标志由硬件设置与清楚,此标志表明SPI通信层的状态。
SPI引脚配置
从设备引脚管理(NSS)
①软件模式:
可以设置SPI_CR1寄存器的SSM位来使能这种模式,在这种模式下NSS引脚可以用作它用,而内部NSS信号电平可以通过写SPI_CR1的SSI位来驱动。
②硬件模式:
第一种情况:NSS输出使能,当STM32工作为SPI模式的时,NSS输出已经通过SPI_CR2寄存器的SSOE位使能,这时NSS引脚被拉低,所有NSS引脚与这个主SPI的NSS引脚相连并配置为硬件NSS的SPI设备,将自动变成从的SPI设备。
第二种情况:NSS输出被关闭:允许操作于多主环境。
SPI结构体
typedef struct
{uint16_t SPI_Direction; //方向 uint16_t SPI_Mode; //模式uint16_t SPI_DataSize; //数据大小uint16_t SPI_CPOL; //时钟极性uint16_t SPI_CPHA; //时钟相位uint16_t SPI_NSS; //NSS位uint16_t SPI_BaudRatePrescaler; //波特率uint16_t SPI_FirstBit; //选择数据传送方向高位开始还是低位开始 uint16_t SPI_CRCPolynomial; //CRC校验位
}SPI_InitTypeDef;
SPI配置过程
①配置引脚,使能时钟
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
②初始化SPI,设置工作模式
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
③使能SPIx
void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);
④SPI传输数据
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);
⑤查看SPI传输状态
FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
整合
初始化SPI
#include "SPI.h"
#include "stm32f10x.h"void SPI2_Init(void)//SPI2初始化 主模式
{SPI_InitTypeDef SPI_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;/* Enable SPI1 and GPIOA clocks */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);/* Configure SPI1 pins: NSS, SCK, MISO and MOSI */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;//SCK MOSI GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//PB13/14/15复用推挽输出 GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_SetBits(GPIOB,GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15);//PB13/14/15上拉/* SPI2 configuration */ SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI2设置为两线全双工SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI2为主模式SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //SPI发送接收8位帧结构SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //串行时钟在不操作(空闲)时,时钟为高电平SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //第二个时钟沿开始采样数据(此处为上升沿采集数据)SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由软件(使用SSI位)管理SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; //定义波特率预分频的值:波特率预分频值为8,分频后为9MHZSPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从MSB位开始 高位SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式SPI_Init(SPI2, &SPI_InitStructure);/* Enable SPI2 */SPI_Cmd(SPI2, ENABLE); //使能SPI1外设
}
读写字节
u8 SPI2_ReadWriteByte(u8 Data)
{unsigned char t = 0;while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET){t++;if(t>=200){return 0;}}SPI_I2S_SendData(SPI2, Data);while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET){t++;if(t>=200){return 0;}}return SPI_I2S_ReceiveData(SPI2);}
设置SPI的波特率
void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)
{assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));SPI2->CR1 &= 0XFFC7; //&1111 1111 1100 0111 把D3~D4清零SPI2->CR1 |= SPI_BaudRatePrescaler; //这里是把SPI_BaudRatePrescaler的值与SPI2->CR1中的值按2进制的方式相加//如果SPI_BaudRatePrescaler的值为0x038,二进制数为00111000与1111 1111 1100 0111相或后结果是1111 1111 1111 1111->0XFFFF; SPI_Cmd(SPI2, ENABLE);}
spi.h
#ifndef __spi_h__
#define __spi_h__#include "stm32f10x.h"void SPI2_Init(void) ;//SPI2初始化 主模式
u8 SPI2_ReadWriteByte(u8 dat);
void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler);#endif
spi.c
#include "spi.h"
#include "stm32f10x.h"void SPI2_Init(void)//SPI2初始化 主模式
{SPI_InitTypeDef SPI_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;/* Enable SPI1 and GPIOA clocks */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);/* Configure SPI1 pins: NSS, SCK, MISO and MOSI */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;//SCK MOSI GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽 GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_SetBits(GPIOB,GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15);/* SPI2 configuration */ SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI2设置为两线全双工SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI2为主模式SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //SPI发送接收8位帧结构SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //串行时钟在不操作(空闲)时,时钟为低电平SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //第一个时钟沿开始采样数据(此处为上升沿采集数据)SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由软件(使用SSI位)管理SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; //定义波特率预分频的值:波特率预分频值为8,分频后为9MHZSPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从MSB位开始 高位SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式SPI_Init(SPI2, &SPI_InitStructure);/* Enable SPI2 */SPI_Cmd(SPI2, ENABLE); //使能SPI1外设
}//SPIx 读写一个字节
//返回值:读取到的字节
//#define SPI_RW SPI1_ReadWriteByte //代替函数名,以便适用于后面函数u8 SPI2_ReadWriteByte(u8 Data)
{unsigned char t = 0;while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET){t++;if(t>=200){return 0;}}SPI_I2S_SendData(SPI2, Data);while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET){t++;if(t>=200){return 0;}}return SPI_I2S_ReceiveData(SPI2);}void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)
{assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));SPI2->CR1 &= 0XFFC7; //&1111 1111 1100 0111 把D3~D4清零SPI2->CR1 |= SPI_BaudRatePrescaler; //这里是把SPI_BaudRatePrescaler的值与SPI2->CR1中的值按2进制的方式相加//如果SPI_BaudRatePrescaler的值为0x038,二进制数为00111000与1111 1111 1100 0111相或后结果是1111 1111 1111 1111->0XFFFF; SPI_Cmd(SPI2, ENABLE);}
17. STM32——SPI硬件相关推荐
- STM32 SPI 软件NSS和硬件NSS解读
[导读] SSM可以控制内部NSS引脚与SSI(一个寄存器,软件模式)相连,还是与NSS外部引脚(真正的STM32引脚,硬件模式)相连.真正作用的是内部NSS引脚(内部NSS引脚才真正连接到SPI通信 ...
- stm32 spi nss硬件模式配置参考程序
By: Ailson Jack Date: 2020.11.27 个人博客:http://www.only2fire.com/ 本文在我博客的地址是:http://www.only2fire.com/ ...
- STM32 SPI NSS 引脚为不能拉高问题
使用过 STM32 的SPI 的朋友可能都会发现一个问题,那就是其NSS引脚 在硬件模式下无法自动拉高,可能也因为这个原因致使 .大部分朋友在使用SPI时候都是优先选用软件 控制NSS引脚的工作模式 ...
- STM32 SPI难点浅析
我用的是战舰STM32开发板,两个知识点,一:是STM32 SPI的原理:二:用STM32自带的SPI对外部flash(W25Q64)的读写. 一:STM32 SPI的原理 先上图: 主模 ...
- 17章 SPI控制器(XIlinx ZYNQ-7000 SOC UG-585文档)
第17章 SPI控制器 注:本文为笔者自己翻译的XILINX ZYNQ-7000 SOC UG-585官方文档,文档版本UG585 (v1.12.2) July 1, 2018 文章目录 第17章 S ...
- STM32——SPI接口
STM32--SPI接口 宗旨:技术的学习是有限的,分享的精神是无限的. 一.SPI协议[SerialPeripheral Interface] 串行外围设备接口,是一种高速全双工的通信总线.在ADC ...
- stm32 SPI、FLASH
main.c FLASH:掉电后数据不丢失,U 盘.SD 卡.SSD 固态硬盘.STM32 芯片内部用于存储程序的设备,都是 FLASH 类型的存储器.FLASH芯片(W25Q64)是一种使用 SPI ...
- stm32 SPI架构
STM32 芯片集成了专门用于 SPI 协议通讯的外设. 通讯引脚:SPI 硬件架构从 MOSI.MISO.SCK 及 NSS 线展开:STM32 芯片有多个 SPI 外设,它们的 SPI 通讯信号引 ...
- STM32 SPI NSS 作用
STM32 SPI NSS 作用 原创 2017年06月16日 11:18:14 142 SSM可以控制内部NSS引脚与SSI(一个寄存器,软件模式)相连,还是与NSS外部引脚(真正的STM32引脚, ...
最新文章
- Android的Application的生命周期方法
- 元宇宙教不会区块链赚钱
- CDN视频流中的3个问题以及解决方法
- 2021CSP:游记
- 防火墙阻止网页连接网络连接_win7电脑打不开网页怎么办 win7电脑打不开网页解决步骤【图文步骤】...
- Android开发之AudioManager(音频管理器)详解
- 深度学习与计算机视觉教程(17) | 深度强化学习 (马尔可夫决策过程,Q-Learning,DQN)(CV通关指南·完结)
- python句柄无效_使用pyinstaller打包,subprocess报“句柄无效”错误的解决方法
- 日记 [2006年06月06日] 庞加莱猜想
- RW文档翻译学习——RW-BLE-KERNEL核心函数功能
- Mysql基础篇(2)—— 单行函数和聚合函数
- 群晖emby服务端下载(弃坑,官网已经能顺畅访问)
- 国内自由下载jar包的地址
- Bootstrap整体架构
- 删除文件后,磁盘可用空间并没有释放怎么办?
- java 反射为何耗性能_Java反射的性能成本
- 如何将一个现有的Vue网页项目封装成electron桌面应用(转载)
- 《电路分析导论(原书第12版)》一导读
- 高精度加法(C++基础算法)
- 拥挤的自动驾驶赛道,谁会成为最终赢家
热门文章
- python加速度算位移,计算加速度给定速度
- IDEA的Maven Show Dependency使用心得
- java基础-Idea的使用和方法
- ESP32S2 固件烧录需满足的硬件环境整理
- [安卓开发Android][叠层 层叠 卡片效果]RecyclerView与CardView的混合使用
- 解决双显卡笔记本安装XP系统后独立显卡无法驱动的问题!Win7双显卡笔记本换XP系统如何安装独立显卡驱动!
- 2017计算机应用期末考核,2017年秋季计算机应用基础期末考核作业.pdf
- vs使用未初始化的内存怎么解决_C语言内存管理(转载)
- Flask(十二)——消息闪现
- ps -ef|grep tomcat是啥意思呢?(☆)