STM32 SPI接口的简单实现要点
stm32 SPI 接口
stm32 SPI 接口1、SPI 简介SPI 是英语Serial Peripheral interface 的缩写,顾名思义就是串行外围设备接口。
是Motorola 首先在其MC68HCXX 系列处理器上定义的。
SPI 接口主要应用在EEPROM,FLASH,实时时钟,AD 转换器,还有数字信号处理器和数字信号解码器之间。
SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB 的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,STM32 也有SPI 接口。
SPI 接口一般使用4 条线:MISO 主设备数据输入,从设备数据输出。
MOSI 主设备数据输出,从设备数据输入。
SCLK 时钟信号,由主设备产生。
CS 从设备片选信号,由主设备控制。
SPI 主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。
SPI 总线四种工作方式SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。
如果CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。
时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。
如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。
SPI 主模块和与之通信的外设备时钟相位和极性应该一致。
从选择(NSS)脚管理有2 种NSS 模式:图211●软件NSS 模式:可以通过设置SPI_CR1 寄存器的SSM 位来使能这种模式(见)。
在这种模式下NSS 引脚可以用作它用,而内部NSS 信号电平可以通过写SPI_CR1 的SSI 位来驱动●硬件NSS 模式,分两种情况:─NSS 输出被使。
单片机与外部设备的SPI接口设计与实现
单片机与外部设备的SPI接口设计与实现1.引言单片机与外部设备的通信是嵌入式系统设计中的重要环节。
SPI(Serial Peripheral Interface)是一种常用的串行通信协议,用于实现单片机与外设之间的数据传输。
本文将探讨单片机与外部设备之间的SPI接口设计与实现。
2.SPI接口简介SPI接口是一种同步的数据总线协议,通常由一个主设备和一个或多个从设备组成。
SPI接口包含四个信号线:时钟线(SCK)、主输出从输入线(MISO)、主输入从输出线(MOSI)和片选线(SS)。
通过时钟线的同步操作,主设备可以与从设备进行双向数据传输。
3.SPI接口的工作原理SPI接口的工作原理如下:首先,主设备通过片选线选择从设备,并将数据发送到MOSI线上;随后,主设备通过时钟线提供时钟信号,从而同步数据的传输;同时,从设备将数据通过MISO线发送给主设备;最后,主设备将片选线置高,表示数据传输结束。
4.SPI接口的硬件设计在实现SPI接口的硬件设计时,需要考虑以下几个方面:4.1 片选线的设计片选线的数量由从设备的数量决定。
如果只有一个从设备,可以直接连接到片选线上。
如果有多个从设备,需要使用多个片选线,并通过逻辑门进行选择。
4.2 时钟线的设计时钟线的频率由主设备的时钟频率决定。
需要根据从设备的要求,选择适当的时钟频率。
时钟频率过高可能导致数据传输出错,过低可能导致传输速度较慢。
4.3 数据线的设计数据线包括主输出从输入线(MISO)和主输入从输出线(MOSI)。
需要根据从设备的要求,确定数据线的数量和宽度。
通常,每个从设备都需要一个MISO线和一个MOSI线。
5.SPI接口的软件实现在单片机中实现SPI接口的软件需要编写相应的驱动程序。
以下是SPI接口软件实现的基本步骤:5.1 硬件初始化首先,需要初始化单片机的IO口,并设置片选线等相应的引脚。
5.2 时钟设置根据从设备的时钟要求,设置单片机相应的时钟频率。
STM32 SPI初始化和使用
串行外设接口(SPI)。
初始化步骤:1、连接SPI外设时钟,通过RCC->APB2ENR设置。
2、连接被复用的GPIO的外设时钟,也是通过RCC->APB2ENR设置为什么还要连接GPIO时钟,参见STM32参考手册8.1.4节。
手册上这么说的:对于复用输出功能,端口必须配置成复用功能输出模式(推挽或开漏)。
3、设置被复用的GPIO为推挽输出,并设置时钟。
不能设置为开漏输出。
设置成开漏输出时,示波器上看输出是锯齿波,而不是需要的方波。
4、通过配置SPIx->CR1来设置SPI 的工作模式。
最后使能SPI5、收发数据。
收发数据可以使用同一个函数,因为SPI是同步输入输出的,在发送数据的时候已经在接受数据。
配置SPI1代码如下:void SPI1_Init(void){RCC->APB2ENR |= 1<<12;//使能SPI1 时钟RCC->APB2ENR |= 1<<2;//配置服用功能输出GPIOA->CRL&=0X000FFFFF;GPIOA->CRL|=0XBBB00000;//PA5.6.7 复用,推挽输出50M时钟(不能配置成开漏,否则输出为锯齿波)GPIOA->ODR|=0X7<<5;SPI1->CR1|=0<<11;//8bit数据格式SPI1->CR1|=0<<10;//全双工模式SPI1->CR1|=1<<9; //软件nss 管理SPI1->CR1|=1<<8;SPI1->CR1|=0<<7; //MSBfirstSPI1->CR1|=7<<3; //设置时钟Fsck=Fcpu/256SPI1->CR1|=1<<2; //SPI 主机SPI1->CR1|=1<<1; //空闲模式下SCK为1 CPOL=1SPI1->CR1|=1<<0; //数据采样从第二个时间边沿开始SPI1->CR1|=1<<6; //使能SPI}现在可以读写数据了:u8 SPI1_ReadWriteByte(u8 data){//while((SPI1->SR && 1<<7) == 0); //等待SPI1空闲while((SPI1->SR && 1<<1)==0); //等待发送缓冲区空SPI1->DR = data;while((SPI1->SR && 1<<0)==0);return SPI1->DR;}。
STM32的SPI通信总结(含DMA)
STM32---SPI(DMA)通信的总结(库函数操作)本文主要由7项内容介绍SPI并会在最后附上测试源码供参考:1.SPI的通信协议2.SPI通信初始化(以STM32为从机,LPC1114为主机介绍)3.SPI的读写函数4.SPI的中断配置5.SPI的SMA操作6.测试源码7.易出现的问题及原因和解决方法一、SPI的通信协议SPI(Serial Peripheral Interface)是一种串行同步通讯协议,由一个主设备和一个或多个从设备组成,主设备启动一个与从设备的同步通讯,从而完成数据的交换。
SPI 接口一般由4根线组成,CS片选信号(有的单片机上也称为NSS),SCLK时钟信号线,MISO数据线(主机输入从机输出),MOSI数据线(主机输出从机输入),CS 决定了唯一的与主设备通信的从设备,如没有CS 信号,则只能存在一个从设备,主设备通过产生移位时钟信号来发起通讯。
通讯时主机的数据由MISO输入,由MOSI 输出,输入的数据在时钟的上升或下降沿被采样,输出数据在紧接着的下降或上升沿被发出(具体由SPI的时钟相位和极性的设置而决定)。
二、以STM32为例介绍SPI通信1.STM32f103 带有3个SPI模块其特性如下:2SPI 初始化初始化SPI 主要是对SPI要使用到的引脚以及SPI通信协议中时钟相位和极性进行设置,其实STM32的工程师已经帮我们做好了这些工作,调用库函数,根据自己的需要来修改其中的参量来完成自己的配置即可,主要的配置是如下几项:引脚的配置SPI1的SCLK, MISO ,MOSI分别是PA5,PA6,PA7引脚,这几个引脚的模式都配置成GPIO_Mode_AF_PP 复用推挽输出(关于GPIO的8种工作模式如不清楚请自己百度,在此不解释),如果是单主单从, CS引脚可以不配置,都设置成软件模式即可。
通信参数的设置1.SPI_Direction_2Lines_FullDuplex把SPI设置成全双工通信;2.在SPI_Mode 里设置你的模式(主机或者从机),3.SPI_DataSize是来设置数据传输的帧格式的SPI_DataSize_8b是指8位数据帧格式,也可以设置为SPI_DataSize_16b,即16位帧格式4.SPI_CPOL和SPI_CPHA是两个很重要的参数,是设置SPI通信时钟的极性和相位的,一共有四种模式在库函数中CPOL有两个值SPI_CPOL_High(=1)和SPI_CPOL_Low ( =0). CPHA有两个值SPI_CPHA_1Edge (=0) 和SPI_CPHA_2Edge(=1)CPOL表示时钟在空闲状态的极性是高电平还是低电平,而CPHA则表示数据是在什么时刻被采样的,手册中如下:我的程序中主、从机的这两位设置的相同都是设置成1,即空闲时时钟是高电平,数据在第二个时钟沿被采样,实验显示数据收发都正常。
STM32F103SPI操作
STM32F103SPI操作本主记录SPI 普通操作⽅式,主要为后续SPI设备提供基础1、MDK⼯程⽬录(创建⼯程⽅式略),⼯程结构与前⾯ "STM32F103 DMA模式操作UART" 类式2、spi 设备引脚初始在platform.c ⽂件下,这⾥⾯列出SX1278 设备platform.c 内容/*** @file platform.c 控制板GPIO引脚定义** @author T0213-ZH* @date 2018.06.13** @note 使⽤STM32F103VC6 微处理器** >>>> sx1278 模组接⼝定义 SPI1* SPI1 引脚 PA5->CLK, PA6->MISO, PA7->MOSI PA2->CSN* 控制引脚 PA3->RST, PA4->DIO0*** >>> 串⼝接⼝定义* USART1 引脚 PA9->TX, PA10->RX**/#include "platform.h"//******************************** SX1278 ******************************************************************extern void Delayms(uint32_t ms);void SX1278_spi_init(void){GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//PA3->RST PA2->CSNGPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_WriteBit(GPIOA, GPIO_Pin_3, Bit_RESET);GPIO_WriteBit(GPIOA, GPIO_Pin_2, Bit_SET);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); //PA5->CLK PA6->MISO PA7->MOSIGPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_Init(GPIOA, &GPIO_InitStructure);//DIO0RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;GPIO_Init(GPIOA, &GPIO_InitStructure);SPI_ApiStructure.init(&sx1278_ApiStructre.spi);Delayms(10);GPIO_WriteBit(GPIOA, GPIO_Pin_3, Bit_SET);Delayms(10);}void SX1278_spi_csn(uint8_t enable){GPIO_WriteBit(GPIOA, GPIO_Pin_2, (enable != 0x00) ? Bit_SET: Bit_RESET);}void sx1278_reset(uint8_t state){GPIO_WriteBit(GPIOA, GPIO_Pin_3, (state == 0) ? Bit_RESET: Bit_SET);}uint8_t sx1278_read_Dio_0(void){return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_4);}SX1278_TypeDef sx1278_ApiStructre = {.spi.pfunc_csn = SX1278_spi_csn,.spi.SPIx = SPI1,.init = SX1278_spi_init,.reset = sx1278_reset,.rDio0 = sx1278_read_Dio_0,};platform.h ⽂件内容,主要⽤于包含头⽂件及对应设备提供接⼝#ifndef __PLATFORM_H#define __PLATFORM_H#include "stm32f10x.h"#include "stm32f10x\F103\driver\gpio.h"#include "stm32f10x\F103\driver\time.h"#include "stm32f10x\F103\driver\spi.h"#include "stm32f10x\F103\driver\uart.h"#include "SX1278\radio\radio.h"#include "SX1278\radio\sx1276-Hal.h"extern UART_TypeDef UART_TypeDef_param;extern SX1278_TypeDef sx1278_ApiStructre;extern volatile uint32_t TickCounter;#endif3、spi 接⼝源⽂件spi.h 内容#ifndef __SPI_H#define __SPI_H/*** @brief SPI 控制参数*/typedef struct{void (* pfunc_csn)(uint8_t enable); /*!< csn 控制的函数指针 */SPI_TypeDef *SPIx; /*!< 对就的SPI组,如:SPI1,SPI2,SPI3 */}SPI_CtrDef;/*** @brief SPI 接⼝函数*/typedef struct{uint8_t (* init)(SPI_CtrDef *p); /*!< 初始化spi */uint8_t (* read_buf)(SPI_CtrDef *p, uint8_t reg, uint8_t *pbuf, uint8_t len); /*!< 读多个数据 */ uint8_t (* write_buf)(SPI_CtrDef *p, uint8_t reg, uint8_t *pbuf, uint8_t len); /*!< 写多个数据 */ uint8_t (* rwrite_onebyte)(SPI_CtrDef *p, uint8_t reg, uint8_t ch); /*!< 读单个数据 */uint8_t (* write_reg)(SPI_CtrDef *p, uint8_t reg); /*!< 写寄存器 */}SPI_ApiDef;extern const SPI_ApiDef SPI_ApiStructure;#endifspi.c 内容/*** @file spi.c spi 驱动程序** @author T0213-ZH* @date 2018.06.13*/#include "platform.h"/*** @brief 初始化SPI 参数* @param p: 指向SPI设置参数指针** @retval 返回初始结果 0-成功,1-失败*/static uint8_t SPIx_Init(SPI_CtrDef *p){SPI_InitTypeDef SPI_InitStructure;if(p->SPIx == SPI1){RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);}else if(p->SPIx == SPI2){RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);}else if(p->SPIx == SPI3){RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);}else{return 1;}SPI_Cmd(p->SPIx, DISABLE);SPI_InitStructure.SPI_Direction =SPI_Direction_2Lines_FullDuplex;SPI_InitStructure.SPI_Mode =SPI_Mode_Master;SPI_InitStructure.SPI_DataSize =SPI_DataSize_8b;SPI_InitStructure.SPI_CPOL =SPI_CPOL_Low;SPI_InitStructure.SPI_CPHA =SPI_CPHA_1Edge;SPI_InitStructure.SPI_NSS =SPI_NSS_Soft;SPI_InitStructure.SPI_BaudRatePrescaler =SPI_BaudRatePrescaler_8;SPI_InitStructure.SPI_FirstBit =SPI_FirstBit_MSB;SPI_InitStructure.SPI_CRCPolynomial =7;SPI_Init(p->SPIx,&SPI_InitStructure);SPI_Cmd(p->SPIx, ENABLE);return 0;}/*** @brief 读写1Bbyte数据* @param SPIx: 指向SPI组* @arg SPI1 使⽤SPI1 组* @arg SPI2 使⽤SPI2 组** @retval 8-bit 数据*//*static*/ uint8_t SPI_RWbyte(SPI_TypeDef *SPIx, uint8_t ch){while( SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_TXE) == RESET);SPI_I2S_SendData(SPIx, ch);while(SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) ==RESET);return SPI_I2S_ReceiveData(SPIx);}/*** @brief 读取指定寄存器中多个数据* @param p: 指向SPI设置参数指针* @param reg: 寄存器地址* @param pbuf: 指向存储数据的缓存指针* @param len: 读取长度** @retval 返回结果*/static uint8_t SPIx_Read_buf(SPI_CtrDef *p, uint8_t reg, uint8_t *pbuf, uint8_t len){ uint8_t status, i;p->pfunc_csn(0);status = SPI_RWbyte(p->SPIx, reg);for(i=0; i<len; i++){pbuf[i] = SPI_RWbyte(p->SPIx, 0xFF);}p->pfunc_csn(1);return status;}/*** @brief 向指定寄存器中写多个数据* @param p: 指向SPI设置参数指针* @param reg: 寄存器地址* @param pbuf: 指向写数据的缓存指针* @param len: 写数据长度** @retval 返回结果*/static uint8_t SPIx_Write_buf(SPI_CtrDef *p, uint8_t reg, uint8_t *pbuf, uint8_t len){ uint8_t status, i;p->pfunc_csn(0);status = SPI_RWbyte(p->SPIx, reg);for(i=0; i<len; i++){SPI_RWbyte(p->SPIx, *pbuf++);}p->pfunc_csn(1);return status;}/*** @brief 向指定寄存器中写读1Byte个数据* @param p: 指向SPI设置参数指针* @param reg: 寄存器地址* @param ch: 需写⼊的数据** @retval 返回结果*/static uint8_t SPIx_RWrite_Onebyte(SPI_CtrDef *p, uint8_t reg, uint8_t ch){uint8_t value;p->pfunc_csn(0);SPI_RWbyte(p->SPIx, reg);value = SPI_RWbyte(p->SPIx, ch);p->pfunc_csn(1);return value;}/*** @brief 写指定寄存器数据* @param p: 指向SPI设置参数指针* @param reg: 寄存器地址** @retval 返回结果*/static uint8_t SPIx_Write_reg(SPI_CtrDef *p, uint8_t reg){uint8_t status;p->pfunc_csn(0);status = SPI_RWbyte(p->SPIx, reg);p->pfunc_csn(1);return status;}/*** @brief SPI 接⼝函数*/const SPI_ApiDef SPI_ApiStructure = {.init = SPIx_Init, /*!< 初始化SPI */.read_buf = SPIx_Read_buf, /*!< 读多个数据 */.write_buf = SPIx_Write_buf, /*!< 写多个数据 */.rwrite_onebyte = SPIx_RWrite_Onebyte, /*!< 读写1Byte数据 */ .write_reg = SPIx_Write_reg, /*!< 写指定寄存器 */};4、具体操作在下节 SX1278 驱动.....本节⽂件:。
stm32SPI介绍和配置
stm32SPI介绍和配置 SPI是⼀种⾼速的,全双⼯同步的通信总线,在芯⽚管脚上占⽤了四根线,节约了芯⽚的管脚,同时为PCB的布局节省了空间,提供了⽅便,因此越来越多的芯⽚集成了这种通信协议,STM32也就有了SPI接⼝。
有上图可知有四个通信⼝,两个位移寄存器是同步的,那MISO和MOSI就不难理解了。
SCLK时钟信号,由主设备产⽣。
CS从设备⽚选信号,由主设备控制。
1、配置相关引脚的复⽤功能,使能SPI2时钟。
假设我们要使⽤SPI2,第⼀步SPI2时钟使能,第⼆步相关引脚的输出模式(MISO,MOSI,SCLK,(CS没有接外设的话,我们使⽤软件管理⽅式))。
1 GPIO_InitTypeDef GPIO_InitStructure;2 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE );//PORTB 时钟使能3 RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE );//SPI2 时钟使能4 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;5 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15 复⽤推挽输出6 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;7 GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化 GPIOB2、初始化SPI2,设置SPI2⼯作模式1 SPI_InitTypeDef SPI_InitStructure;2 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线双向全双⼯,还有半双⼯以及串⾏发和串⾏收⽅式3 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主 SPI 还有副 SPI4 SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // SPI 发送接收8或者16位帧结构5 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//串⾏同步时钟的空闲状态为⾼电平或者低电平6 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//第⼀个或者第⼆个跳变沿数据被采样7 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS 信号由软件控制8 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //预分频 2569 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从 MSB 位开始10 SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC 值计算的多项式11 SPI_Init(SPI2, &SPI_InitStructure); //根据指定的参数初始化外设 SPIx 寄存器3、使能SPI2初始化完成之后我们就要使能SPI2通信了。
STM32单片机SPI的使用原理解析
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
{
while((SPIx-》SR //等待发送区空
SPIx-》DR=Byte; //发送一个byte
while((SPIx-》SR//等待接收完一个byte
return SPIx-》DR; //返回收到的数据
}
GPIO_Init(GPIOB,
3、SPI配置
typedef struct
{
uint16_t SPI_Direction;//设置方向(2线全双工、2线只接受、一线发送、一线接受)
uint16_t SPI_Mode; //模式(从或主设备)
uint16_t SPI_DataSize; //宽度(8或16位)
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI2,
//使能SPI2
SPI_Cmd SPI_Exchange(SPI_TypeDef* SPIx,u8 Byte)
SPI_InitStructure.SPI_DirecTIon = SPI_DirecTIon_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
STM32的SPI通信总结(含DMA)
STM32---SPI(DMA)通信的总结(库函数操作)本文主要由7项内容介绍SPI并会在最后附上测试源码供参考:1.SPI的通信协议2.SPI通信初始化(以STM32为从机,LPC1114为主机介绍)3.SPI的读写函数4.SPI的中断配置5.SPI的SMA操作6.测试源码7.易出现的问题及原因和解决方法一、SPI的通信协议SPI(Serial Peripheral Interface)是一种串行同步通讯协议,由一个主设备和一个或多个从设备组成,主设备启动一个与从设备的同步通讯,从而完成数据的交换。
SPI 接口一般由4根线组成,CS片选信号(有的单片机上也称为NSS),SCLK时钟信号线,MISO数据线(主机输入从机输出),MOSI数据线(主机输出从机输入),CS 决定了唯一的与主设备通信的从设备,如没有CS 信号,则只能存在一个从设备,主设备通过产生移位时钟信号来发起通讯。
通讯时主机的数据由MISO输入,由MOSI 输出,输入的数据在时钟的上升或下降沿被采样,输出数据在紧接着的下降或上升沿被发出(具体由SPI的时钟相位和极性的设置而决定)。
二、以STM32为例介绍SPI通信1.STM32f103 带有3个SPI模块其特性如下:2SPI 初始化初始化SPI 主要是对SPI要使用到的引脚以及SPI通信协议中时钟相位和极性进行设置,其实STM32的工程师已经帮我们做好了这些工作,调用库函数,根据自己的需要来修改其中的参量来完成自己的配置即可,主要的配置是如下几项:引脚的配置SPI1的SCLK, MISO ,MOSI分别是PA5,PA6,PA7引脚,这几个引脚的模式都配置成GPIO_Mode_AF_PP 复用推挽输出(关于GPIO的8种工作模式如不清楚请自己百度,在此不解释),如果是单主单从,CS引脚可以不配置,都设置成软件模式即可。
通信参数的设置1.SPI_Direction_2Lines_FullDuplex把SPI设置成全双工通信;2.在SPI_Mode 里设置你的模式(主机或者从机),3.SPI_DataSize是来设置数据传输的帧格式的SPI_DataSize_8b是指8位数据帧格式,也可以设置为SPI_DataSize_16b,即16位帧格式4.SPI_CPOL和SPI_CPHA是两个很重要的参数,是设置SPI通信时钟的极性和相位的,一共有四种模式在库函数中CPOL有两个值SPI_CPOL_High(=1)和SPI_CPOL_Low ( =0). CPHA有两个值SPI_CPHA_1Edge (=0) 和SPI_CPHA_2Edge(=1)CPOL表示时钟在空闲状态的极性是高电平还是低电平,而CPHA则表示数据是在什么时刻被采样的,手册中如下:我的程序中主、从机的这两位设置的相同都是设置成1,即空闲时时钟是高电平,数据在第二个时钟沿被采样,实验显示数据收发都正常。
STM32之IO口模拟SPI
STM32之IO⼝模拟SPI本⽂介绍如何使⽤STM32标准外设库的GPIO端⼝模拟SPI,本例程使⽤PA5、PA6和PA7模拟⼀路SPI。
SPI有4种⼯作模式,模拟SPI使⽤模式0,即空闲时SCK为低电平,在奇数边沿采样。
本⽂适合对单⽚机及C语⾔有⼀定基础的开发⼈员阅读,MCU使⽤STM32F103VE系列。
1. 简介SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串⾏外围设备接⼝,是⼀种⾼速全双⼯的通信总线。
它被⼴泛地使⽤在要求通讯速率较⾼的场合。
SPI⽤于多设备之间通讯,分为主机Master和从机Slave,主机只有⼀个,从机可以有多个,通过⽚选信号对从机进⾏选择,⼀次只能选择⼀个从机。
通讯只能由主机发起,⽀持的操作分为读取和写⼊,即主机读取从机的数据,以及向从机写⼊数据。
SPI⼀般有4根线,分别是⽚选线SS、时钟线SCK、主设备输出\从设备输⼊MOSI、主设备输⼊\从设备输出MISO,其中除MISO对于主机为输⼊引脚外,其他引脚对于主机均为输出引脚。
因为有独⽴的输⼊和输出引脚,因此SPI⽀持全双⼯⼯作模式,即可以同时接收和发送。
2. 总线传输信号空闲状态:⽚选信号SS低电平有效,那么空闲状态⽚选信号SS为⾼。
开始信号及结束信号:开始信号需要将⽚选信号SS拉低,结束信号需要将⽚选信号SS拉⾼。
通讯模式:SPI有4种通讯模式,分别为0、1、2、3,根据时钟极性和时钟相位确定,时钟极性分别为空闲低电平和空闲⾼电平,时钟相位分别为SCK奇数边沿采样和偶数边沿采样。
常⽤的模式为模式0和模式3。
SPI模式时钟极性(空闲时SCK时钟)时钟相位(采样时刻)0低电平奇数边沿1低电平偶数边沿2⾼电平奇数边沿3⾼电平偶数边沿3. 时序说明以模式0举例说明:空闲状态:⽚选信号SS为⾼,SCK输出低电平。
开始信号:⽚选信号SS变低,SCK输出低电平。
结束信号:⽚选信号SS变⾼,SCK输出低电平。
STM32实现SPI简单通信配置
STM32实现SPI简单通信配置/*******************************************************说明:次程序成功实现SPI简单通信配置,能实现SPI1发送数据,SPI2接收数据简单通信功能,仅供测试使用。
*******************************************************/#include "stm32f10x_lib.h"#include "stm32f10x_conf.h"#include "ili9320.h"/****************************/#define LCD_X_ADDR 190#define LCD_Y_ADDR 100vu16 SPI_IDx = 0; //Reveive Datau16 SPI_ID_Temp = 0; //接收数据备份,用于判断接收到的数据是否发生变化u16 SPI_ID[4]; //位显示数据/********** 函数声明 *********/void RCC_Configuration(void);void NVIC_Configuration(void);void GPIO_Configuration(void);void SPI_Configuration(void);void Sys_Init(void);void LCD_Print_Out(void);/********** 系统初始化 ******************/void Sys_Init(void){#ifdef DEBUGdebug();#endif//----- 模块初始化配置 -------RCC_Configuration();NVIC_Configuration();GPIO_Configuration();SPI_Configuration();//--------- LCD -----------Lcd_Configuration();ili9320_Initializtion();ili9320_Clear(0);ili9320_PutChar(LCD_X_ADDR, LCD_Y_ADDR, 0x30 + 0, 65535, 0);}/******* 主函数 **********/int main(void){Sys_Init();while(1){LCD_Print_Out();}}/******** 用于调试LCD显示数据界面 *********/void LCD_Print_Out(void){//如果数据发生变化,则执行数据更新if(SPI_ID_Temp != SPI_IDx){if(SPI_IDx >= 1000){SPI_ID[1] = (SPI_IDx % 100) / 10;SPI_ID[2] = (SPI_IDx / 100) % 10;SPI_ID[3] = SPI_IDx / 1000;ili9320_PutChar(LCD_X_ADDR, LCD_Y_ADDR, 0x30 + SPI_ID[0], 65535, 0);ili9320_PutChar(LCD_X_ADDR - 8, LCD_Y_ADDR, 0x30 + SPI_ID[1], 65535, 0);ili9320_PutChar(LCD_X_ADDR - 16, LCD_Y_ADDR, 0x30 + SPI_ID[2], 65535, 0);ili9320_PutChar(LCD_X_ADDR - 24, LCD_Y_ADDR, 0x30 + SPI_ID[3], 65535, 0);}else if(SPI_IDx >= 100){SPI_ID[0] = SPI_IDx % 10;SPI_ID[1] = (SPI_IDx / 10) % 10;SPI_ID[2] = SPI_IDx / 100;ili9320_PutChar(LCD_X_ADDR, LCD_Y_ADDR, 0x30 + SPI_ID[0], 65535, 0);ili9320_PutChar(LCD_X_ADDR - 8, LCD_Y_ADDR, 0x30 + SPI_ID[1], 65535, 0);ili9320_PutChar(LCD_X_ADDR - 16, LCD_Y_ADDR, 0x30 + SPI_ID[2], 65535, 0);ili9320_PutChar(LCD_X_ADDR - 24, LCD_Y_ADDR, 0x30 + SPI_ID[3], 0, 0);}else if(SPI_IDx >= 10){SPI_ID[0] = SPI_IDx % 10;ili9320_PutChar(LCD_X_ADDR, LCD_Y_ADDR, 0x30 + SPI_ID[0], 65535, 0);ili9320_PutChar(LCD_X_ADDR - 8, LCD_Y_ADDR, 0x30 + SPI_ID[1], 65535, 0);ili9320_PutChar(LCD_X_ADDR - 16, LCD_Y_ADDR, 0x30 + SPI_ID[2], 0, 0);ili9320_PutChar(LCD_X_ADDR - 24, LCD_Y_ADDR, 0x30 + SPI_ID[3], 0, 0);}else{SPI_ID[0] = SPI_IDx % 10;ili9320_PutChar(LCD_X_ADDR, LCD_Y_ADDR, 0x30 + SPI_ID[0], 65535, 0);ili9320_PutChar(LCD_X_ADDR - 8, LCD_Y_ADDR, 0x30 + SPI_ID[1], 0, 0);ili9320_PutChar(LCD_X_ADDR - 16, LCD_Y_ADDR, 0x30 + SPI_ID[2], 0, 0);ili9320_PutChar(LCD_X_ADDR - 24, LCD_Y_ADDR, 0x30 + SPI_ID[3], 0, 0);}SPI_ID_Temp = SPI_IDx; //数据备份,为判断数据是否发生变化}}/************************************************************** ************//************************************************************** ************//****** 系统时钟配置函数 ********/void RCC_Configuration(void){ErrorStatus HSEStartUpStatus;RCC_DeInit(); //RCC system reset(for debug purpose)RCC_HSEConfig(RCC_HSE_ON); //Enable HSEHSEStartUpStatus = RCC_WaitForHSEStartUp(); //Wait till HSE is readyif(HSEStartUpStatus == SUCCESS){FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //Enable Prefetch BufferFLASH_SetLatency(FLASH_Latency_2); //Set 2 Latency cyclesRCC_HCLKConfig(RCC_SYSCLK_Div1); //AHB clock = SYSCLK RCC_PCLK2Config(RCC_HCLK_Div2); //APB2 clock = HCLK/2 RCC_PCLK1Config(RCC_HCLK_Div4); //APB1 clock = HCLK/4 RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //PLLCLK = 8MHz * 9 = 72 MHzRCC_PLLCmd(ENABLE); //Enable PLLwhile(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //Wait till PLL is readyRCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //Select PLL as system clock sourcewhile(RCC_GetSYSCLKSource() != 0x08); //Wait till PLL is used as system clock source//-----------------------------------------------------RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB| RCC_APB2Periph_SPI1, ENABLE); //GPIOD clock ENABLERCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); //TIM2 clock ENABLE}}/****** 中断配置函数 ********/void NVIC_Configuration(void){NVIC_InitTypeDef NVIC_InitStructure;//--- 在内存中进行调试 -----#ifdef VECT_TAB_RAMNIVC_SetVectorTable(NVIC_VectTab_RAM, 0x0);//--- 在FLASH中进行调试 ----#elseNVIC_SetVectorTable(NVIC_VectTab_FLASH,0x0);#endif//---- 配置TIM2中断,并使能 -----NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);NVIC_InitStructure.NVIC_IRQChannel = SPI1_IRQChannel;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);//------------------------------------NVIC_InitStructure.NVIC_IRQChannel = SPI2_IRQChannel;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure);}/********** 通用I/O配置函数 ***************/void GPIO_Configuration(void){GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//------------------GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;GPIO_Init(GPIOB, &GPIO_InitStructure);}/*****************************************************函数名称:SPI_Configuration(void)功能说明:SPI通信模块配置,配置SPI工作模式;输入参数:无返回值:无*****************************************************/void SPI_Configuration(void){SPI_InitTypeDef SPI_InitStructure;//--------------------- SPI1 configuration ------------------SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;SPI_InitStructure.SPI_Mode = SPI_Mode_Master;SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;SPI_InitStructure.SPI_CRCPolynomial = 7;SPI_Init(SPI1, &SPI_InitStructure);//--------- SPI2 configuration ------------------SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;SPI_Init(SPI2, &SPI_InitStructure);//--------- Enable SPI1 TXE interrupt ------------SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_TXE, ENABLE);//--------- Enable SPI2 RXNE interrupt -------------SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_RXNE, ENABLE);//----- Enable SPI2 ------SPI_Cmd(SPI2, ENABLE);//----- Enable SPI1 ----SPI_Cmd(SPI1, ENABLE);}/*************************************************/#ifdef DEBUGvoid assert_failed(u8 *file, u32 line){while(1){}}#endif/***********************************************************/ /***********************************************************/#include "stm32f10x_it.h"/****************************/extern vu16 SPI_IDx;/*********** TX发送中断服务子程序 *********************/ void SPI1_IRQHandler(void){//-------- 发送数据 ----------SPI_I2S_SendData(SPI1, 47);}/*********** RX接收中断服务子程序 ****************/void SPI2_IRQHandler(void){//------------- 接收数据 ------------SPI_IDx = SPI_I2S_ReceiveData(SPI2);}。
STM32SPI接口的简单实现
STM32SPI接口的简单实现SPI(Serial Peripheral Interface,串行外设接口)是一种常用的外设通信接口协议,用于与外部设备进行高速串行数据通信。
在STM32芯片中,SPI接口是通过SPI外设模块来实现的。
本文将介绍STM32 SPI接口的简单实现方法。
1.SPI接口的基本原理SPI接口是一种同步的、全双工的、点对点的通信协议,它由一个主设备和一个或多个从设备组成。
主设备通过时钟信号(SCK)为从设备提供时钟,同时通过主设备出发的数据信号(MOSI)发送数据给从设备。
从设备通过时钟信号和主设备进行同步,并通过数据信号(MISO)返回数据给主设备。
2.SPI硬件结构在STM32芯片中,多个SPI外设模块(SPI1、SPI2、SPI3等)可以分别配置为主模式或从模式。
每个SPI外设模块都包含一个发送数据寄存器(SPI_DR)和一个控制寄存器(SPI_CR1)。
SPI_DR寄存器用于发送和接收数据,SPI_CR1寄存器用于配置SPI模式、通信速率等参数。
3.SPI初始化配置在使用SPI接口之前,首先需要对SPI进行初始化配置。
具体配置步骤如下:a.配置GPIO管脚,将SPI相关管脚配置为SPI模式。
b.配置SPI_CR1寄存器,设置SPI模式(主模式或从模式)、通信速率、数据长度等参数。
c.使能SPI外设模块。
4.数据传输函数SPI提供了两种数据传输方式:轮询模式和中断模式。
在轮询模式下,主设备通过查询标志位的方式等待从设备的响应,然后发送或接收数据。
在中断模式下,主设备可以配置为在数据发送或接收完成时触发中断,然后进入中断服务程序处理数据。
5.数据传输流程SPI的数据传输流程通常分为以下几个步骤:a.配置SPI模式、通信速率等参数。
b.通过SPI_DR寄存器发送数据。
c.等待接收完毕或发送完毕。
d.通过SPI_DR寄存器读取接收到的数据。
6.实例代码下面是一个简单的SPI接口实现示例代码,以SPI1为例:```c#include "stm32f4xx.h"void SPI1_Init(void)/*配置GPIO管脚*//*将SPI1对应的GPIO管脚配置为SPI模式*//*配置SPI_CR1寄存器*//*设置SPI模式为主模式,通信速率为1MHz,数据长度为8位*/ /*使能SPI外设模块*/SPI1->CR1,=SPI_CR1_SPE;void SPI_SendData(uint8_t data)/*等待发送缓冲区为空*/while (!(SPI1->SR & SPI_SR_TXE));/*将数据写入发送缓冲区*/SPI1->DR = data;uint8_t SPI_ReceiveData(void)/*等待接收缓冲区非空*/while (!(SPI1->SR & SPI_SR_RXNE));/*读取接收缓冲区的数据*/return SPI1->DR;int main(void)uint8_t sendData;/*初始化SPI1外设*/SPI1_Init(;/*发送数据*/sendData = 0xAA;SPI_SendData(sendData);/*接收数据*/uint8_t receiveData = SPI_ReceiveData(;while (1)/*业务逻辑处理*/}```以上就是STM32SPI接口的简单实现方法,通过对SPI进行初始化配置和数据传输函数的调用,即可实现与外部设备的通信。
关于STM32的SPI端口设置问题
关于STM32的SPI端口设置问题在复用SPI 总线时,必须先设置总线端口。
读取其他ARM 芯片(如NXP)一般很容易看出芯片的设置是否正确。
不过对于STM32 就容易让人迷惑了。
例如,我们在使用SPI 总线进行通信时,可以这样设置:GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用的推挽输出恐怕大家对MISO 端口的设置就会产生疑惑了,MISO 不是应该设置成为输入端口(GPIO_Mode_IN_FLOATING)才行的吗?答题是肯定的,对于STM32 的这一类管脚来说(如USART_RX)即可以设置成为输入模式,也可以设置成为复用的推挽输出。
其工作都是正常的,不过建议大家还是设置成为输入端口的好,容易理解。
具体产生这一问题的原因是:从功能上来说,MISO 应该配置为输入模式才对,但为什么也可以配置为GPIO_Mode_AF_PP?请看下面的GPIO 复用功能配置框图。
当一个GPIO 端口配置为GPIO_Mode_AF_PP 是,这个端口的内部结构框图如下:图中可以看到,片上外设的复用功能输出信号会连接到输出控制电路,然后在端口上产生输出信号。
但是在芯片内部,MISO 是SPI 模块的输入引脚,而不是输出引脚,也就是说图中的”复用功能输出信号”根本不存在,因此”输出控制电路”不能对外产生输出信号。
而另一方面看,即使在GPIO_Mode_AF_PP 模式下,复用功能输入信号却与外部引脚之间相互连接,既MISO 得到了外部信号的电平,实现了输入的功能。
STM32SPI接口的简单实现要点
STM32 SPI接口的简单实现通常SPI通过4个引脚与外部器件相连:・MISO:主设备输入/从设备输出引脚。
该引脚在从模式下发送数据,在主模式下接收数据。
・MOSI:主设备输出/从设备输入引脚。
该引脚在主模式下发送数据,在从模式下接收数据。
・SCK:串口时钟,作为主设备的输出,从设备的输入・NSS:从设备选择。
这是一个可选的引脚,用来选择主/从设备。
它的功能是用来作为“片选引脚”,让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。
从设备的NSS引脚可以由主设备的一个标准I/O引脚来驱动。
一旦被使能(SSOE位),NSS引脚也可以作为输出引脚,并在SPI处于主模式时拉低;此时,所有的SPI设备,如果它们的NSS引脚连接到主设备的NSS引脚,则会检测到低电平,如果它们被设置为NSS硬件模式,就会自动进入从设备状态。
当配置为主设备、NSS配置为输入引脚(MSTR=1, SSOE=0)时,如果NSS被拉低,则这个SPI 设备进入主模式失败状态:即MSTR位被自动清除,此设备进入从模式。
时钟信号的相位和极性SPI_CR寄存器的CPOL和CPHA位,能够组合成四种可能的时序关系。
CPOL(时钟极性)位控制在没有数据传输时时钟的空闲状态电平,此位对主模式和从模式下的设备都有效。
如果CPOL被清‘ 0’,SCK引脚在空闲状态保持低电平;如果CPOL 被置’ 1’,SCK引脚在空闲状态保持高电平。
如果CPHA (时钟相位)位被置‘ 1’,SCK时钟的第二个边沿(CPOL位为0时就是下降沿,CPOL位为‘ 1’时就是上升沿)进行数据位的采样,数据在第二个时钟边沿被锁存。
如果CPHA位被清‘ 0’,SCK时钟的第一边沿(CPOL位为‘ 0’时就是下降沿,CPOL位为‘ 1’时就是上升沿)进行数据位采样,数据在第一个时钟边沿被锁存。
CPOL时钟极性和CPHA时钟相位的组合选择数据捕捉的时钟边沿。
图212显示了SPI传输的4种CPHA和CPOL位组合。
STM32SPI详解
STM32SPI详解1、SPI简介SPI 规定了两个 SPI 设备之间通信必须由主设备 (Master) 来控制次设备 (Slave). ⼀个 Master 设备可以通过提供 Clock 以及对 Slave 设备进⾏⽚选 (Slave Select) 来控制多个Slave 设备, SPI 协议还规定 Slave 设备的 Clock 由 Master 设备通过 SCK 管脚提供给 Slave 设备, Slave 设备本⾝不能产⽣或控制 Clock, 没有 Clock 则 Slave 设备不能正常⼯作。
2、SPI特点2.1、SPI控制⽅式采⽤主-从模式(Master-Slave) 的控制⽅式。
SPI 规定了两个 SPI 设备之间通信必须由主设备 (Master) 来控制次设备 (Slave). ⼀个 Master 设备可以通过提供 Clock 以及对 Slave 设备进⾏⽚选 (Slave Select) 来控制多个Slave 设备, SPI 协议还规定 Slave 设备的 Clock 由 Master 设备通过 SCK 管脚提供给 Slave 设备, Slave 设备本⾝不能产⽣或控制 Clock, 没有 Clock 则 Slave 设备不能正常⼯作。
2.2、SPI传输⽅式采⽤同步⽅式(Synchronous)传输数据Master 设备会根据将要交换的数据来产⽣相应的时钟脉冲(Clock Pulse), 时钟脉冲组成了时钟信号(Clock Signal) , 时钟信号通过时钟极性 (CPOL) 和时钟相位 (CPHA) 控制着两个 SPI 设备间何时数据交换以及何时对接收到的数据进⾏采样, 来保证数据在两个设备之间是同步传输的。
2.3、SPI数据交换SPI数据交换框图上图只是对 SPI 设备间通信的⼀个简单的描述, 下⾯就来解释⼀下图中所⽰的⼏个组件(Module):SSPBUF,Synchronous Serial Port Buffer, 泛指 SPI 设备⾥⾯的内部缓冲区, ⼀般在物理上是以 FIFO 的形式, 保存传输过程中的临时数据;SSPSR, Synchronous Serial Port Register, 泛指 SPI 设备⾥⾯的移位寄存器(Shift Regitser), 它的作⽤是根据设置好的数据位宽(bit-width) 把数据移⼊或者移出 SSPBUF; Controller, 泛指 SPI 设备⾥⾯的控制寄存器, 可以通过配置它们来设置 SPI 总线的传输模式。
STM32SPI注意要点
STM32SPI注意要点觉得SPI很简单,所以从来没有去仔细去看就直接用了,这次在调一个芯片的时候出现了一个比较奇怪的问题,以为是程序逻辑的问题,浪费了好几天的时间都没有找到原因。
今天乖乖查阅了一些手册,最后在《STM32不完全手册》里找到了线索,现在索性对SPI做个总结。
首先说最近碰到的问题。
问题一:错以为SPI的读数据,直接读取SPIx->DR寄存器就可以完成。
这个问题我一直没注意,十分惭愧。
原来SPI的时钟只有在往DR 寄存器里面写数据的时候才会产生,读是不会产生的(暂时没有从哪个资料中得到确认,不过我猜就是这样)。
所以要读取slave发过来的数据,master必须先发一个“DUMMY”数据,这个数据内容不重要,目的只是为了产生一组clock给slave,slave的数据就沿着这一组clock给发了出来。
master给slave读写数据的过程是这样的:写:master对DR写数据,产生clock,同时数据从MOSI管脚移位发送到slave的MOSI管脚;读:master对DR写DUMMy,产生clock,同时DUMMy由MOSI发给slave(这个数据没有意义),同时读取的数据从slave的MISO管脚移位发送到master的MISO管脚。
问题二:在配置为双线全双工的时候,如上面所说,在master写数据的时候,其实stm32的SPI同时也往master的DR寄存器里面读进数据(读写虽然都是DR,其实是两个不同的寄存器)。
对这点的忽略,就是这次问题产生的原因。
我在对采集芯片读取数据之前,需要向芯片发送一个读取数据的指令,在发送指令后,理论来说采集芯片会自动等待发送数据过来,只要我stm32这边发一个DUMMy产生一组clock,然后就可以从DR中读取数据。
但是由于在发送读取指令的时候,其实STM32也同时也把一个无用的数据读到DR里面去了,这个数据在没有被取走之前,是不会再接受新的数据的,所以在后来发送DUMMY的时候,读寄存器DR并没有更新,所以读到的数据自然是错的。
STM32硬件SPI主从通信教程
STM32硬件SPI主从通信教程STM32是一款广泛使用的微控制器,具有丰富的外设和强大的性能。
其中,硬件SPI(Serial Peripheral Interface)是一种常用的通信协议,用于在主设备和从设备之间进行高速数据交换。
本篇文章将介绍如何在STM32上实现硬件SPI主从通信。
首先,我们需要了解SPI的工作原理。
SPI是一种同步串行通信协议,使用四根线进行通信:SCLK(时钟线)、MOSI(主输出从输入线)、MISO (主输入从输出线)和NSS(片选信号线)。
其中,时钟线用于同步数据传输,主设备通过MOSI线发送数据,从设备通过MISO线接收数据,NSS线用于选择从设备。
SPI可以支持全双工通信,即主设备和从设备可以同时发送和接收数据。
在STM32中,SPI外设由SPI1、SPI2等多个SPI控制器组成。
每个SPI控制器都有两个工作模式:主模式和从模式。
主设备负责控制通信的时序和片选信号,从设备则被动地响应主设备的操作。
现在,我们来编写一个简单的SPI主从通信程序,以说明硬件SPI的使用方法。
首先,我们需要初始化SPI控制器。
在STM32CubeIDE中,可以通过CubeMX工具进行外设的初始化配置。
选择SPI控制器并设置工作模式为主模式,然后生成代码。
在生成的代码中,我们需要将SPI控制器的时钟频率、数据位、校验位等参数进行配置,并使能SPI外设。
此外,还需要配置GPIO,将SCLK、MOSI、MISO和NSS线连接到正确的引脚上。
接下来,我们需要编写主设备和从设备的SPI通信代码。
首先,主设备通过SPI发送数据给从设备://选择从设备HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);//发送数据uint8_t sendData = 0x55;HAL_SPI_Transmit(&hspi1, &sendData, 1, HAL_MAX_DELAY);//取消选择从设备HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);```这段代码首先使用NSS引脚选择从设备,然后使用HAL_SPI_Transmit函数发送数据。
最新STM32的SPI通信总结(含DMA)
STM32---SPI(DMA)通信的总结(库函数操作)本文主要由7项内容介绍SPI并会在最后附上测试源码供参考:1.SPI的通信协议2.SPI通信初始化(以STM32为从机,LPC1114为主机介绍)3.SPI的读写函数4.SPI的中断配置5.SPI的SMA操作6.测试源码7.易出现的问题及原因和解决方法一、SPI的通信协议SPI(Serial Peripheral Interface)是一种串行同步通讯协议,由一个主设备和一个或多个从设备组成,主设备启动一个与从设备的同步通讯,从而完成数据的交换。
SPI 接口一般由4根线组成,CS片选信号(有的单片机上也称为NSS),SCLK时钟信号线,MISO数据线(主机输入从机输出),MOSI数据线(主机输出从机输入),CS 决定了唯一的与主设备通信的从设备,如没有CS 信号,则只能存在一个从设备,主设备通过产生移位时钟信号来发起通讯。
通讯时主机的数据由MISO输入,由MOSI 输出,输入的数据在时钟的上升或下降沿被采样,输出数据在紧接着的下降或上升沿被发出(具体由SPI的时钟相位和极性的设置而决定)。
二、以STM32为例介绍SPI通信1.STM32f103 带有3个SPI模块其特性如下:2SPI 初始化初始化SPI 主要是对SPI要使用到的引脚以及SPI通信协议中时钟相位和极性进行设置,其实STM32的工程师已经帮我们做好了这些工作,调用库函数,根据自己的需要来修改其中的参量来完成自己的配置即可,主要的配置是如下几项:引脚的配置SPI1的SCLK, MISO ,MOSI分别是PA5,PA6,PA7引脚,这几个引脚的模式都配置成GPIO_Mode_AF_PP 复用推挽输出(关于GPIO的8种工作模式如不清楚请自己百度,在此不解释),如果是单主单从,CS引脚可以不配置,都设置成软件模式即可。
通信参数的设置1.SPI_Direction_2Lines_FullDuplex把SPI设置成全双工通信;2.在SPI_Mode 里设置你的模式(主机或者从机),3.SPI_DataSize是来设置数据传输的帧格式的SPI_DataSize_8b是指8位数据帧格式,也可以设置为SPI_DataSize_16b,即16位帧格式4.SPI_CPOL和SPI_CPHA是两个很重要的参数,是设置SPI通信时钟的极性和相位的,一共有四种模式在库函数中CPOL有两个值SPI_CPOL_High(=1)和SPI_CPOL_Low ( =0). CPHA有两个值SPI_CPHA_1Edge (=0) 和SPI_CPHA_2Edge(=1)CPOL表示时钟在空闲状态的极性是高电平还是低电平,而CPHA则表示数据是在什么时刻被采样的,手册中如下:我的程序中主、从机的这两位设置的相同都是设置成1,即空闲时时钟是高电平,数据在第二个时钟沿被采样,实验显示数据收发都正常。
stm32SPI通信[操作寄存器+库函数]-ChangingsBlog
stm32SPI通信[操作寄存器+库函数]-ChangingsBlogSPI(Serial Peripheral Interface--串行外设接口) 总线系统是一种同步串行外设接口,它可以使MCU与各种外围设备以串行方式进行通信以交换信息。
SPI是Freescale(原Motorola)公司首先在其处理器上定义的。
SPI是一种高速、主从式、全双工、同步传输的通信总线,SPI总线在物理层体现为四根传输线:•MOSI (Master Output Slaver Input) –主器件数据输出,从器件数据输入•MISO (Master Input Slaver Output) –主器件数据输入,从器件数据输出•SCLK –时钟信号,由主器件产生•NSS –从器件使能信号,由主器件控制,有的IC会标注为CS(Chip select)CS线用于控制片选信号,当一个SPI从设备的CS线识别到了预先规定的片选电平,则该设备被选中。
显然可以通过CS线,完成“一主多从”的SPI网络架设,在进行“一主一从”的SPI通信时,SPI并不是必须的。
SPI总线传输数据时,由主机的SCLK线提供时钟脉冲,从机被动的接收时钟脉冲。
主机在数据发送前,将数据写入数据寄存器,等待时钟脉冲移位输出,每个脉冲周期传输1位数据。
从机在主机的数据发送中,依靠主机的时钟,将从机的数据寄存器内容移位发送。
所以要实现主从数据交换,在时钟信号前,主机从机都必须先将数据写入数据寄存器,并且从机必须在主机前写入,然后由主机的SCLK时钟驱动发送。
不注意这个问题很容易造成SPI接收的数据错位。
这样的全双工、同步传输完全依赖于主机控制的时钟线SCLK,而且SCLK上只有数据传输的时候才有时钟信号。
主机向从机发送数据不会有问题,但是如果从机主动向主机发送数据呢?从机要发送数据,必须要有SCLK的时钟,所以只能主机发送一个DUMMY(哑巴)字节,产生时钟,来实现和从机的数据交换。
STM32SPIDMA的使用
STM32SPIDMA的使⽤ ⼀是想总结⼀下SPI总线的特点与注意点,⼆是总结⼀下SPI DMA的使⽤⼀、SPI信号线说明 通常SPI通过4个引脚与外部器件相连:MISO:主设备输⼊/从设备输出引脚。
该引脚在从模式下发送数据,在主模式下接收数据。
MOSI:主设备输出/从设备输⼊引脚。
该引脚在主模式下发送数据,在从模式下接收数据。
SCK:串⼝时钟,作为主设备的输出,从设备的输⼊NSS:从设备选择。
这是⼀个可选的引脚,⽤来选择主/从设备。
它的功能是⽤来作为“⽚选引脚”,让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。
⼆、原理 MOSI脚相互连接,MISO脚相互连接。
这样,数据在主和从之间串⾏地传输(MSB位在前)。
通信总是由主设备发起。
主设备通过MOSI脚把数据发送给从设备,从设备通过MISO引脚回传数据。
这意味全双⼯通信的数据输出和数据输⼊是⽤同⼀个时钟信号同步的;时钟信号由主设备通过SCK脚提供。
三、NSS说明与注意点 NSS分为内部引脚和外部引脚。
NSS外部引脚可以作为输⼊信号或者输出信号,输⼊信号⼀般⽤作硬件⽅式从机的⽚选,⽽输出信号⼀般⽤于主SPI去⽚选与之相连的从SPI。
NSS从设备选择有两种模式:1、软件模式 可以通过设置SPI_CR1寄存器的SSM位来使能这种模式,当它为1时,NSS引脚上的电平由SSI决定。
在这种模式下NSS外部引脚可以⽤作它⽤,⽽内部NSS信号电平可以通过写SPI_CR1的SSI位来驱动。
2、硬件模式两种⽅式:(1)对于主SPI,NSS可以直接接⾼电平,对于从SPI,可以直接接低电平。
(2)当STM32F10xxx⼯作为主SPI,并且NSS输出已经通过SPI_CR2寄存器的SSOE位使能,这时主机的NSS讲作为输出信号,引脚信号被拉低,所有NSS引脚与这个主SPI的NSS引脚相连并配置为硬件NSS的SPI设备,将⾃动变成从SPI设备。
此时两个的NSS信号线可以接个上拉电阻直连。
STM32-SPI
4月25日---- 4月27日SPI总线经过两天的学习,哈哈SPI终于搞定,用STM32控制IIC EEPORM,将EEPROM中的数据读到SPI的FLASH中。
---------------------------------------------------------------------------------------------------------------------- SPI(Serial Peripheral Interface)总线:串行外围总线接口一、SPI总线的简介:SPI是由摩托罗拉公司开发的全双工同步串行总线,没有总线仲裁机制,所以工作在单主机系统中。
主设备启动与从设备的同步通讯,从而完成数据的交换。
对于只能做从机的器件接口来说,SPI接口由SDI(串行数据输入),SDO(串行数据输出),SCK(串行移位时钟)和CS(从使能信号)。
通讯时,数据由SDO输出,SDI输入,数据在时钟的上升沿或者下降沿由SDO输出,在紧接着的下降沿或者上升沿由SDI读入,这样经过8/16帧,以完成8/16位数据的传输。
对于有的微控制器来说,它的硬件SPI即可做主机,也可以做从机,即可实现主从配置,MOSI:主出从入、MISO:主入从出、SCK:串行时钟、SS:从属选择。
(当做主机输出时,该信号用于选中需要访问的从机,SS输入高电平,外部决定其为主机;当做从机时,SS为输入或者一般的IO口使用)。
常用的SPI接法:在软件配置的条件下SS不使用。
1、SPI总线的通信时序(1)在SPI通信中,在全双工模式下,发送和接收是同事进行的(2)数据传输的时钟基来自主控制器的时钟脉冲;摩托罗拉没有定义任何通用的SPI 时钟的规范,最常用的时钟设置是基于时钟极性CPOL和时钟相位CPHA两个参数。
a)CPOL=0,表示时钟的空闲状态为低电平b)CPOL=1,表示时钟的空闲状态为高电平c)CPHA=0,表示同步始终的第一个边沿(上升或者下降)数据被采样d)CPHA=1,表示同步始终的第二个边沿(上升或者下降)数据被采样即CPOL和CPHA的设置决定了数据采样的时钟沿。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
STM32 SPI接口的简单实现通常SPI通过4个引脚与外部器件相连:● MISO:主设备输入/从设备输出引脚。
该引脚在从模式下发送数据,在主模式下接收数据。
● MOSI:主设备输出/从设备输入引脚。
该引脚在主模式下发送数据,在从模式下接收数据。
● SCK:串口时钟,作为主设备的输出,从设备的输入●NSS:从设备选择。
这是一个可选的引脚,用来选择主/从设备。
它的功能是用来作为“片选引脚”,让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。
从设备的NSS引脚可以由主设备的一个标准I/O引脚来驱动。
一旦被使能(SSOE位),NSS引脚也可以作为输出引脚,并在SPI处于主模式时拉低;此时,所有的SPI设备,如果它们的NSS引脚连接到主设备的NSS引脚,则会检测到低电平,如果它们被设置为NSS硬件模式,就会自动进入从设备状态。
当配置为主设备、NSS配置为输入引脚(MSTR=1,SSOE=0)时,如果NSS被拉低,则这个SPI 设备进入主模式失败状态:即MSTR位被自动清除,此设备进入从模式。
时钟信号的相位和极性SPI_CR寄存器的CPOL和CPHA位,能够组合成四种可能的时序关系。
CPOL(时钟极性)位控制在没有数据传输时时钟的空闲状态电平,此位对主模式和从模式下的设备都有效。
如果CPOL被清’0’,SCK引脚在空闲状态保持低电平;如果CPOL 被置’1’,SCK引脚在空闲状态保持高电平。
如果CPHA(时钟相位)位被置’1’,SCK时钟的第二个边沿(CPOL位为0时就是下降沿,CPOL位为’1’时就是上升沿)进行数据位的采样,数据在第二个时钟边沿被锁存。
如果CPHA位被清’0’,SCK时钟的第一边沿(CPOL位为’0’时就是下降沿,CPOL位为’1’时就是上升沿)进行数据位采样,数据在第一个时钟边沿被锁存。
CPOL时钟极性和CPHA时钟相位的组合选择数据捕捉的时钟边沿。
图212显示了SPI传输的4种CPHA和CPOL位组合。
此图可以解释为主设备和从设备的SCK脚、MISO脚、MOSI脚直接连接的主或从时序图。
CPOL时钟极性和CPHA时钟相位的组合选择数据捕捉的时钟边沿。
上图显示了SPI传输的4种CPHA和CPOL位组合。
此图可以解释为主设备和从设备的SCK脚、MISO脚、MOSI脚直接连接的主或从时序图。
注意:1. 在改变CPOL/CPHA位之前,必须清除SPE位将SPI禁止。
2. 主和从必须配置成相同的时序模式。
3.SCK的空闲状态必须和SPI_CR1寄存器指定的极性一致(CPOL为’1’时,空闲时应上拉SCK为高电平;CPOL为’0’时,空闲时应下拉SCK为低电平)。
4. 数据帧格式(8位或16位)由SPI_CR1寄存器的DFF位选择,并且决定发送/接收的数据长度。
我只要知道主机和从机的CPOL和CPHA位要一致就够了。
有2种NSS模式:●软件NSS模式:可以通过设置SPI_CR1寄存器的SSM位来使能这种模式。
在这种模式下NSS引脚可以用作它用,而内部NSS信号电平可以通过写SPI_CR1的SSI位来驱动●硬件NSS模式,分两种情况:─NSS输出被使能:当STM32F10xxx工作为主SPI,并且NSS输出已经通过SPI_CR2寄存器的SSOE位使能,这时NSS引脚被拉低,所有NSS引脚与这个主SPI的NSS 引脚相连并配置为硬件NSS的SPI设备,将自动变成从SPI设备。
当一个SPI 设备需要发送广播数据,它必须拉低NSS信号,以通知所有其它的设备它是主设备;如果它不能拉低NSS,这意味着总线上有另外一个主设备在通信,这时将产生一个硬件失败错误(HardFault)。
─ NSS输出被关闭:允许操作于多主环境。
//我们用软件NSS主从的转换都可借助库来实现数据帧格式根据SPI_CR1寄存器中的LSBFIRST位,输出数据位时可以MSB在先也可以LSB 在先。
根据SPI_CR1寄存器的DFF位,每个数据帧可以是8位或是16位。
所选择的数据帧格式对发送和/或接收都有效。
配置一个SPI 这里选SPI2如下:SPI_InitTypeDef SPI_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);SPI_Cmd(SPI2, DISABLE); //必须先禁能,才能改变MODESPI_InitStructure.SPI_Direction =SPI_Direction_2Lines_FullDuplex; //两线全双工SPI_InitStructure.SPI_Mode =SPI_Mode_Master; //主SPI_InitStructure.SPI_DataSize =SPI_DataSize_8b; //8位SPI_InitStructure.SPI_CPOL =SPI_CPOL_High; //CPOL=1时钟悬空高SPI_InitStructure.SPI_CPHA =SPI_CPHA_1Edge; //CPHA=1 数据捕获第2个SPI_InitStructure.SPI_NSS =SPI_NSS_Soft; //软件NSSSPI_InitStructure.SPI_BaudRatePrescaler =SPI_BaudRatePrescaler_2; //2分频SPI_InitStructure.SPI_FirstBit =SPI_FirstBit_MSB; //高位在前SPI_InitStructure.SPI_CRCPolynomial =7; //CRC7SPI_Init(SPI2,&SPI_InitStructure);SPI_Cmd(SPI2, ENABLE);//spi的配置结束了可以使用了。
也可用函数SPI_StructInit 把SPI_InitStruct中的每一个参数按缺省值填入_____________________________________________________________________ ________________发送缓冲器空闲标志(TXE)【3.0 SPI_I2S_FLAG_TXE】此标志为’1’时表明发送缓冲器为空,可以写下一个待发送的数据进入缓冲器中。
当写入SPI_DR时,TXE标志被清除。
接收缓冲器非空(RXNE)【3.0 SPI_I2S_FLAG_RXNE】此标志为’1’时表明在接收缓冲器中包含有效的接收数据。
读SPI数据寄存器可以清除此标志。
注意在2.0的库中函数SPI_SendData SPI_ReceiveData SPI_GetFlagStatus 等在3.0的库中变为SPI_I2S_SendData SPI_I2S_ReceiveData SPI_I2S_GetFlagStatus写一个发送/接受函数static u8 SPIByte(u8 byte){while((SPI2->SR &SPI_I2S_FLAG_TXE)==RESET);//while((SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE))==RESET);SPI2->DR = byte;//SPI_I2S_SendData(SPI2,byte);while((SPI2->SR &SPI_I2S_FLAG_RXNE)==RESET);//while((SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE))==RESET); return(SPI2->DR);//returnSPI_I2S_ReceiveData(SPI2);读寄存器用硬件清除标志位。
//SPI_I2S_ClearFlag(SPI2,SPI_I2S_FLAG_RXNE) ;直接软件清除标志位。
}这里有两种写法直接操作寄存器与用库函数,相对来说直接操作寄存器应该更直观一些。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~调试总会遇到这样那样的小问题和朋友一起进步总会更快希望对别人能有点帮助节省些时间万利的板子板载的M25P80 PDF写的这一型号有 25MHZ 和50MHZ两种注意初始化SPI的设置 BAUD至少就要PCLK/2 = 36Mhz即使配置为主模式发送时钟也不是连产生的接收的时候同样需要发送无用字节以维持数据时钟STM32 的 NSS脚需要配置为软件模式(可以通过寄存器控制NSS高低) 硬件模式下 NSS引脚必须+ 正的逻辑电平才能将SPE MSTR位时能配置SPI为主模式先写了一遍然后借鉴一个sst25vf08的应用笔记又写了一遍看这个笔记省了不少时间很感谢同一地址单元重新写入需先擦除否者二次写入后读出无效其他按照时序走就可以了程序如下:#define WREN 0x06#define WRDI 0x04#define RDSR 0x05#define WRSR 0x01#define READ 0x03#define FAST_READ 0x06#define PAGE_PROG 0x02#define SECTOR_ERASER 0xd8#define BULK_ERASER 0xc7#define DEEP_SLEEP 0xb9#define RES 0xab#define M25P80_SELECT() GPIOB->BRR |= GPIO_Pin_2#define M25P80_DESELECT() GPIOB->BSRR |= GPIO_Pin_2u8 SPI_SeRe_Byte(u8 data);void M25p80_Cmd1b(u8 cmd);u8 M25p80_Cmd1b_R1b(u8 cmd);void M25p80_Cmd1b_S1b(u8 cmd , u8 data);void M25p80_WP_En(void);void M25p80_Write_En(void);u8 M25p80_Busy(void);u8 M25p80_Read_1Byte(u32 addr );void M25p80_Read_Bytes(u32 addr , u8* re_buf_p , u16 no);void M25p80_Write_1Byte(u32 addr , u8 data);void M25p80_Write_Bytes(u32 addr , u8* wr_buf_p , u16 no);void M25p80_Section_Erase(u32 addr);void M25p80_Bulk_Erase(void);/******************************************************************** ************************************************* op-code for M25P80 SPI EEROM* 8Mbit = 1,048,576 bytes = 16 sectors (512 Kbits, 65536 bytes each) = 4096 pages (256 bytes each)* 1sector = 256 page* address range 00000 ~ fffffh********************************************************************* ************************************************/u8 SPI_SeRe_Byte(u8 data){u8 re_data;while(! (SPI1->SR & 0x0002) );/*TXE?*/SPI1->DR = data;while(! (SPI1->SR & 0x0001) );/*RXNE?*/re_data = SPI1->DR;return re_data;}/******************************************************************** *****************************************函数名称:M25p80_cmd1b_r1b/M25p80_Cmd1b_S1b CMD命令发送返回1BYTE/命令发送跟送1BYTE********************************************************************* ***************************************/void M25p80_Cmd1b(u8 cmd){M25P80_SELECT();SPI_SeRe_Byte(cmd);M25P80_DESELECT();}u8 M25p80_Cmd1b_R1b(u8 cmd){u8 data;M25P80_SELECT();SPI_SeRe_Byte(cmd);data = SPI_SeRe_Byte(0xff);M25P80_DESELECT();return data;}void M25p80_Cmd1b_S1b(u8 cmd , u8 data){M25P80_SELECT();SPI_SeRe_Byte(cmd);SPI_SeRe_Byte(data);M25P80_DESELECT();}/******************************************************************** ********************************************//******************************************************************** *****************************************函数名称:M25p80_WP_En/M25p80_Write_En M25p80写保护使能/写使能 status 寄存器修改********************************************************************* ***************************************/void M25p80_WP_En(void){u8 sta;sta = M25p80_Cmd1b_R1b(RDSR) | 0x1c;M25p80_Cmd1b_S1b(WRSR, sta);M25p80_Cmd1b(WRDI);}void M25p80_Write_En(void){u8 sta;sta = M25p80_Cmd1b_R1b(RDSR) & (~0x1c);M25p80_Cmd1b_S1b(WRSR, sta);M25p80_Cmd1b(WREN);}/******************************************************************** ********************************************/u8 M25p80_Busy(void){u8 sta;sta = M25p80_Cmd1b_R1b(RDSR);return (sta & 0x01);}u8 M25p80_Read_1Byte(u32 addr ){u8 ad[3] , data;ad[0] = (addr & 0x00ff0000) >> 16;ad[1] = (addr & 0x0000ff00) >> 8;ad[2] = (addr & 0x000000ff);M25P80_SELECT();SPI_SeRe_Byte(READ);SPI_SeRe_Byte(ad[0]);SPI_SeRe_Byte(ad[1]);SPI_SeRe_Byte(ad[2]);data = SPI_SeRe_Byte(0xff);M25P80_DESELECT();return data;}void M25p80_Read_Bytes(u32 addr , u8* re_buf_p , u16 no){u8 ad[3] ;ad[0] = (addr & 0x00ff0000) >> 16;ad[1] = (addr & 0x0000ff00) >> 8;ad[2] = (addr & 0x000000ff);M25P80_SELECT();SPI_SeRe_Byte(READ);SPI_SeRe_Byte(ad[0]);SPI_SeRe_Byte(ad[1]);SPI_SeRe_Byte(ad[2]);for(; no > 0; no--)*re_buf_p++ = SPI_SeRe_Byte(0xff);M25P80_DESELECT();}/******************************************************************** ********************************************/void M25p80_Write_1Byte(u32 addr , u8 data){u8 ad[3] ;ad[0] = (addr & 0x00ff0000) >> 16;ad[1] = (addr & 0x0000ff00) >> 8;ad[2] = (addr & 0x000000ff);M25p80_Write_En();M25P80_SELECT();SPI_SeRe_Byte(PAGE_PROG);SPI_SeRe_Byte(ad[0]);SPI_SeRe_Byte(ad[1]);SPI_SeRe_Byte(ad[2]);SPI_SeRe_Byte(data);M25P80_DESELECT();M25p80_WP_En();while(M25p80_Busy());}void M25p80_Write_Bytes(u32 addr , u8* wr_buf_p , u16 no){/*u8 ad[3] ;ad[0] = (addr & 0x00ff0000) >> 16;ad[1] = (addr & 0x0000ff00) >> 8;ad[2] = (addr & 0x000000ff);if( no < 255 -ad[2] ) return 0 ;//PP写操作过本页地址从本页首地址循环写M25p80_Cmd1b(WREN);M25P80_SELECT();SPI_SeRe_Byte(PAGE_PROG);SPI_SeRe_Byte(ad[0]);SPI_SeRe_Byte(ad[1]);SPI_SeRe_Byte(ad[2]);for(; no > 0; no--)*wr_buf_p++ = SPI_SeRe_Byte(0xff);M25P80_DESELECT();M25p80_Cmd1b(WRDI);return 1;*/for(; no > 0; no--){M25p80_Write_1Byte(addr , *wr_buf_p);addr++;wr_buf_p++;}M25p80_WP_En();}/******************************************************************** ********************************************/void M25p80_Section_Erase(u32 addr){u8 ad[3] ;ad[0] = (addr & 0x00ff0000) >> 16;ad[1] = (addr & 0x0000ff00) >> 8;ad[2] = (addr & 0x000000ff);M25p80_Write_En();M25P80_SELECT();SPI_SeRe_Byte(SECTOR_ERASER);SPI_SeRe_Byte(ad[0]);SPI_SeRe_Byte(ad[1]);SPI_SeRe_Byte(ad[2]);M25P80_DESELECT();while(M25p80_Busy());M25p80_WP_En();}void M25p80_Bulk_Erase(void){M25p80_Write_En();M25p80_Cmd1b(BULK_ERASER);while(M25p80_Busy());M25p80_WP_En();}这里的多字节写函数void M25p80_Write_Bytes(u32 addr , u8* wr_buf_p , u16 no) 调用了单字节写函数实际上还是一个一个写主要因为用页写操作的话当数据到达页底会返回本页从头开始写会破坏数据而且没有字节写操作码感觉比sst25vf08麻烦很多。