仅需一个 HAL 库函数:轻松实现 STM32 的 SPI 编程(以 Flash 25Q128 为例)

 主要介绍如何用HAL_SPI_TransmitReceive()函数实现对W25Q128 Flash存储器ID的读取。先介绍SPI是一种高速且简单的同步串行接口技术,由四根线((MOSI、MISO、SCLK和SS/CS))组成。接着介绍Flash ,它是串行闪存芯片,能提供更大存储容量。还提到STM32 HAL库简化了SPI编程,以正点原子精英V2开发板为例,给出开发环境及函数原型和参数。最后展示读取ID的代码示例,通过发送命令、接收数据并判断状态来获取ID。 

一、SPI介绍

百科上解释,SPI(Serial Peripheral Interface一一串行外设接口)总线是Motorola公司推出的一种同步串行接口技术。SPI通讯其实就是设备之间互相“唠嗑”、传数据的一种方式。就好比在餐厅里,顾客、服务员和厨房之间得交流,才能顺利地把饭吃上。有些在电子设备里,主机和从机就靠着SPI通讯。广泛应用:常用于传感器、存储芯片(如W25Q64)、显示器等外设与主控芯片之间的通信。

1.为啥要用SPI?

高速传输:比其他一些通信协议(如I2C)更快。

简单直接:只需要4根线就能实现设备间的通信。

2.SPI的基本组成

四根线搞定,SPI通信只需要四根线,分别是:

MOSI (Master Out Slave In):主机发送数据给从机。

MISO (Master In Slave Out):主机接收从机的数据。

SCLK (Serial Clock):时钟信号,控制数据传输的节奏。

SS/CS (Slave Select/Chip Select):选择要通信的从设备。

3.餐馆点餐吃饭类比SPI

为了让你更好地理解这些概念,我们用一个大家都熟悉的场景- 餐馆点餐吃饭.

顾客点菜(MOSI):你告诉服务员你想点什么菜(发送命令和地址)。
服务员送餐(MISO):服务员将你点的菜送到桌上(返回数据)。
服务流程时间表(SCK):餐厅有一套严格的时间表,确保每个步骤都能按时完成(时钟信号控制数据传输节奏)。
顾客呼叫特定服务员(SS/CS):当你按下餐桌上的呼叫按钮时,只有负责这张桌子的服务员会过来服务(选择从设备进行通信)。

4.SPI时序图

主机通过拉低 CS 选中从机,SCK 提供时钟信号确定节奏。主机经 MOSI 发数据,从机经 MISO 回传。按时钟信号在特定边沿收发数据,完成一次通讯后主机拉高 CS 结束。

二、 Flash 介绍

1.什么是Flash串行闪存芯片?

Flash串行闪存芯片是一种非易失性存储器,即使在断电的情况下也能保存数据。它们通过 SPI(Serial Peripheral Interface) 接口与主控设备进行通信。相比于并行闪存,串行闪存具有引脚数少、封装小、成本低等优点。W25Q64和W25Q128都是常见的串行闪存芯片。它就是单片机的 “小仓库”,可以把程序、数据啥的存进去,要用的时候再取出来,就像咱们把东西存进仓库,要用的时候再去拿。

2.为啥需要Flash?

尽管单片机有内置Flash,但外置提供更大存储容量,适合大数据存储,大多数单片机的内置 Flash 容量相对较小(例如,STM32F103C8T6 只有 64 KB 或 128 KB)。而 W25Q64 提供了 8 MB 的存储空间。它还能分离存储,保护主程序代码,避免干扰和误操作,提升系统稳定性和灵活性。

3.Flash的类比

Flash可类比为电脑硬盘与U盘。电脑硬盘长期存储操作系统、软件、文档等多样数据,Flash在嵌入式系统里能存程序代码、传感器数据。Flash类似一个巨大的字节数组,支持按地址进行数据增删改查操作(CRUD:Create, Read, Update, Delete)。增(写入)数据需按页对齐,删(擦除)以扇区或块为单位,改(更新)需先擦除再写入,查(读取)则直接按地址读取。

4.Flash芯片举例

4.1 W25Q64 是由 Winbond(华邦电子) 生产的一款 SPI NOR Flash 存储器。

W25Q64 容量为 64 兆比特(Megabit, Mbit),W25Q的"64"是Mbit ,换算后为 8 兆字节(MB),即 8×1024×1024 字节(Byte),选型的时候要注意的。包含 128 个块、2048 个扇区或 32768 个页。

4.1 NM25Q128EVB容量

NM25Q128EVB容量为 128 兆比特(Mbit),W25Q的"128"是Mbit ,换算后为 16 兆字节(MB),即 16×1024×1024 字节(Byte),是 W25Q64 容量的两倍,因此它包含 256 个块、4096 个扇区或 65536 个页。读 ID(Manufacturer ID)的命令是0x90,其默认值是0x52.

5.命令统计

W25Q64和NM25Q128EVB都通用的.

操作

命令

读 ID

0x90

读数据

0x03

写使能

0x06

读状态

0x05或0x35 或 0x15

擦除扇区

0x20

三、SPI编程

1.STM32 HAL库

以前SPI编程可麻烦了,各种底层操作让人眼花缭乱。但有了HAL库函数就不一样了,它就像贴心的餐厅服务生,对服务流程门儿清。你去餐厅点餐,只要告诉服务生想吃啥,后面的备餐、上菜他都能搞定。同样,在SPI通信里,你只要跟HAL库说清楚数据发送和接收的需求,像初始化、参数设置这些复杂细节,它都帮你处理,你就能专心搞数据传输。通过SPI接口,STM32和Flash之间的数据传输就跟顾客和服务生交流一样,简单又高效 。

2.开发环境

硬件:正点原子精英版 V2 STM32F103开发板

单片机:STM32F103ZET6

Keil版本:5.32

STM32CubeMX版本:6.9.2

STM32Cube MCU Packges版本:STM32F1xx_DFP.2.4.1
串口:USART1(PA9,PA10)

SPI2:

PB12 SPI2_CS

PB13 SPI2_SCK

PB14 SPI2_MISO

PB15 SPI2_MOSI
Flash型号:NM25Q128EVB

3.一个 HAL 库函数搞定 SPI 编程

函数原型

HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, const uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout)//同时收发数据

参数

说明

SPI_HandleTypeDef *hspi

哪个SPI控制器

uint8_t *pTxData

要发送的数据的 buffer

uint8_t *pRxData

存储接收到的数据是 buffer

uint16_t Size

数据个数

uint32_t Timeout

超时时间,单位是 Tick,一般是 1ms

返回值

HAL_OK:成功

HAL_ERROR:错误

HAL_BUSY:总线忙

HAL_TIMEOUT:超时

 4.配置STM32CubeMX

4.1.启动STM32CubeMX,新建STM32CubeMX项目

4.2.选择MCU:在软件中选择你的STM32型号-STM32F103ZET6。

4.3.选择时钟源:

4.4.配置时钟:

​4.5.使能Debug功能:Serial Wire

4.​6.HAL库时基选择:SysTick

4.7.USART1配置:选择异步模式,使能中断。

4.8.开启SPI2
4.9.配置工程参数:在Project标签页中,配置项目名称和位置,选择工具链MDK-ARM。4.10.生成代码:在Code Generator标签页中,配置工程外设文件与HAL库,勾选头文件.c和.h文件分开,然后点击Project > Generate Code生成代码。 

5.代码示例

具体源码请查看附件.

/**
 * @brief 读取W25Q128 Flash存储器的设备ID
 * @note  使用HAL库函数 HAL_SPI_TransmitReceive 来发送读取ID命令并接收响应
 * @retval 返回制造商ID(Manufacturer ID)
 */
int SPIFlash_25QX_ReadID(void)
{
    // 发送缓冲区,包含读取设备ID的命令 0x9F 和一个填充字节 0xFF
    uint8_t txbuf[2] = {0x9F, 0xff};
    
    // 接收缓冲区,用于存储从W25Q128接收到的设备ID信息
    uint8_t rxbuf[2] = {0, 0};

    // 打开片选引脚(CS/SS),告诉W25Q128我们即将开始通信
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);  // 拉低片选引脚

    // 发送读取设备ID的命令,并接收设备ID信息
    // 参数解释:
    // &hspi2: SPI句柄
    // txbuf: 发送缓冲区
    // rxbuf: 接收缓冲区
    // 2: 发送和接收的数据长度(以字节为单位)
    // 10: 超时时间(毫秒)
    HAL_StatusTypeDef status = HAL_SPI_TransmitReceive(&hspi2, txbuf, rxbuf, 2, 10);

    // 关闭片选引脚(CS/SS),结束与W25Q128的通信
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);  // 拉高片选引脚

    // 检查传输状态是否成功
    if (status == HAL_OK)
    {
        // 返回制造商ID(Manufacturer ID),位于rxbuf[1]
        return rxbuf[1];
    }
    else
    {
        // 如果传输失败,返回错误码 -1
        return -1;
    }
}
6.运行结果

编译烧写后,打开串口助手,接收到数据0x52,与芯片手册的一致.

六、总结

本文不仅涵盖了SPI和Flash存储器的基础知识,还展示了如何利用HAL库简化SPI编程,为嵌入式开发人员提供了一个清晰且实用的指南。希望这篇文章能帮助你轻松上手STM32的SPI编程,并在实际项目中应用自如。仅供参考,有任何问题,欢迎在评论区留言讨论!

作者:jmlinux

物联沃分享整理
物联沃-IOTWORD物联网 » 仅需一个 HAL 库函数:轻松实现 STM32 的 SPI 编程(以 Flash 25Q128 为例)

发表回复