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

相关推荐
情意绵绵67410 分钟前
车用CAN接口芯片:汽车神经系统的沉默构建者
单片机·嵌入式硬件·汽车·硬件架构·硬件工程
愚润求学11 分钟前
【Linux】动静态库的使用
linux·运维·服务器·开发语言·c++·笔记
weixin_4481199411 分钟前
Datawhale 5月llm-universe 第2次笔记
笔记
Dovis(誓平步青云)34 分钟前
基于面向对象设计的C++日期推算引擎:精准高效的时间运算实现与运算重载工程化实践
开发语言·c++·经验分享·笔记
寂空_39 分钟前
【算法笔记】ACM数论基础模板
c++·笔记·算法
xu_wenming2 小时前
华为Watch的ECG功能技术分析
人工智能·嵌入式硬件·算法
ALINX技术博客2 小时前
【ALINX 实战笔记】FPGA 大神 Adam Taylor 使用 ChipScope 调试 AMD Versal 设计
笔记·fpga开发
关山煮酒2 小时前
【数据挖掘笔记】兴趣度度量Interest of an association rule
笔记·数据挖掘
欢乐熊嵌入式编程2 小时前
智能手表集成测试报告(Integration Test Report)
嵌入式硬件·物联网·目标跟踪·集成测试·智能手表
我不是帅戈2 小时前
STM32单片机内存分配详细讲解
stm32·单片机·嵌入式·内存管理·.map文件