STM32-笔记39-SPI-W25Q128

一、什么是SPI?

SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且 在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这 种简单易用的特性,越来越多的芯片集成了这种通信协议,比如 AT91RM9200 。

二、有I2C为什么要使用SPI

  1. IIC 是半双工通讯,无法同时收发信息;SPI 是全双工通讯,可以同时收发信息;

  2. IIC 通讯协议较复杂,而 SPI 通讯协议较简单;

  3. IIC 需要通过地址选择从机,而 SPI 只需一个引脚即可选中从机;

  4. IIC 通讯速率一般为 100kHz 左右,而 SPI 可以达到 50MHz ;

  5. IIC 需要的通讯线较少,而 SPI 需要较多。

三、SPI物理架构

SPI 总线包含 4 条通讯线,分别为 SS、SCK、MOSI、MISO。

它们的作用介绍如下 :

(1) MISO -- Master Input Slave Output,主设备数据输入,从设备数据输出

(2) MOSI -- Master Output Slave Input,主设备数据输出,从设备数据输入

(3) SCK -- Serial Clock,时钟信号,由主设备产生

(4) CS -- Chip Select,片选信号,由主设备控制 STM32F1 系列芯片有 3 个SPI 接口。

四、SPI工作模式

时钟极性(CPOL): 没有数据传输时时钟线的空闲状态电平

0:SCK在空闲状态保持低电平

1:SCK在空闲状态保持高电平

时钟相位(CPHA): 时钟线在第几个时钟边沿采样数据

0:SCK的第一(奇数)边沿进行数据位采样,数据在第一个时钟边沿被锁存

1:SCK的第二(偶数)边沿进行数据位采样,数据在第二个时钟边沿被锁存

模式 0 和模式 3 最常用。

模式 0 时序图:

模式 3 时序图:

五、SPI寄存器及库函数介绍

5.1 SPI控制寄存器 1(SPI_CR1)(I 2 S模式下不使用)

5.2 SPI控制寄存器 2(SPI_CR2)

5.3 SPI 状态寄存器(SPI_SR)

5.4 SPI 数据寄存器(SPI_DR)

5.5 库函数

HAL_SPI_Init() ;//初始化SPI函数,主要配置CR1和CR2寄存器

HAL_SPI_TransmitReceive(); //普通的收发SPI(小数据量)

HAL_SPI_TransmitReceive_DMA();//DMA搬运收发SPI(大数据量)

HAL_SPI_TransmitReceive_IT(); //中断收发IT(在中断中用)

六、什么是W25Q128?

一般我们使用存储器,都是ARM、ROM、FLASH

W25Q128是NOR Flash:一种非易失性存储器,它可以在断电或掉电后仍然保持存储的数据,因此被广泛应用于长期数据存储。它具有容量大,可重复擦写、按"扇区/块"擦除的特性。

Flash 是有一个物理特性:只能写 0 ,不能写 1 ,写 1 靠擦除。

W25Q128是华邦公司推出的一款容量为 128M-bit(相当于 16M-byte)的 SPI 接口的 NOR Flash 芯片。

6.1 W25Q128存储架构

W25Q128 将 16M 的容量分为 256 个块(block),每块 64K 字节;每块分为 16 个扇区(sector),一扇区 4K 字节;每扇区分为 16 个页(page),一页 256 字节。

W25Q128 的最小擦除单位为一个扇区,也就是每次必须擦除 4K 个字节。这样我们需要给 W25Q128 开辟一个至少 4K 的缓存区。

6.2 W25Q128常用指令

具体工作时序如下:

  • 写使能 (06H)

执行页写,扇区擦除,块擦除,片擦除,写状态寄存器等指令前,需要写使能。

拉低 CS 片选 → 发送 06H → 拉高 CS 片选

  • 读SR1(05H)

拉低 CS 片选 → 发送 05H → 返回SR1的值 → 拉高 CS 片选

  • 读数据(03H)

拉低 CS 片选 → 发送 03H → 发送24位地址 → 读取数据(1~n)→ 拉高 CS 片选

  • 页写 (02H)

页写命令最多可以向FLASH传输256个字节的数据。

拉低 CS 片选 → 发送 02H → 发送24位地址 → 发送数据(1~n)→ 拉高 CS 片选

  • 扇区擦除(20H)

写入数据前,检查内存空间是否全部都是 0xFF ,不满足需擦除。

拉低 CS 片选 → 发送 20H→ 发送24位地址 → 拉高 CS 片选

6.3 W25Q128状态寄存器

七、W25Q128实验

实验目的

读写W25Q128

复制项目文件19-串口打印功能

重命名为50-读写W25Q128实验

加载文件

main,c

cpp 复制代码
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "w25q128.h"

uint8_t data_write[4] = {0xAA, 0xBB, 0xCC, 0xDD};
uint8_t data_read[4] = {0};
int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    led_init();                         /* 初始化LED灯 */
    uart1_init(115200);
    w25q128_init();
    printf("hello world!\r\n");
    
    uint16_t device_id = w25q128_read_id();
    printf("device id: %X\r\n", device_id);

   w25q128_erase_sector(0x000000);
    w25q128_write_page(0x000000, data_write, 4);
    w25q128_read_data(0x000000, data_read, 4);
    
    printf("data read: %X, %X, %X, %X\r\n", data_read[0], data_read[1], data_read[2], data_read[3]);
    while(1)
    { 

    }
}

w25q128.c

cpp 复制代码
#include "w25q128.h"

SPI_HandleTypeDef spi_handle = {0};
void w25q128_spi_init(void)
{
    spi_handle.Instance = SPI1;//指定哪个SPI?
    spi_handle.Init.Mode = SPI_MODE_MASTER;//指定主设备还是从设备?主设备
    spi_handle.Init.Direction = SPI_DIRECTION_2LINES;//全双工还是半双工?全双工
    spi_handle.Init.DataSize = SPI_DATASIZE_8BIT;//数据长度?8byte
    spi_handle.Init.CLKPolarity = SPI_POLARITY_LOW;  极性         /* CPOL = 0 */
    spi_handle.Init.CLKPhase = SPI_PHASE_1EDGE;                  /* CPHA = 0 */
    spi_handle.Init.NSS = SPI_NSS_SOFT;
    spi_handle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;//波特率分频-256
    spi_handle.Init.FirstBit = SPI_FIRSTBIT_MSB;//指定高位先行还是低位先行:高位先行
    spi_handle.Init.TIMode = SPI_TIMODE_DISABLE;
    spi_handle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    spi_handle.Init.CRCPolynomial = 7;
    HAL_SPI_Init(&spi_handle);
}

void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
    if(hspi->Instance == SPI1)
    {
        GPIO_InitTypeDef gpio_initstruct;
        //打开时钟
        __HAL_RCC_GPIOA_CLK_ENABLE();                           // 使能GPIOB时钟
        __HAL_RCC_SPI1_CLK_ENABLE();
        
        //调用GPIO初始化函数
        gpio_initstruct.Pin = GPIO_PIN_4;          
        gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;           
        gpio_initstruct.Pull = GPIO_PULLUP;                    
        gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;          
        HAL_GPIO_Init(GPIOA, &gpio_initstruct);
        
        gpio_initstruct.Pin = GPIO_PIN_5 | GPIO_PIN_7;          
        gpio_initstruct.Mode = GPIO_MODE_AF_PP;           
        HAL_GPIO_Init(GPIOA, &gpio_initstruct);
        
        gpio_initstruct.Pin = GPIO_PIN_6;          
        gpio_initstruct.Mode = GPIO_MODE_INPUT;           
        HAL_GPIO_Init(GPIOA, &gpio_initstruct);
    }
}
//交换字节 -- 写字节操作
uint8_t w25q128_spi_swap_byte(uint8_t data)
{
    uint8_t recv_data = 0;
    HAL_SPI_TransmitReceive(&spi_handle, &data, &recv_data, 1, 1000);
    return recv_data;
}
//初始化W25Q128
void w25q128_init(void)
{
    w25q128_spi_init();
}
//读W25Q128的id
uint16_t w25q128_read_id(void)
{
    uint16_t device_id = 0;
    W25Q128_CS(0);
    
    w25q128_spi_swap_byte(FLASH_ManufactDeviceID);
    w25q128_spi_swap_byte(0x00);
    w25q128_spi_swap_byte(0x00);
    w25q128_spi_swap_byte(0x00);
    device_id = w25q128_spi_swap_byte(FLASH_DummyByte) << 8;
    device_id |= w25q128_spi_swap_byte(FLASH_DummyByte);
    
    W25Q128_CS(1);
    return device_id;
}
//W25Q128写使能
void w25q128_writ_enable(void)
{
    W25Q128_CS(0);
    w25q128_spi_swap_byte(FLASH_WriteEnable);
    W25Q128_CS(1);
}
//W25Q128读寄存器(SR1)
uint8_t w25q128_read_sr1(void)
{
    uint8_t recv_data = 0;
    
    W25Q128_CS(0);
    w25q128_spi_swap_byte(FLASH_ReadStatusReg1);
    recv_data = w25q128_spi_swap_byte(FLASH_DummyByte);
    W25Q128_CS(1);
    
    return recv_data;
}
//忙等待 - 等待空闲
void w25q128_wait_busy(void)
{
    while((w25q128_read_sr1() & 0x01) == 0x01);
}
//传入地址
void w25q128_send_address(uint32_t address)
{
    w25q128_spi_swap_byte(address >> 16);
    w25q128_spi_swap_byte(address >> 8);
    w25q128_spi_swap_byte(address);
}
//读数据
void w25q128_read_data(uint32_t address, uint8_t *data, uint32_t size)
{
    uint32_t i = 0;
    W25Q128_CS(0);
    w25q128_spi_swap_byte(FLASH_ReadData);
    w25q128_send_address(address);
    
    for(i = 0; i < size; i++)
        data[i] = w25q128_spi_swap_byte(FLASH_DummyByte);
    W25Q128_CS(1);
}
//页写
void w25q128_write_page(uint32_t address, uint8_t *data, uint16_t size)
{
    uint16_t i = 0;
    w25q128_writ_enable();
    
    W25Q128_CS(0);
    w25q128_spi_swap_byte(FLASH_PageProgram);
    w25q128_send_address(address);
    
    for(i = 0; i < size; i++)
        w25q128_spi_swap_byte(data[i]);
    
    W25Q128_CS(1);
    //等待空闲
    w25q128_wait_busy();
}
//扇区擦除
void w25q128_erase_sector(uint32_t address)
{
    //写使能
    w25q128_writ_enable();
    //等待空闲
    w25q128_wait_busy();
    //拉低片选
    W25Q128_CS(0);
    //发送扇区擦除指令
    w25q128_spi_swap_byte(FLASH_SectorErase);
    //发送地址
    w25q128_send_address(address);
    //拉高片选
    W25Q128_CS(1);
    //等待空闲
    w25q128_wait_busy();
}

w25q128.h

cpp 复制代码
#ifndef __W25Q128_H__
#define __W25Q128_H__

#include "sys.h"

#define W25Q128_CS(x)   do{ x ? \
                                HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET): \
                                HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); \
                        }while(0)

/* 指令表 */
#define FLASH_ManufactDeviceID                  0x90
#define FLASH_WriteEnable                       0x06
#define FLASH_ReadStatusReg1                    0x05
#define FLASH_ReadData                          0x03
#define FLASH_PageProgram                       0x02
#define FLASH_SectorErase                       0x20
#define FLASH_DummyByte                         0xFF

void w25q128_init(void);
uint16_t w25q128_read_id(void);
void w25q128_read_data(uint32_t address, uint8_t *data, uint32_t size);
void w25q128_write_page(uint32_t address, uint8_t *data, uint16_t size);
void w25q128_erase_sector(uint32_t address);

#endif

效果实现

FE77

0xAA, 0xBB, 0xCC, 0xDD

相关推荐
鲁子狄12 分钟前
[笔记] 使用 Jenkins 实现 CI/CD :从 GitLab 拉取 Java 项目并部署至 Windows Server
java·windows·笔记·ci/cd·ssh·gitlab·jenkins
沐欣工作室_lvyiyi6 小时前
基于物联网技术的电动车防盗系统设计(论文+源码)
单片机·嵌入式硬件·毕业设计·单片机毕业设计·nbiot
9命怪猫7 小时前
AI大模型-提示工程学习笔记5-零提示
人工智能·笔记·学习·ai·提示工程
厉昱辰7 小时前
一文读懂单片机的串口
单片机·嵌入式硬件
沐欣工作室_lvyiyi8 小时前
基于STM32的智能生态水族箱系统设计(论文+源码)
stm32·单片机·嵌入式硬件·物联网·毕业设计·水族箱
达子6668 小时前
笔记-使用ffmpeg产生rtsp视频流,然后用进行VLC播放
windows·笔记·ffmpeg
7yewh9 小时前
自制红外热像仪(二) MLX90640移植 RP2040 STM32 ESP32
驱动开发·stm32·单片机·嵌入式硬件·mcu·计算机视觉
m0_726965989 小时前
Harmony开发【笔记1】报错解决(字段名写错了。。)
笔记·bug·debug·harmonyos
冰糖雪莲IO10 小时前
【江协STM32】10-2/3 MPU6050简介、软件I2C读写MPU6050
stm32·单片机·嵌入式硬件