嵌入式单片机基础篇(三十六)之STM32F1SPI标准接口程序以及模拟SPI标准接口程序讲解

本文详细介绍了STM32F1系列单片机中SPI标准接口的初始化步骤,包括IO口配置、SPI模式设置、波特率预分频值的选择等。此外,还讲解了如何模拟SPI标准接口,并提到了与W25Q128FLASH的交互,包括初始化、读写状态寄存器、擦除和写入操作。提供了完整的程序代码结构。
摘要由CSDN通过智能技术生成

STM32F1SPI标准接口程序以及模拟SPI标准接口程序讲解

一、SPI标准接口初始化:

1、SPI 内部结构简明图
在这里插入图片描述

2、上一篇文章提到,spi标准协议包含4条信号线、时钟相位、时钟极性、数据单字节从高位到低位传输、片选信号可以由软件控制,也可以由硬件控制,依据以上内容初始化SPI标准接口程序如下:

void SPI2_Init(void)
{
   
GPIO_InitTypeDef GPIO_InitStructure;
 SPI_InitTypeDef SPI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE );//PORTB 时钟使能
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE );//①SPI2 时钟使能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); //①初始化 GPIOB
GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); //PB13/14/15 上拉
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置 SPI 全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置 SPI 工作模式:设置为主 SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 8 位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//选择了串行时钟的稳态:时钟悬空高
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //数据捕获于第二个时钟沿
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS 信号由硬件管理
 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //预分频 256
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从 MSB 位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC 值计算的多项式
SPI_Init(SPI2, &SPI_InitStructure); //②根据指定的参数初始化外设 SPIx 寄存器
SPI_Cmd(SPI2, ENABLE); //③使能 SPI 外设
SPI2_ReadWriteByte(0xff); //④启动传输
}

(1)IO口初始化:
要用 SPI2,第一步就要使能 SPI2 的时钟。其次要设置 SPI2 的相关引脚为复用输出,这样才会连接到 SPI2 上否则这些 IO 口还是默认的状态,也就是标准输入输出口。这里使用的是 PB13、14、15 这 3 个(SCK.、MISO、MOSI,CS 使用软件管理方式),所以设置这三个为复用 IO。
(2)初始化SPI标准接口(结构体):
第一个参数 SPI_Direction 是用来设置 SPI 的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式,这里选择全双工模式 SPI_Direction_2Lines_FullDuplex。
第二个参数 SPI_Mode 用来设置 SPI 的主从模式,这里设置为主机模式 SPI_Mode_Master,当然也可以选择为从机模式 SPI_Mode_Slave。
第三个参数 SPI_DataSiz 为 8 位还是 16 位帧格式选择项,这里是 8 位传输,选择SPI_DataSize_8b。
第四个参数 SPI_CPOL 用来设置时钟极性,设置串行同步时钟的空闲状态为高电平所以选择 SPI_CPOL_High。
第五个参数 SPI_CPHA 用来设置时钟相位,也就是选择在串行同步时钟的第几个跳变沿(上升或下降)数据被采样,可以为第一个或者第二个条边沿采集,这里选择第二个跳变沿,所以选择 SPI_CPHA_2Edge
第六个参数 SPI_NSS 设置 NSS 信号由硬件(NSS 管脚)还是软件控制,这里通过软件控制 NSS 关键,而不是硬件自动控制,所以选择 SPI_NSS_Soft。
第七个参数 SPI_BaudRatePrescaler 很关键,就是设置 SPI 波特率预分频值也就是决定 SPI 的时钟的参数,从不分频道 256 分频 8 个可选值,初始化的时候选择 256 分频值SPI_BaudRatePrescaler_256, 传输速度为 36M/256=140.625KHz。
第八个参数 SPI_FirstBit 设置数据传输顺序是 MSB 位在前还是 LSB 位在前,这里选择SPI_FirstBit_MSB 高位在前。
第九个参数 SPI_CRCPolynomial 是用来设置 CRC 校验多项式,提高通信可靠性,大于 1 即可。
(3)使能 SPI2:
初始化完成之后接下来是要使能 SPI2 通信了,在使能 SPI2 之后,就可以开始 SPI 通讯了。使能 SPI2 的方法是:
SPI_Cmd(SPI2, ENABLE); //使能 SPI 外设
(4)启动数据传输:
外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。
3、标准SPI接口初始化程序写完后,开始写数据传输程序,程序如下:

u8 SPI2_ReadWriteByte(u8 TxData)
{
   
u8 retry=0;
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //等待发送区空
{
   
retry++;
if(retry>200)return 0;
} 
SPI_I2S_SendData(SPI2, TxData); //通过外设 SPIx 发送一个数据
retry=0;
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //等待接收
//完一个 byte
{
    retry++;
if(retry>200)return 0;
}
return SPI_I2S_ReceiveData(SPI2); //返回通过 SPIx 最近接收的数据
}

(1)SPI 传输数据:
通信接口需要有发送数据和接受数据的函数,固件库提供的发送数据函数原型为:
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
往 SPIx 数据寄存器写入数据 Data,从而实现发送。
固件库提供的接受数据函数原型为:
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;
从 SPIx 数据寄存器读出接受到的数据。
(2)获取SPI 传输状态:
SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE);
SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE);
在这里插入图片描述在这里插入图片描述***(3)改变SPI 波特率预分频值从而改变数据传输速度的程序如下:***

//SPI 速度设置函数
//SpeedSet://SPI_BaudRatePrescaler_256 256 分频 (SPI 281.25K@sys 72M)
void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)
{
   
 assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
SPI2->CR1&=0XFFC7;
SPI2->CR1|=SPI_BaudRatePrescaler; //设置 SPI2 速度
SPI_Cmd(SPI2,ENABLE); 
}

至此SPI标准接口初始化程序就已完成了

二、STM32模拟SPI标准接口设计思路以及部分程序讲解

1、既然SPI标准协议是用4条信号线,那么首先需要初始化4个引脚,其中MISO引脚要初始化为输入引脚,其他三条信号线SCL,CS,MOSI引脚都初始化为输出引脚。
2、片选信号与时钟极性在初始化时将对应的引脚设置为高电平或低电平就对应不同的模式(所谓的时钟极性也就是在空闲状态下时钟信号的高低电平状态)
3、初始化设置好后就该写读写数据函数了,那么读写数据函数分为两种形式:
(1)一种是分开读写,也就是读是一个函数,写是一个函数,分时传输数据,程序如下:

/*
* 函数名:void SPI_WriteByte(uint8_t data)
* 输入参数:data -> 要写的数据
* 输出参数:无
* 返回值:无
* 函数作用:模拟 SPI 写一个字节
*/
void SPI_WriteByte(uint8_t data)
{
   
uint8_t i = 0;
uint8_t temp = 0;
for(i=0; i<8; i++) {
   
temp = ((data&0x80)==0x80)? 1:0;
data = data<<1;
SPI_CLK(0); //CPOL=0
SPI_MOSI(temp);
SPI_Delay();
SPI_CLK(1); //CPHA=0
SPI_Delay(); }
SPI_CLK(0); }
/*
* 函数名:uint8_t SPI_ReadByte(void)
* 输入参数:
* 输出参数:无
* 返回值:读到的数据
* 函数作用:模拟 SPI 读一个字节
*/
uint8_t SPI_ReadByte(void) {
   
uint8_t i = 0;
uint8_t read_data = 0xFF;
for(i=0; i<8; i++) {
   
read_data = read_data << 1;
SPI_CLK(0);
SPI_Delay();
SPI_CLK(1);
SPI_Delay();
if(SPI_MISO()==1) {
   
read_data = read_data + 1; } }
SPI_CLK(0);
return read_data;
}

(2)另一种是读写数据是一个函数,同步传输数据,比如在时钟的上升沿是写数据,那么在时钟的下降沿就是读数据,程序如下:

/*
* 函数名:uint8_t SPI_WriteReadByte(uint8_t data)
* 输入参数:data -> 要写的一个字节数据
* 输出参数:无
* 返回值:读到的数据
* 函数作用:模拟 SPI 读写一个字节
*/
uint8_t SPI_WriteReadByte(uint8_t data)
{
   
uint8_t i = 0;
uint8_t temp = 0;
uint8_t read_data = 0xFF;
for(i=0;i<8;i++) {
   
temp = ((data&0x80)==0x80)? 1:0;
data = data<<1;
read_data = read_data<<1;
SPI_CLK(0);
SPI_MOSI(temp);
SPI_Delay();
SPI_CLK(1);
SPI_Delay();
if(SPI_MISO()==1) {
   
read_data = read_data + 1; } }
SPI_CLK(0);
return read_data;
}

至此SPI标准接口模拟程序就已完成了

三、FLASH 介绍

1、Flash物理特性:
只能写0,不能写1,也就是说,当bit=1时,此bit可以写数据改变这个bit值,当bit=0时,说明此bit有数值了,不能再写了,只能擦除后再写数据。
2、结构组成:
W25Q128 将 16M 的容量分为 256 个块(Block),每个块大小为 64K 字节,每个块又分为16 个扇区(Sector),每个扇区 4K 个字节。W25Q128 的最小擦除单位为一个扇区,也就是每次必须擦除 4K 个字节。这样我们需要给 W25Q128 开辟一个至少 4K 的缓存区,这样对 SRAM 要求比较高,要求芯片必须有 4K 以上 SRAM 才能很好的操作。
3、W25Q128 相关指令与函数
(1)初始化片选信号线

void W25QXX_CS(void)
{
   	
  GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );//PORTB时钟使能 

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;  // PB12 推挽 
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
 	GPIO_SetBits(GPIOB,GPIO_Pin_12);
  • 4
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值