IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> [学习笔记]STM32F1硬件SPI读写W25Qx(寄存器、标准库、HAL库) -> 正文阅读

[嵌入式][学习笔记]STM32F1硬件SPI读写W25Qx(寄存器、标准库、HAL库)

?目录

?10. 硬件SPI读写W25Qx

0. 博主调侃:

1. 实验内容及步骤:

2. 硬件说明

3. 步骤详细讲解

?????? 3.1 RCC

?????? 3.2 配置GPIO

?????? 3.3 硬件SPI配置

?????? 3.4 发送和接收过程

4. 程序设计(寄存器)

5. 程序设计(标准库)

6. 程序设计(HAL库)

7. 实验结果

8. 源码下载


?10. 硬件SPI读写W25Qx

0. 博主调侃:

?????? 本实验使用硬件的SPI读写W25Q16JV,通过硬件SPI可减轻CPU负担。这个W25Qx通常用来存储程序,可在W25Qx内部直接运行程序,无需移动到单片机的内部RAM中,当然用来存储数据也行的。

?????? 这里W25Qx的芯片类型有点多,大体操作都相同,每种类型都有一丢丢区别,建议用哪一块就去找对应的芯片(本人就被坑了一次,啊哈哈哈哈~)。

1. 实验内容及步骤:

?????? 1. 配置GPIO,MOSI(复用推挽输出)、MISO(浮空输入)、CS(推挽输出)、SCK(复用输出);

?????? 2. 软件SPI读写Byte(模式0或模式3);

?????? 3. 读取W25Q16的制造商ID和设备ID;

?????? 4. 通过标准SPI对W25Q16擦除-写入-读取;

2. 硬件说明

?????? CS?? ->?? PB12

?????? CLK ->?? PB13

?????? MISO???? ->?? PB14

?????? MOSI???? ->?? PB15

?????? 根据STM32数据中文参考手册可知,PB12-PB15可复用为SPI2

3. 步骤详细讲解

?????? 3.1 RCC

?????? 根据STM32中文参考手册,可知SPI2挂在在APB1(36MHz)。

?????? 3.2 配置GPIO

?????? 硬件SPI的GPIO配置如下图所示。

?????? SPIx_SCK ->? 推挽复用输出

?????? SPIx_MOSI -> 推挽复用输出

?????? SPIx_MISO -> 浮空输入或带上拉输入

?????? SPIx_NSS ->? 推挽复用输出

?????? 3.3 硬件SPI配置

?????? 主要配置思路:

?????? 配置SPI2为主SPI模式,全双工通讯。根据STM32F10x中文(P462)可知配置思路如下:

?????? 配置步骤1:串行时钟波特率

?????? 已知SPI2时钟接在APB1上,时钟为(36MHz)

?????? 这里我们配置为9M,即8分频36M/8=4.5M

?????? SPI2_CR1 &= ~((u32)0x07<<3);??????? //清空配置

?????? SPI2_CR1 |= (u32)0x02<<3;????????????? //8分频

?????? 配置步骤2:选择CPOL和CPHA位

?????? W25Qx芯片的驱动为SPI模式0和SPI模式3,即:(详细可以看SPI原理讲解)

?????? 模式(CPOL,CPHA)

?????? ?????? Mode 0 CPOL=0, CPHA=0(0,0)

????????????? Mode 3 CPOL=1, CPHA=1(1,1)

?????? 在本实验使用模式3

?????? CPOL,CPHA分别由SPI_CR1寄存器的第1、0位控制。

??????? 使用模式3,(CPOL,CPHA)=(1,1)

?????? SPI2_CR1 |= (u32)0x03<<0;????????????? //模式3

?????? 配置步骤3:设置DFF位来定义8位或16位数据帧格式

?????? DFF数据帧格式由SPI_CR1寄存器的第11位控制

??????? 从SPI(W25Qx芯片)是使用8位数据帧。

?????? SPI2_CR1 &= ~((u32)0x01<<11);??????????????????? //8位数据帧

?????? 配置步骤4:配置SPI_CR1寄存器的LSBFIRST位定义帧格式

?????? LSBFIRST位是由SPI_CR1寄存器的第7位控制,即控制高位先传输还是地位先传输

??????? 从SPI(W25Qx芯片)是使用高位先数传模式(MSB)。

?????? SPI2_CR1 &= ~((u32)0x01<<7);?????????????? // MSB

?????? 配置步骤5:NSS引脚工作在软件输出

?????? 软件NSS模式:可以通过设置SPI_CR1寄存器的SSM位来使能这种模式。在这种模式下NSS引脚可以用作它用,而内部NSS信号电平可以通过写SPI_CR1的SSI位来驱动

?????? 通过SSM开启软件从设备管理,并使能内部从设备选择(否则主设备不会输出)

??? SPI2->CR1 |= (u32)1<<9;???? //软件SSM

??? SPI2->CR1 |= (u32)1<<8;???? //使能NSS?????

?????? 配置步骤6:设置MSTR位和SPE位

?????? MSTR为主设备选择由SPI_CR1寄存器的第二位控制;SPE为SPI使能由SPI_CR1寄存器的第六位控制;

??????? 配置为主设备,并使能SPI

?????? SPI_CR1 |= 0x01<<2;????????? //主设备

?????? SPI_CR1 |= 0x01<<6;????????? //使能SPI

?????? 3.4 发送和接收过程

?????? 数据发送过程

当写入数据至发送缓冲器时,发送过程开始。在发送第一个数据位时,数据字被并行地(通过内部总线)传入移位寄存器,而后串行地移出到MOSI脚上;MSB在先还是LSB在先,取决于SPI_CR1寄存器中的LSBFIRST位的设置。数据从发送缓冲器传输到移位寄存器时TXE标志将被置位,如果设置了SPI_CR1寄存器中的TXEIE位,将产生中断。

?????? 数据接收过程

对于接收器来说,当数据传输完成时:

● 传送移位寄存器里的数据到接收缓冲器,并且RXNE标志被置位。

● 如果设置了SPI_CR2寄存器中的RXNEIE位,则产生中断。

在最后采样时钟沿,RXNE位被设置,在移位寄存器中接收到的数据字被传送到接收缓冲器。读SPI_DR寄存器时,SPI设备返回接收缓冲器中的数据。读SPI_DR寄存器将清除RXNE位。

??????? 发送一个字节如上所示,先判断TEX标志位,为1(则表示发送完成)就写入DR寄存器,并等待RXNE标志位为1(则接收完成),获取DR寄存器接收的值。

????????一旦传输开始,如果下一个将发送的数据被放进了发送缓冲器,就可以维持一个连续的传输

流。在试图写发送缓冲器之前,需确认TXE标志应该为’1’。

4. 程序设计(寄存器)

?????? 程序中的W25Qx读写部分与软件SPI读写W25Qx一致,不同的是底层SPI,这里使用的是硬件SPI。

?????? 源码:这里只列出配置硬件SPI部分

//硬件GPIO配置
void hard_SPI_GPIO_Config(void)
{
    //时钟
    RCC->APB2ENR    |=  HARD_CS_RCC_CLK;   //CS时钟
    RCC->APB2ENR    |=  HARD_SCK_RCC_CLK;  //SCK时钟   
    RCC->APB2ENR    |=  HARD_MISO_RCC_CLK;   //MISO时钟
    RCC->APB2ENR    |=  HARD_MOSI_RCC_CLK;   //MOSI时钟   

    //CS 推挽复用输出
    GPIOX_PIN_SetMode(HARD_CS_GPIOX,HARD_CS_PIN_NUM,GPIOx_PP|GPIOx_OUT_10M);  //推挽输出、速度10MHz       
    //SCK 推挽复用输出
    GPIOX_PIN_SetMode(HARD_SCK_GPIOX,HARD_SCK_PIN_NUM,GPIOx_AF_PP|GPIOx_OUT_10M);    //推挽输出、速度10MHz      
    //MISO 浮空输入或带上拉输入
    GPIOX_PIN_SetMode(HARD_MISO_GPIOX,HARD_MISO_PIN_NUM,GPIOx_IN);   //输入 浮空模式
    //MOSI 推挽复用输出
    GPIOX_PIN_SetMode(HARD_MOSI_GPIOX,HARD_MOSI_PIN_NUM,GPIOx_AF_PP|GPIOx_OUT_10M);  //推挽输出、速度10MHz  
}


//硬件SPI配置
void hard_SPI_Config(void)
{
    //时钟
    RCC->APB1ENR    |=  RCC_APB1Periph_SPI2;   //SPI2时钟    
    
    //配置步骤1:波特率 8分频  36M/8=4.5M
	SPI2->CR1 &= ~((u32)0x07<<3);		//清空配置
	SPI2->CR1 |= (u32)0x02<<3;			//8分频
    
    //配置步骤2:选择CPOL和CPHA位
    SPI2->CR1 |= (u32)0x03<<0;			//模式3
    
    //配置步骤3:设置DFF位来定义8位或16位数据帧格式
    SPI2->CR1 &= ~((u32)0x01<<11);		//8位数据帧
    
    //配置步骤4:配置SPI_CR1寄存器的LSBFIRST位定义帧格式
    SPI2->CR1 &= ~((u32)0x01<<7);			// MSB
    
    //配置步骤5:NSS引脚软件输出
    HARD_CS_OUT = 1;
    
    SPI2->CR1 |= (u32)1<<9;     //软件SSM
    SPI2->CR1 |= (u32)1<<8;     //使能NSS
    //SPI2->CR1 &= ~((u32)0x01<<8);
    //	配置步骤6:设置MSTR位和SPE位
	SPI2->CR1 |= 0x01<<2;		//主设备
	SPI2->CR1 |= 0x01<<6;		//使能SPI
   
}

5. 程序设计(标准库)

?????? 标准库中与寄存器库的最大不同点在于:

?????? 1. GPIO使用GPIO_Init函数进行初始化

?????? 2. SPI使用结构体,并分别使用:

????????????? SPI_Init函数:初始化

????????????? SPI_NSSInternalSoftwareConfig函数:软件使能NSS

????????????? SPI_Cmd函数:使能SPI

?????? 源码:这里只列出配置硬件SPI配置部分

//硬件GPIO配置
void hard_SPI_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    //时钟
    RCC_APB2PeriphClockCmd(HARD_CS_RCC_CLK,ENABLE);     //CS时钟
    RCC_APB2PeriphClockCmd(HARD_SCK_RCC_CLK,ENABLE);    //SCK时钟   
    RCC_APB2PeriphClockCmd(HARD_MISO_RCC_CLK,ENABLE);   //MISO时钟
    RCC_APB2PeriphClockCmd(HARD_MOSI_RCC_CLK,ENABLE);   //MOSI时钟

    //CS 推挽输出
    GPIO_InitStruct.GPIO_Mode   =   HARD_CS_Mode;
    GPIO_InitStruct.GPIO_Pin    =   HARD_CS_PIN;
    GPIO_InitStruct.GPIO_Speed  =   GPIO_Speed_10MHz;
    GPIO_Init(HARD_CS_GPIOX,&GPIO_InitStruct);  
    //SCK(推挽输出、速度10MHz)
    GPIO_InitStruct.GPIO_Mode   =   HARD_SCK_Mode;
    GPIO_InitStruct.GPIO_Pin    =   HARD_SCK_PIN;
    GPIO_InitStruct.GPIO_Speed  =   GPIO_Speed_10MHz;
    GPIO_Init(HARD_SCK_GPIOX,&GPIO_InitStruct);     
    //MISO(输入 浮空模式)
    GPIO_InitStruct.GPIO_Mode   =   HARD_MISO_Mode;
    GPIO_InitStruct.GPIO_Pin    =   HARD_MISO_PIN;
    GPIO_Init(HARD_MISO_GPIOX,&GPIO_InitStruct);     
    //MOSI(推挽输出、速度10MHz)
    GPIO_InitStruct.GPIO_Mode   =   HARD_MOSI_Mode;
    GPIO_InitStruct.GPIO_Pin    =   HARD_MOSI_PIN;
    GPIO_InitStruct.GPIO_Speed  =   GPIO_Speed_10MHz;
    GPIO_Init(HARD_MOSI_GPIOX,&GPIO_InitStruct);   
    
}


//硬件SPI配置
void hard_SPI_Config(void)
{
    SPI_InitTypeDef SPI_InitStruct;
    //时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);     //SPI2时钟
    
    //配置步骤1:波特率 8分频  36M/8=4.5M
    SPI_InitStruct.SPI_BaudRatePrescaler    =   SPI_BaudRatePrescaler_8;
    //配置步骤2:选择CPOL和CPHA位(模式3)
    SPI_InitStruct.SPI_CPHA     =   SPI_CPHA_2Edge;
    SPI_InitStruct.SPI_CPOL     =   SPI_CPOL_High;
    //配置步骤3:设置DFF位来定义8位或16位数据帧格式
    SPI_InitStruct.SPI_DataSize =   SPI_DataSize_8b;
    //配置步骤4:配置SPI_CR1寄存器的LSBFIRST位定义帧格式
    SPI_InitStruct.SPI_FirstBit =   SPI_FirstBit_MSB;
    //配置步骤5:NSS引脚软件输出
    SPI_InitStruct.SPI_NSS      =   SPI_NSS_Soft;
    //配置步骤6:设置MSTR位和SPE位
    SPI_InitStruct.SPI_Direction    =   SPI_Direction_2Lines_FullDuplex;
    SPI_InitStruct.SPI_Mode     =   SPI_Mode_Master;
    //SPI初始化  
    SPI_Init(SPI2,&SPI_InitStruct);
    //软件使能NSS
    SPI_NSSInternalSoftwareConfig(SPI2,SPI_NSSInternalSoft_Set);
    //使能SPI
    SPI_Cmd(SPI2,ENABLE);
    
    HARD_CS_OUT = 1;
    
}


//发送一个字节数据
u8 hard_SPI_Byte(u8 data)
{
    while(!(SPI2->SR&SPI_I2S_FLAG_TXE));
    SPI2->DR = data;
    while(!(SPI2->SR&SPI_I2S_FLAG_RXNE));
    return SPI2->DR;
}

6. 程序设计(HAL库)

?????? HAL库与寄存器库的最大不同点在于:

?????? 1. 时钟使用__HAL_RCC_xxxx_CLK_ENABLE函数配置;

?????? 2. ?GPIO使用HAL_GPIO_Init函数进行初始化;

?????? 3. ?? 硬件SPI使用HAL_SPI_Init函数配置;

????????????? 使用__HAL_SPI_ENABLE函数使能SPI;

?????? 4. 使用__HAL_SPI_GET_FLAG函数获取发送完成、接收完成标志位。

?????? 源码:这里只列出配置硬件SPI配置部分

//SPI结构体
 SPI_HandleTypeDef HSPIStruct;
 
//硬件GPIO配置
void hard_SPI_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    //时钟
    HARD_CS_CLK_ENABLE();       //CS时钟
    HARD_SCK_CLK_ENABLE();      //SCK时钟   
    HARD_MISO_CLK_ENABLE();       //MISO时钟
    HARD_MOSI_CLK_ENABLE();       //MOSI时钟  
    
    //CS(推挽输出、速度10MHz)
    GPIO_InitStruct.Mode    =   HARD_CS_Mode;
    GPIO_InitStruct.Pin     =   HARD_CS_PIN;
    GPIO_InitStruct.Speed   =   GPIO_SPEED_FREQ_MEDIUM;
    HAL_GPIO_Init(HARD_CS_GPIOX,&GPIO_InitStruct);      
    //SCK(复用推挽输出、速度10MHz)
    GPIO_InitStruct.Mode    =   HARD_SCK_Mode;
    GPIO_InitStruct.Pin     =   HARD_SCK_PIN;
    GPIO_InitStruct.Speed   =   GPIO_SPEED_FREQ_MEDIUM;
    HAL_GPIO_Init(HARD_SCK_GPIOX,&GPIO_InitStruct);    
    //MISO(输入 浮空模式)
    GPIO_InitStruct.Mode    =   HARD_MISO_Mode;
    GPIO_InitStruct.Pin     =   HARD_MISO_PIN;
    GPIO_InitStruct.Pull    =   GPIO_NOPULL;
    HAL_GPIO_Init(HARD_MOSI_GPIOX,&GPIO_InitStruct);      
    //MOSI(复用推挽输出、速度10MHz)
    GPIO_InitStruct.Mode    =   HARD_MOSI_Mode;
    GPIO_InitStruct.Pin     =   HARD_MOSI_PIN;
    GPIO_InitStruct.Speed   =   GPIO_SPEED_FREQ_MEDIUM;
    HAL_GPIO_Init(HARD_MOSI_GPIOX,&GPIO_InitStruct); 
    
}


//硬件SPI配置
void hard_SPI_Config(void)
{

    //时钟
    __HAL_RCC_SPI2_CLK_ENABLE();   //SPI2时钟    
    
    HSPIStruct.Instance = SPI2;
    //配置步骤1:波特率 8分频  36M/8=4.5M
    HSPIStruct.Init.BaudRatePrescaler     =     SPI_BAUDRATEPRESCALER_8;    
    //配置步骤2:选择CPOL和CPHA位
    HSPIStruct.Init.CLKPolarity =   SPI_POLARITY_HIGH;
    HSPIStruct.Init.CLKPhase    =   SPI_PHASE_2EDGE;
    //配置步骤3:设置DFF位来定义8位或16位数据帧格式
    HSPIStruct.Init.DataSize    =   SPI_DATASIZE_8BIT;
    //配置步骤4:配置SPI_CR1寄存器的LSBFIRST位定义帧格式
    HSPIStruct.Init.FirstBit    =   SPI_FIRSTBIT_MSB;
    //配置步骤5:NSS引脚软件输出
    HSPIStruct.Init.NSS     =   SPI_NSS_SOFT;
    //	配置步骤6:设置MSTR位和SPE位
    HSPIStruct.Init.Direction   =   SPI_DIRECTION_2LINES;
    HSPIStruct.Init.Mode        =   SPI_MODE_MASTER;
    //禁止校验
    HSPIStruct.Init.CRCCalculation  =   SPI_CRCCALCULATION_DISABLE;
    //硬件SPI初始化
    HAL_SPI_Init(&HSPIStruct);

    //使能SPI
    __HAL_SPI_ENABLE(&HSPIStruct);
    
    HARD_CS_OUT = 1;
}


//发送一个字节数据
u8 hard_SPI_Byte(u8 data)
{
    while(!__HAL_SPI_GET_FLAG(&HSPIStruct, SPI_FLAG_TXE));
    SPI2->DR = data;
    while(!__HAL_SPI_GET_FLAG(&HSPIStruct, SPI_FLAG_RXNE));
    return SPI2->DR;
}

7. 实验结果

?????? 第一部分先进行整体擦除,再进行写入(存储,读取正常)

?????? 第二部分边擦除边写入,(存储,读取正常)

?

8. 源码下载

????????源码下载

?

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-07-14 00:22:40  更:2021-07-14 00:22:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 19:43:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码