【STM32-学习笔记-9-】SPI通信

文章目录

  • SPI通信
    • Ⅰ、SPI通信概述
      • 1、SPI技术规格
        • 2、SPI应用
      • 3、硬件电路
        • 移位示意图
    • Ⅱ、SPI时序基本单元
      • ①、起始条件
      • ②、终止条件
      • ③、交换一个字节(模式0)
      • ④、交换一个字节(模式1)
      • ⑤、交换一个字节(模式2)
      • ⑥、交换一个字节(模式3)
    • Ⅲ、SPI时序
      • ①、发送指令
      • ②、指定地址写
      • ③、指定地址读
    • Ⅳ、W25Q64 - Nor Flash(闪存)
      • ①、硬件电路
      • ②、W25Q64框图
      • ③、Flash操作注意事项
      • ④、指令表
      • ⑤、电气特性表
      • ⑥、软件模拟SPI
        • 1>W25Q64.c
        • 2>W25Q64.h
        • 3>W25Q64_Ins.h
    • Ⅴ、硬件SPI(SPI外设)
      • 1、SPI外设简介
      • 2、SPI框图
      • 3、SPI基本结构
      • 4、主模式全双工连续传输
      • 5、非连续传输
    • Ⅵ、配置SPI外设
      • 1、SPI函数
      • 2、SPI_InitTypeDef结构体参数
        • ①、SPI_Mode
        • ②、SPI_Direction
        • ③、SPI_DataSize
        • ④、SPI_FirstBit
        • ⑤、SPI_BaudRatePrescaler
        • ⑥、SPI_CPOL
        • ⑦、SPI_CPHA
        • ⑧、SPI_NSS
        • ⑨、SPI_CRCPolynomial
      • 3、SPI的四种工作模式
      • 4、SPI外设
        • HardSPI.c

SPI通信

Ⅰ、SPI通信概述

SPISerial Peripheral Interface,串行外设接口)是一种高速、全双工、同步的通信总线,广泛应用于嵌入式系统与外围设备间的短距离通信。SPI由摩托罗拉公司在20世纪80年代中期开发,后逐渐发展成为行业规范。它采用主从模式,通常由一个主设备(Master)和一个或多个从设备(Slave)组成。主设备负责控制通信过程,包括时钟信号的生成、从设备的选择以及数据的发送与接收

1、SPI技术规格

  • SPI四根通信线
    • SCK(Serial Clock):时钟信号线,由主设备产生,用于同步数据传输
    • MOSI(Master Output, Slave Input):主设备输出、从设备输入的数据线
    • MISO(Master Input, Slave Output):主设备输入、从设备输出的数据线
    • CS/SS(Chip Select/Slave Select):从设备选择信号线,用于主设备选择与其通信的从设备(默认1
  • 工作模式:SPI有四种工作模式,由CPOL(时钟极性)和CPHA(时钟相位)定义。这决定了数据采样和发送的时钟边沿
  • 数据帧格式:通常为8位或16位,可以配置为MSB(高位在前)或LSB(低位在前)。
  • 传输速率:SPI支持较高的数据传输速率,通常能达到甚至超过10M/bps
  • 通信特点:全双工通信,可以同时发送和接收数据;同步通信,使用时钟信号来同步数据传输;连接简单,硬件结构简单
2、SPI应用
  • 存储器芯片:如EEPROM、SRAM、SPI Flash等,用于数据的高速读写和存储
  • 传感器:用于与各种传感器进行通信,获取传感器数据
  • 显示器:如液晶显示器和OLED显示器,传输图像数据和控制信号
  • 通信模块:如无线模块等,用于数据传输
  • 其他外设:如ADC、DAC等

3、硬件电路

image-20250108163022842

  • 所有SPI设备的SCK、MOSI、MISO分别连在一起

  • 主机另外引出多条SS控制线,分别接到各从机的SS引脚

  • 输出引脚配置为推挽输出,输入引脚配置为浮空或上拉输入

移位示意图

image-20250108170335243

Ⅱ、SPI时序基本单元

①、起始条件

SS从高电平切换到低电平

image-20250108170847890

②、终止条件

SS从低电平切换到高电平

image-20250108170914447

③、交换一个字节(模式0)

  • CPOL(时钟极性)=0:空闲状态时,SCK为低电平
  • CPHA(时钟相位)=0:SCK第一个边沿移入数据,第二个边沿移出数据
    • 由于SCK第一个边沿就要移入数据,因此在SCK之前,就要先将数据移出

image-20250108171658293

④、交换一个字节(模式1)

CPOL(时钟极性)=0:空闲状态时,SCK为低电平

CPHA(时钟相位)=1:SCK第一个边沿移出数据,第二个边沿移入数据

    • SCK上升沿时,MOSI和MISO都将数据移至数据寄存器中
    • 直到SCK下降沿时,
      • 主机移入数据寄存器中的最高位数据“B7”移入从机的最低位;
      • 从机移入数据寄存器中的最高位数据“B7”移入主机的最低位,从而完成数据交换

image-20250108171827304

⑤、交换一个字节(模式2)

CPOL(时钟极性)=1:空闲状态时,SCK为高电平

CPHA(时钟相位)=0:SCK第一个边沿移入数据,第二个边沿移出数据

image-20250108171910609

⑥、交换一个字节(模式3)

CPOL(时钟极性)=1:空闲状态时,SCK为高电平

CPHA(时钟相位)=1:SCK第一个边沿移出数据,第二个边沿移入数据

image-20250108171941946

Ⅲ、SPI时序

①、发送指令

  • 向SS指定的设备,发送指令(0x06)

image-20250108174857750

②、指定地址写

  • 向SS指定的设备,发送写指令(0x02),随后在指定地址(Address[23:0])下,写入指定数据(Data

image-20250110110128634

③、指定地址读

  • 向SS指定的设备,发送读指令(0x03),随后在指定地址(Address[23:0])下,读取从机数据(Data

image-20250110110155463

Ⅳ、W25Q64 - Nor Flash(闪存)

image-20250108175316341

①、硬件电路

image-20250109111414536

②、W25Q64框图

image-20250109112639477

  • W25Q64闪存芯片中有8Mbyte闪存空间,这些空间被划分为128Block 0~Block 127),每个块占有64kb

  • 每个块又被分成16扇区Sector 0~Sector 15),每个扇区占有4kb

  • 扇区又可以划分成16个,每页占有256byte

请添加图片描述

③、Flash操作注意事项

  • 写入操作时:

    • 写入操作前,必须先进行写使能
    • 每个数据位只能由1改写为0,不能由0改写为1
    • 写入数据前必须先擦除,擦除后,所有数据位变为1
    • 擦除必须按最小擦除单元进行(擦除所有、擦除块、擦除扇区)
    • 连续写入多字节时,最多写入一页的数据,超过页尾位置的数据,会回到页首覆盖写入
    • 写入操作结束后,芯片进入忙状态,不响应新的读写操作
  • 读取操作时:

    • 直接调用读取时序,无需使能,无需额外操作,没有页的限制,读取操作结束后不会进入忙状态,但不能在忙状态时读取

④、指令表

image-20250109152616615

⑤、电气特性表

image-20250109154933784

⑥、软件模拟SPI

#include "stm32f10x.h"                  // Device header
#include "MySPI.h"
//模拟SPI
//输出:>SS:	PA4
//输出:>SCK:	PA5
//输出:>MOSI:	PA7
//输入:>MISO:	PA6void MySPI_W_SS(uint8_t BitValue) {GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}
void MySPI_W_SCK(uint8_t BitValue) {GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);
}
void MySPI_W_MOSI(uint8_t BitValue) {GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);
}
uint8_t MySPI_R_MISO(void) {return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}void MySPI_Init(void)
{//初始化GPIORCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_Init(GPIOA, &GPIO_InitStructure);//配置默认电平MySPI_W_SS(1);MySPI_W_SCK(0);//使用SPI模式0
}//起始条件-----SS从高电平切换到低电平
void MySPI_Start(void)
{MySPI_W_SS(0);
}//终止条件-----SS从低电平切换到高电平
void MySPI_Stop(void)
{MySPI_W_SS(1);
}//交换一个字节(模式0)
uint8_t MySPI_SwapByte_Mod0(uint8_t ByteSend)
{uint8_t	ByteReceive = 0;uint8_t	i = 0;for(i = 0;i < 8;i++){MySPI_W_MOSI(ByteSend & (0x80 >> i));MySPI_W_SCK(1);if(MySPI_R_MISO() == 1) {ByteReceive |= 0x80 >> i;}MySPI_W_SCK(0);}return ByteReceive;
}
方法2:使用移位模型
//uint8_t MySPI_SwapByte_Mod0(uint8_t ByteSend)
//{
//	uint8_t	ByteReceive = 0;
//	uint8_t	i = 0;
//	for(i = 0;i < 8;i++)
//	{
//		MySPI_W_MOSI(ByteSend & 0x80);
//		ByteSend <<= 1
//		MySPI_W_SCK(1);
//		if(MySPI_R_MISO() == 1) {ByteSend |= 0x01;}
//		MySPI_W_SCK(0);
//	}
//	return ByteSend;
//}//交换一个字节(模式1)
uint8_t MySPI_SwapByte_Mod1(uint8_t ByteSend)
{uint8_t	ByteReceive = 0;uint8_t	i = 0;for(i = 0;i < 8;i++){MySPI_W_SCK(1);MySPI_W_MOSI(ByteSend & (0x80 >> i));MySPI_W_SCK(0);if(MySPI_R_MISO() == 1) {ByteReceive |= 0x80 >> i;}}return ByteReceive;
}
1>W25Q64.c
#include "stm32f10x.h"                  // Device header
#include "MySPI.h"
#include "W25Q64.h"
#include "W25Q64_Ins.h"void W25Q64_Init(void)
{MySPI_Init();
}//读取ID
void W25Q64_ReadID(W25Q64_ID_TypeDef* ID)
{MySPI_Start();MySPI_SwapByte_Mod0(W25Q64_JEDEC_ID);ID->Manufacturer_ID = MySPI_SwapByte_Mod0(W25Q64_Dummy_BYTE);ID->Device_ID = MySPI_SwapByte_Mod0(W25Q64_Dummy_BYTE);ID->Device_ID <<= 8;ID->Device_ID |= MySPI_SwapByte_Mod0(W25Q64_Dummy_BYTE);MySPI_Stop();
}//W25Q64写使能
void W25Q64_W_Enable(void)
{MySPI_Start();MySPI_SwapByte_Mod0(W25Q64_WRITE_ENABLE);MySPI_Stop();
}//W25Q64读状态寄存器1(判断是否处于忙状态)
void W25Q64_WaitBusy(void)
{uint32_t Timeout = 100000;MySPI_Start();MySPI_SwapByte_Mod0(W25Q64_READ_STATUS_REGISTER_1);while((MySPI_SwapByte_Mod0(W25Q64_Dummy_BYTE) & 0x01) == 1){Timeout--;if(Timeout == 0){break;//超时退出}}MySPI_Stop();
}/*************************************************************************************** 名称				W25Q64_PageProgram* 功能				W25Q64页编程* 参数Addr			写入数据的地址* 参数DataArray	存放写入数据的数组* 参数Count		写入的数据数量*/
//Count <= 256  (256字节页面缓冲区)
void W25Q64_PageProgram(uint32_t Addr, uint8_t* DataArray, uint16_t Count)
{W25Q64_W_Enable();//写使能MySPI_Start();MySPI_SwapByte_Mod0(W25Q64_PAGE_PROGRAM);//写入页编程指令MySPI_SwapByte_Mod0(Addr >> 16);//A23~A16MySPI_SwapByte_Mod0(Addr >> 8);	//A15~A8MySPI_SwapByte_Mod0(Addr);		//A7~A0	uint16_t i = 0;for(i = 0;i < Count;i++){MySPI_SwapByte_Mod0(DataArray[i]);}MySPI_Stop();W25Q64_WaitBusy();//等待忙状态
}//W25Q64扇区擦除(4KB)
void W25Q64_SectorErase(uint32_t Addr)
{W25Q64_W_Enable();//写使能MySPI_Start();MySPI_SwapByte_Mod0(W25Q64_SECTOR_ERASE_4KB);//写入扇区擦除(4KB)指令MySPI_SwapByte_Mod0(Addr >> 16);//A23~A16MySPI_SwapByte_Mod0(Addr >> 8);	//A15~A8MySPI_SwapByte_Mod0(Addr);		//A7~A0	MySPI_Stop();W25Q64_WaitBusy();//等待忙状态
}/*************************************************************************************** 名称				W25Q64_ReadData* 功能				W25Q64读取数据* 参数Addr			读取数据的地址* 参数DataArray	存放读取数据的数组* 参数Count		读取的数据数量* 返回值			无*/
void W25Q64_ReadData(uint32_t Addr, uint8_t* DataArray, uint32_t Count)
{uint32_t i;MySPI_Start();MySPI_SwapByte_Mod0(W25Q64_READ_DATA);//写入读取数据指令MySPI_SwapByte_Mod0(Addr >> 16);//A23~A16MySPI_SwapByte_Mod0(Addr >> 8);	//A15~A8MySPI_SwapByte_Mod0(Addr);		//A7~A0for(i = 0;i < Count;i++){	DataArray[i] = MySPI_SwapByte_Mod0(W25Q64_Dummy_BYTE);}MySPI_Stop();
}
2>W25Q64.h
#ifndef __W25Q64_H__
#define __W25Q64_H__
#include "stdint.h"typedef struct
{uint8_t Manufacturer_ID;//厂商IDuint16_t Device_ID;//设备ID
}W25Q64_ID_TypeDef;void W25Q64_Init(void);
void W25Q64_ReadID(W25Q64_ID_TypeDef* ID);//读取ID
void W25Q64_PageProgram(uint32_t Addr, uint8_t* DataArray, uint16_t Count);//页编程
void W25Q64_SectorErase(uint32_t Addr);//W25Q64扇区擦除(4KB)
void W25Q64_ReadData(uint32_t Addr, uint8_t* DataArray, uint32_t Count);//读取数据#endif
3>W25Q64_Ins.h
#ifndef __W25Q64_INS_H
#define __W25Q64_INS_H#define W25Q64_WRITE_ENABLE							0x06	//写入使能
#define W25Q64_WRITE_DISABLE						0x04	//写入失能
#define W25Q64_READ_STATUS_REGISTER_1				0x05	//读取状态寄存器-1
#define W25Q64_READ_STATUS_REGISTER_2				0x35	//读取状态寄存器-2
#define W25Q64_WRITE_STATUS_REGISTER				0x01	//写入状态寄存器
#define W25Q64_PAGE_PROGRAM							0x02	//页面编程
#define W25Q64_QUAD_PAGE_PROGRAM					0x32	//四页面编程
#define W25Q64_BLOCK_ERASE_64KB						0xD8	//块擦除(64KB)
#define W25Q64_BLOCK_ERASE_32KB						0x52	//块擦除(32KB)
#define W25Q64_SECTOR_ERASE_4KB						0x20	//扇区擦除(4KB)
#define W25Q64_CHIP_ERASE							0xC7	//芯片擦除
#define W25Q64_ERASE_SUSPEND						0x75	//擦除暂停
#define W25Q64_ERASE_RESUME							0x7A	//擦除恢复
#define W25Q64_POWER_DOWN							0xB9	//掉电
#define W25Q64_HIGH_PERFORMANCE_MODE				0xA3	//高性能模式
#define W25Q64_CONTINUOUS_READ_MODE_RESET			0xFF	//连续读取模式重置
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID		0xAB	//退出掉电或高性能模式/设备ID
#define W25Q64_MANUFACTURER_DEVICE_ID				0x90	//制造商/设备ID
#define W25Q64_READ_UNIQUE_ID						0x4B	//读取唯一ID
#define W25Q64_JEDEC_ID								0x9F	//JEDEC ID
#define W25Q64_READ_DATA							0x03	//读取数据
#define W25Q64_FAST_READ							0x0B	//快速读取
#define W25Q64_FAST_READ_DUAL_OUTPUT				0x3B	//快速读取双输出
#define W25Q64_FAST_READ_DUAL_IO					0xBB	//快速读取双输入/输出
#define W25Q64_FAST_READ_QUAD_OUTPUT				0x6B	//快速读取四输出
#define W25Q64_FAST_READ_QUAD_IO					0xEB	//快速读取四输入/输出
#define W25Q64_OCTAL_WORD_READ_QUAD_IO				0xE3	//八进制字读取四输入/输出#define W25Q64_Dummy_BYTE							0xFF	//无用数据#endif

Ⅴ、硬件SPI(SPI外设)

1、SPI外设简介

  • STM32内部集成了硬件SPI收发电路,可以由硬件自动执行时钟生成、数据收发等功能,减轻CPU的负担

  • 可配置8位/16位数据帧高位先行/低位先行

  • 时钟频率:

    • 时钟频率 = f P C L K 2 , 4 , 8 , 16 , 32 , 64 , 128 , 256 = 总线时钟频率 预分频系数 时钟频率=\frac{f_{PCLK}}{2, 4, 8, 16, 32, 64, 128, 256} =\frac{总线时钟频率}{预分频系数} 时钟频率=2,4,8,16,32,64,128,256fPCLK=预分频系数总线时钟频率
  • 支持多主机模型、主或从操作

  • 可精简为半双工/单工通信

  • 支持DMA

  • 兼容I2S协议(数字音频信号传输协议)

  • STM32F103C8T6 硬件SPI资源:SPI1挂载在APB2上,PCLK为72MHz)、SPI2挂载在APB1上,PCLK为36MHz

2、SPI框图

图中所示的是低位先行

image-20250111145559624

  • 发送缓冲区的数据移至移位寄存器中时,会置TXE发送寄存器空)标志位
  • 当移位寄存器中的数据移动至接收缓冲区时,会置RXNE接收寄存器非空)标志位

3、SPI基本结构

image-20250111191525365

4、主模式全双工连续传输

image-20250111191911988

5、非连续传输

image-20250111191919123

Ⅵ、配置SPI外设

1、SPI函数

// 重置指定的SPI/I2S为默认值
void SPI_I2S_DeInit(SPI_TypeDef* SPIx);// 初始化指定的SPI,根据初始化结构体配置参数
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);// 初始化指定的I2S,根据初始化结构体配置参数
void I2S_Init(SPI_TypeDef* SPIx, I2S_InitTypeDef* I2S_InitStruct);// 初始化SPI初始化结构体的默认值
void SPI_StructInit(SPI_InitTypeDef* SPI_InitStruct);// 初始化I2S初始化结构体的默认值
void I2S_StructInit(I2S_InitTypeDef* I2S_InitStruct);// 开启或关闭指定的SPI
void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);// 开启或关闭指定的I2S
void I2S_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);// 开启或关闭SPI/I2S的中断
void SPI_I2S_ITConfig(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT, FunctionalState NewState);// 开启或关闭SPI/I2S的DMA请求
void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState);// 通过SPI/I2S发送数据(写DR数据寄存器)
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
// 通过SPI/I2S接收数据(读DR数据寄存器)
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);// 配置SPI的内部NSS软件管理
void SPI_NSSInternalSoftwareConfig(SPI_TypeDef* SPIx, uint16_t SPI_NSSInternalSoft);// 开启或关闭SPI的SS输出
void SPI_SSOutputCmd(SPI_TypeDef* SPIx, FunctionalState NewState);// 配置SPI的数据大小
void SPI_DataSizeConfig(SPI_TypeDef* SPIx, uint16_t SPI_DataSize);// 通过SPI发送CRC值
void SPI_TransmitCRC(SPI_TypeDef* SPIx);// 开启或关闭SPI的CRC计算
void SPI_CalculateCRC(SPI_TypeDef* SPIx, FunctionalState NewState);// 获取SPI的CRC值
uint16_t SPI_GetCRC(SPI_TypeDef* SPIx, uint8_t SPI_CRC);// 获取SPI的CRC多项式
uint16_t SPI_GetCRCPolynomial(SPI_TypeDef* SPIx);
// 配置SPI的双向线模式
void SPI_BiDirectionalLineConfig(SPI_TypeDef* SPIx, uint16_t SPI_Direction);// 获取SPI/I2S标志状态
FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
// 清除SPI/I2S标志
void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);// 获取SPI/I2S中断状态
ITStatus SPI_I2S_GetITStatus(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);
// 清除SPI/I2S中断待处理位
void SPI_I2S_ClearITPendingBit(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);

2、SPI_InitTypeDef结构体参数

①、SPI_Mode

指定SPI的工作模式

  • 该参数可以是@ref SPI_mode的值

  • @ref SPI_mode

    • 宏定义解释

      1. SPI_Mode_Master
        • 描述:定义了SPI主模式。在这种模式下,SPI设备作为主设备,负责控制时钟信号(SCLK),并发起数据传输。主设备通常用于控制从设备,如传感器、存储器等
      2. SPI_Mode_Slave
        • 描述:定义了SPI从模式。在这种模式下,SPI设备作为从设备,响应主设备的时钟信号(SCLK),并根据主设备的指令进行数据传输。从设备通常用于提供数据或接收数据,如传感器、存储器等

      宏函数

      1. IS_SPI_MODE(MODE)
        • 描述:这是一个宏函数,用于检查给定的工作模式设置是否有效
        • 参数MODE,代表SPI的工作模式设置
        • 功能:检查MODE是否等于SPI_Mode_MasterSPI_Mode_Slave中的任一个
        • 返回值:如果MODE有效,返回1(真),否则返回0(假)

      表格:

      宏定义描述
      SPI_Mode_Master0x0104SPI主模式
      SPI_Mode_Slave0x0000SPI从模式
      宏函数描述
      IS_SPI_MODE(MODE)检查MODE是否为有效的SPI工作模式设置
②、SPI_Direction

指定SPI的单向或双向数据模式

  • 该参数可以是@ref SPI_data_direction

    • 宏定义解释

      1. SPI_Direction_2Lines_FullDuplex
        • 描述:定义了SPI的全双工模式,使用两条线进行数据传输。在这种模式下,SPI设备可以同时发送和接收数据
      2. SPI_Direction_2Lines_RxOnly
        • 描述:定义了SPI的仅接收模式,使用两条线进行数据传输。在这种模式下,SPI设备仅接收数据,不发送数据
      3. SPI_Direction_1Line_Rx
        • 描述:定义了SPI的单线接收模式。在这种模式下,SPI设备使用一条线进行数据接收
      4. SPI_Direction_1Line_Tx
        • 描述:定义了SPI的单线发送模式。在这种模式下,SPI设备使用一条线进行数据发送

      宏函数

      1. IS_SPI_DIRECTION_MODE(MODE)
        • 描述:这是一个宏函数,用于检查给定的数据方向设置是否有效
        • 参数MODE,代表SPI的数据方向设置
        • 功能:检查MODE是否等于SPI_Direction_2Lines_FullDuplexSPI_Direction_2Lines_RxOnlySPI_Direction_1Line_RxSPI_Direction_1Line_Tx中的任一个
        • 返回值:如果MODE有效,返回1(真),否则返回0(假)

      表格:

      宏定义描述
      SPI_Direction_2Lines_FullDuplex0x0000全双工模式,使用两条线
      SPI_Direction_2Lines_RxOnly0x0400仅接收模式,使用两条线
      SPI_Direction_1Line_Rx0x8000单线接收模式
      SPI_Direction_1Line_Tx0xC000单线发送模式
      宏函数描述
      IS_SPI_DIRECTION_MODE(MODE)检查MODE是否为有效的SPI数据方向设置
③、SPI_DataSize

指定SPI数据大小

  • 该参数可以是@ref SPI_data_size

    • 宏定义解释

      1. SPI_DataSize_16b
        • 描述:定义了SPI的数据大小为16位。在这种模式下,每次数据传输的大小为16位(2字节)
      2. SPI_DataSize_8b
        • 描述:定义了SPI的数据大小为8位。在这种模式下,每次数据传输的大小为8位(1字节)

      宏函数

      1. IS_SPI_DATASIZE(DATASIZE)
        • 描述:这是一个宏函数,用于检查给定的数据大小设置是否有效
        • 参数DATASIZE,代表SPI的数据大小设置
        • 功能:检查DATASIZE是否等于SPI_DataSize_16bSPI_DataSize_8b中的任一个
        • 返回值:如果DATASIZE有效,返回1(真),否则返回0(假)

      表格:

      宏定义描述
      SPI_DataSize_16b0x080016位数据大小
      SPI_DataSize_8b0x00008位数据大小
      宏函数描述
      IS_SPI_DATASIZE(DATASIZE)检查DATASIZE是否为有效的SPI数据大小设置
④、SPI_FirstBit

指定数据传输是MSB(高位先行)还是LSB(低位先行)

  • 该参数可以是@ref SPI_MSB_LSB_transmission

    • 宏定义解释

      1. SPI_FirstBit_MSB
        • 描述:定义了SPI的数据传输顺序为先传输最高位(MSB)高位先行。在这种模式下,数据的最高位(最左边的位)先被发送和接收。这是最常见的数据传输顺序,适用于大多数SPI设备
      2. SPI_FirstBit_LSB
        • 描述:定义了SPI的数据传输顺序为先传输最低位(LSB)低位先行。在这种模式下,数据的最低位(最右边的位)先被发送和接收。这种配置在某些特定的应用场景中可能有用,例如在处理某些特殊的编码或数据格式时

      宏函数

      1. IS_SPI_FIRST_BIT(BIT)
        • 描述:这是一个宏函数,用于检查给定的数据传输顺序设置是否有效
        • 参数BIT,代表SPI的数据传输顺序设置
        • 功能:检查BIT是否等于SPI_FirstBit_MSBSPI_FirstBit_LSB中的任一个
        • 返回值:如果BIT有效,返回1(真),否则返回0(假)

      表格:

      宏定义描述
      SPI_FirstBit_MSB0x0000先传输最高位(MSB)高位先行
      SPI_FirstBit_LSB0x0080先传输最低位(LSB)低位先行
      宏函数描述
      IS_SPI_FIRST_BIT(BIT)检查BIT是否为有效的SPI数据传输顺序设置
⑤、SPI_BaudRatePrescaler

指定波特率预算器值,该值将为用于配置发送和接收SCK时钟

  • 通信时钟来源于主时钟。不需要设置从时钟

  • 该参数可以是@ref spi_baudrate_precaler

    • 宏定义解释

      1. SPI_BaudRatePrescaler_2
        • 描述:定义了SPI的波特率预分频器为2。这意味着SPI时钟频率是系统时钟频率的1/2
      2. SPI_BaudRatePrescaler_4
        • 描述:定义了SPI的波特率预分频器为4。这意味着SPI时钟频率是系统时钟频率的1/4
      3. … …

      宏函数

      1. IS_SPI_BAUDRATE_PRESCALER(PRESCALER)
        • 描述:这是一个宏函数,用于检查给定的波特率预分频器设置是否有效
        • 参数PRESCALER,代表SPI的波特率预分频器设置
        • 功能:检查PRESCALER是否等于预定义的波特率预分频器值中的任一个
        • 返回值:如果PRESCALER有效,返回1(真),否则返回0(假)

      表格:

      宏定义描述
      SPI_BaudRatePrescaler_20x0000预分频器为2
      SPI_BaudRatePrescaler_40x0008预分频器为4
      SPI_BaudRatePrescaler_80x0010预分频器为8
      SPI_BaudRatePrescaler_160x0018预分频器为16
      SPI_BaudRatePrescaler_320x0020预分频器为32
      SPI_BaudRatePrescaler_640x0028预分频器为64
      SPI_BaudRatePrescaler_1280x0030预分频器为128
      SPI_BaudRatePrescaler_2560x0038预分频器为256
      宏函数描述
      IS_SPI_BAUDRATE_PRESCALER(PRESCALER)检查PRESCALER是否为有效的SPI波特率预分频器设置
⑥、SPI_CPOL

指定时钟极性(Clock Polarity)配置决定了SPI时钟线(SCLK)的默认电平

  • 该参数可以是@ref SPI_Clock_Polarity

    • 宏定义解释

      1. SPI_CPOL_Low
        • 描述定义了SPI的时钟极性为低电平。在这种配置下,SPI时钟线(SCLK)的默认电平为低电平(0)。数据在时钟从低到高的边沿(上升沿)被采样,在从高到低的边沿(下降沿)被发送
      2. SPI_CPOL_High
        • 描述定义了SPI的时钟极性为高电平。在这种配置下,SPI时钟线(SCLK)的默认电平为高电平(1)。数据在时钟从高到低的边沿(下降沿)被采样,在从低到高的边沿(上升沿)被发送

      宏函数

      1. IS_SPI_CPOL(CPOL)
        • 描述:这是一个宏函数,用于检查给定的时钟极性设置是否有效
        • 参数CPOL,代表SPI的时钟极性设置
        • 功能:检查CPOL是否等于SPI_CPOL_LowSPI_CPOL_High中的任一个
        • 返回值:如果CPOL有效,返回1(真),否则返回0(假)

      表格:

      宏定义描述
      SPI_CPOL_Low0x0000时钟极性为低电平
      SPI_CPOL_High0x0002时钟极性为高电平
      宏函数描述
      IS_SPI_CPOL(CPOL)检查CPOL是否为有效的SPI时钟极性设置
⑦、SPI_CPHA

定义了SPI的时钟相位(Clock Phase)配置决定了数据在时钟的哪个边沿被采样和发送

时钟相位CPHA):

  • CPHA = 0:数据在时钟的第一个边沿被采样,在第二个边沿被发送
  • CPHA = 1:数据在时钟的第二个边沿被采样,在第一个边沿被发送
  • 该参数可以是@ref SPI_Clock_Phase

    • 宏定义解释

      1. SPI_CPHA_1Edge
        • 描述0定义了SPI的时钟相位为第一个时钟边沿。在这种配置下,数据在时钟的第一个边沿(上升沿或下降沿,取决于时钟极性)被采样,在第二个边沿被发送
      2. SPI_CPHA_2Edge
        • 描述1定义了SPI的时钟相位为第二个时钟边沿。在这种配置下,数据在时钟的第二个边沿被采样,在第一个边沿被发送

      宏函数

      1. IS_SPI_CPHA(CPHA)
        • 描述:这是一个宏函数,用于检查给定的时钟相位设置是否有效
        • 参数CPHA,代表SPI的时钟相位设置
        • 功能:检查CPHA是否等于SPI_CPHA_1EdgeSPI_CPHA_2Edge中的任一个
        • 返回值:如果CPHA有效,返回1(真),否则返回0(假)

      表格:

      宏定义描述
      SPI_CPHA_1Edge0x0000数据在第一个时钟边沿被采样
      SPI_CPHA_2Edge0x0001数据在第二个时钟边沿被采样
      宏函数描述
      IS_SPI_CPHA(CPHA)检查CPHA是否为有效的SPI时钟相位设置
⑧、SPI_NSS

从设备选择(Slave Select,NSS)管理配置决定了SPI从设备的NSS信号是由硬件控制还是软件控制

  • 该参数可以是@ref SPI_Slave_Select_management

    • 宏定义解释

      1. SPI_NSS_Soft
        • 描述:定义了SPI的NSS信号由软件管理
          • 在这种模式下,NSS信号由软件控制,通常在每次数据传输前后手动设置和清除。这适用于需要灵活控制NSS信号的场景,例如在多从设备系统中,主设备需要精确控制每个从设备的NSS信号
      2. SPI_NSS_Hard
        • 描述:定义了SPI的NSS信号由硬件管理
          • 在这种模式下,NSS信号由硬件自动控制,通常在数据传输开始时自动置低,在数据传输结束时自动置高。这适用于简单的SPI通信场景,减少了软件控制的复杂性

      宏函数

      1. IS_SPI_NSS(NSS)
        • 描述:这是一个宏函数,用于检查给定的从设备选择管理设置是否有效
        • 参数NSS,代表SPI的从设备选择管理设置
        • 功能:检查NSS是否等于SPI_NSS_SoftSPI_NSS_Hard中的任一个
        • 返回值:如果NSS有效,返回1(真),否则返回0(假)

      表格:

      宏定义描述
      SPI_NSS_Soft0x0200NSS信号由软件管理
      SPI_NSS_Hard0x0000NSS信号由硬件管理
      宏函数描述
      IS_SPI_NSS(NSS)检查NSS是否为有效的SPI从设备选择管理设置
⑨、SPI_CRCPolynomial

CRC(Cyclic Redundancy Check,循环冗余校验)多项式配置

  • 复位值为0x0007,根据应用可以设置其它数值

    • 注:在I2S模式下不使用
  • CRC校验

    • CRC校验是一种常用的错误检测方法,通过在数据传输前后计算CRC值,可以检测数据在传输过程中是否发生了错误
    • CRC多项式是一个特定的多项式,用于生成CRC校验码。不同的多项式会产生不同的CRC校验码,因此选择合适的多项式非常重要

3、SPI的四种工作模式

模式CPOL(时钟极性)CPHA(时钟相位)数据采样时刻
模式000在时钟的上升沿采样数据
模式101在时钟的下降沿采样数据
模式210在时钟的下降沿采样数据
模式311在时钟的上升沿采样数据

4、SPI外设

HardSPI.c
#include "stm32f10x.h"                  // Device header
#include "HardSPI.h"
//硬件SPI
//输出:>SS:		PA4		由软件控制
//输出:>SCK:	PA5		由硬件控制
//输出:>MOSI:	PA7		由硬件控制
//输入:>MISO:	PA6		由硬件控制void HardSPI_W_SS(uint8_t BitValue) {GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}void HardSPI_Init(void)
{//初始化GPIORCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;//将SS(软件控制)配置为通用推挽输出GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStruct);GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;//将输出口配置为复用推挽输出GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStruct);GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;//将输入口配置为上拉输入GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStruct);//配置SPISPI_InitTypeDef SPI_InitStruct;SPI_InitStruct.SPI_Mode = SPI_Mode_Master;//主从模式选择SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//选择全双工SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;//设置8位数据大小SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;//设置为高位先行SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;//设置分频系数为128SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;//0SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;//0	CPOL=0;CPHA=0时为模式0SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;//配置SS由软件管理SPI_InitStruct.SPI_CRCPolynomial = 0x0007;//不使用CRC校验,复位值为0x0007SPI_Init(SPI1, &SPI_InitStruct);SPI_Cmd(SPI1,ENABLE);//使能SPI外设HardSPI_W_SS(1);//配置默认电平
}//起始条件-----SS从高电平切换到低电平
void HardSPI_Start(void) {HardSPI_W_SS(0);
}//终止条件-----SS从低电平切换到高电平
void HardSPI_Stop(void) {HardSPI_W_SS(1);
}//交换一个字节(模式0)
uint8_t HardSPI_SwapByte_Mod0(uint8_t ByteSend)
{while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == !SET);//当发送数据寄存器为空标志位(TXE)== 1时,才跳出循环SPI_I2S_SendData(SPI1, ByteSend);//发送数据(写入DR时会自动清除TXE标志位)while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == !SET);//接收缓冲区接收到数据时跳出循环return  SPI_I2S_ReceiveData(SPI1);//接收数据(读DR时会自动清除RXNE标志位)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/1256.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小米vela系统(基于开源nuttx内核)——如何使用信号量进行PV操作

如何使用信号量进行PV操作 前言信号量1. 信号量简介2. NuttX中信号量的创建与使用2.1 Nuttx信号量的初始化和销毁2.2 信号量的等待和发布 3. 信号量的实际应用&#xff1a;下载任务示例3.1 实际代码3.2 代码说明3.3 执行说明 4. 信号量的优势与应用场景5. 常见应用场景&#xf…

MySQL Binlog 同步工具go-mysql-transfer Lua模块使用说明

一、go-mysql-transfer go-mysql-transfer是一款MySQL实时、增量数据同步工具。能够实时解析MySQL二进制日志binlog&#xff0c;并生成指定格式的消息&#xff0c;同步到接收端。 go-mysql-transfer具有如下特点&#xff1a; 1、不依赖其它组件&#xff0c;一键部署 2、集成多种…

灌区闸门自动化控制系统-精准渠道量测水-灌区现代化建设

项目背景 本项目聚焦于黑龙江某一灌区的现代化改造工程&#xff0c;该灌区覆盖广阔&#xff0c;灌溉面积高达7.5万亩&#xff0c;地域上跨越6个乡镇及涵盖17个村庄。项目核心在于通过全面的信息化建设&#xff0c;强力推动节水灌溉措施的实施&#xff0c;旨在显著提升农业用水的…

vue2修改表单只提交被修改的数据的字段传给后端接口

效果&#xff1a; 步骤一、 vue2修改表单提交的时候&#xff0c;只将修改的数据的字段传给后端接口&#xff0c;没有修改得数据不传参给接口。 在 data 对象中添加一个新的属性&#xff0c;用于存储初始表单数据的副本&#xff0c;与当前表单数据进行比较&#xff0c;找出哪些…

LiveNVR监控流媒体Onvif/RTSP常见问题-二次开发接口jquery调用示例如何解决JS|axios调用接口时遇到的跨域问题

LiveNVR二次开发接口jquery调用示例如何解决JS|axios调用接口时遇到的跨域问题 1、接口调用示例2、JS调用遇到跨域解决示例3、axios请求接口遇到跨域问题3.1、post请求3.2、get请求 4、RTSP/HLS/FLV/RTMP拉流Onvif流媒体服务 1、接口调用示例 下面是完整的 jquery 调用示例 $.a…

RTDETR融合[WACV 2024]的MetaSeg中的gmb模块

RT-DETR使用教程&#xff1a; RT-DETR使用教程 RT-DETR改进汇总贴&#xff1a;RT-DETR更新汇总贴 《MetaSeg: MetaFormer-based Global Contexts-aware Network for Efficient Semantic Segmentation》 一、 模块介绍 论文链接&#xff1a;https://arxiv.org/abs/2408.07576 代…

TensorFlow Quantum快速编程(基本篇)

一、TensorFlow Quantum 概述 1.1 简介 TensorFlow Quantum(TFQ)是由 Google 开发的一款具有开创性意义的开源库,它宛如一座桥梁,巧妙地将量子计算与 TensorFlow 强大的机器学习功能紧密融合。在当今科技飞速发展的时代,传统机器学习虽已取得诸多瞩目成就,然而面对日益…

Spring Boot 2 学习全攻略

Spring Boot 2 学习资料 Spring Boot 2 学习资料 Spring Boot 2 学习资料 在当今快速发展的 Java 后端开发领域&#xff0c;Spring Boot 2 已然成为一股不可忽视的强大力量。它简化了 Spring 应用的初始搭建以及开发过程&#xff0c;让开发者能够更加专注于业务逻辑的实现&am…

深度学习笔记11-优化器对比实验(Tensorflow)

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目录 一、导入数据并检查 二、配置数据集 三、数据可视化 四、构建模型 五、训练模型 六、模型对比评估 七、总结 一、导入数据并检查 import pathlib,…

HBuilderX打包ios保姆式教程

1、登录苹果开发者后台并登录已认证开发者账号ID Sign In - Apple 2、创建标识符&#xff08;App ID&#xff09;、证书&#xff0c;描述文件 3、首先创建标识符&#xff0c;用于新建App应用 3-1、App的话直接选择第一个App IDs&#xff0c;点击右上角继续 3-2、选择App&#x…

计算机网络 (39)TCP的运输连接管理

前言 TCP&#xff08;传输控制协议&#xff09;是一种面向连接的、可靠的传输协议&#xff0c;它在计算机网络中扮演着至关重要的角色。TCP的运输连接管理涉及连接建立、数据传送和连接释放三个阶段。 一、TCP的连接建立 TCP的连接建立采用三次握手机制&#xff0c;其过程如下&…

2 XDMA IP中断

三种中断 1. Legacy 定义&#xff1a;Legacy 中断是传统的中断处理方式&#xff0c;使用物理中断线&#xff08;例如 IRQ&#xff09;来传递中断信号。缺点&#xff1a; 中断线数量有限&#xff0c;通常为 16 条&#xff0c;限制了可连接设备的数量。中断处理可能会导致中断风…

22、PyTorch nn.Conv2d卷积网络使用教程

文章目录 1. 卷积2. python 代码3. notes 1. 卷积 输入A张量为&#xff1a; A [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ] \begin{equation} A\begin{bmatrix} 0&1&2&3\\\\ 4&5&6&7\\\\ 8&9&10&11\\\\ 12&13&14&15 \end{b…

基于微信小程序的汽车销售系统的设计与实现springboot+论文源码调试讲解

第4章 系统设计 一个成功设计的系统在内容上必定是丰富的&#xff0c;在系统外观或系统功能上必定是对用户友好的。所以为了提升系统的价值&#xff0c;吸引更多的访问者访问系统&#xff0c;以及让来访用户可以花费更多时间停留在系统上&#xff0c;则表明该系统设计得比较专…

【ROS2】☆ launch之Python

☆重点 ROS1和ROS2其中一个很大区别之一就是launch的编写方式。在ROS1中采用xml格式编写launch&#xff0c;而ROS2保留了XML 格式launch&#xff0c;还另外引入了Python和YAML 编写方式。选择哪种编写取决于每位开发人员的爱好&#xff0c;但是ROS2官方推荐使用Python方式编写…

Facebook 隐私变革之路:回顾与展望

在数字时代&#xff0c;个人隐私的保护一直是社交平台面临的重大挑战之一。作为全球最大的社交网络平台&#xff0c;Facebook&#xff08;现为Meta&#xff09;在处理用户隐私方面的变革&#xff0c;历经了多次调整与完善。本文将回顾Facebook在隐私保护方面的历程&#xff0c;…

P10打卡——pytorch实现车牌识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 1.检查GPU from torchvision.transforms import transforms from torch.utils.data import DataLoader from torchvision import datasets import torchvisio…

Pycharm连接远程解释器

这里写目录标题 0 前言1 给项目添加解释器2 通过SSH连接3 找到远程服务器的torch环境所对应的python路径&#xff0c;并设置同步映射&#xff08;1&#xff09;配置服务器的系统环境&#xff08;2&#xff09;配置服务器的conda环境 4 进入到程序入口&#xff08;main.py&#…

VS2015 + OpenCV + OnnxRuntime-Cpp + YOLOv8 部署

近期有个工作需求是进行 YOLOv8 模型的 C 部署&#xff0c;部署环境如下 系统&#xff1a;WindowsIDE&#xff1a;VS2015语言&#xff1a;COpenCV 4.5.0OnnxRuntime 1.15.1 0. 预训练模型保存为 .onnx 格式 假设已经有使用 ultralytics 库训练并保存为 .pt 格式的 YOLOv8 模型…

uniApp通过xgplayer(西瓜播放器)接入视频实时监控

&#x1f680; 个人简介&#xff1a;某大型国企资深软件开发工程师&#xff0c;信息系统项目管理师、CSDN优质创作者、阿里云专家博主&#xff0c;华为云云享专家&#xff0c;分享前端后端相关技术与工作常见问题~ &#x1f49f; 作 者&#xff1a;码喽的自我修养&#x1f9…