[其他] 基于MM32F0140的SPI与W25Q80通信

[复制链接]
877|0
 楼主| MindMotion 发表于 2023-9-26 09:05 | 显示全部楼层 |阅读模式
本帖最后由 MindMotion 于 2023-10-10 10:35 编辑

前提

查看MM32F0140的MiniBoard原理图,SPI挂载了W25Q80。

344806510fb38e519c.png

一、SPI介绍

串行外设接口,Serial Peripheral Interface。一种高速的、全双工、同步的串行通信总线。主要应用于EEPROM、FLASH、AD转换器,还有数字信号处理器和数字信号解码器之间。

SPI 支持同时发送和接收数据,采用主从工作方式。SPI 通过 4 个引脚与外部器件相连,分别是:

  • MOSI:主设备输出、从设备输入引脚。传输方向为主设备发送到从设备。
  • MISO:主设备输入、从设备输出引脚。传输方向为从设备发送到主设备。
  • SCK:串行时钟信号,由主设备产生并提供给从设备。
  • NSS:片选信号,从设备选择。


下图示意主、从设备之间一对一通信时的连接情况:

826056510fb746c4f8.png

主设备负责发起通信请求,从设备负责响应,从设备通过 SCK 引脚得到主设备提供的时钟信号,从而使得主、从设备均使用同一个时钟进行同步的全双工通信。对于从设备而言,MOSI 引脚输入来自主设备的发送数据,MISO 引脚输出响应数据传给主设备。

SPI 需要根据从机特性,选配时钟极性(CPOL)和时钟相位(CPHA),比如从机时钟空闲时为低电平,且在时钟信号的第一个跳变沿采样,此时时序见下图:

918666510fb972f4ae.png

二、W25Q80介绍

W25Q80是一种串行闪存存储器,主要特点包括:

  • 容量:8 Mbit(1 MB)的存储容量,可以存储大量数据。
  • SPI接口:采用SPI接口进行数据通信,具备高速的数据传输能力。
  • 快速读取:支持快速的连续读取操作,可提供高效的读取性能。
  • 块擦除:支持块擦除功能,可以批量擦除数据,提高擦除效率。
  • 低功耗:采用低功耗设计,适用于对功耗要求较高的应用场景。

W25Q80可以划分为 4096 个扇区,每个扇区包含 256 个页,每个页的大小为 256 字节。

下图为W25Q80的指令:

84426510fbc1483b8.png

三、例程

1.介绍

本例程实现SPI与W25Q80通信,SPI查询W25Q80的设备号,并全片擦除后写入数据,再读取出来,看写入的数据与读取的数据是否一致。

2.编码详情

(1)初始化配置SPI,速度为PCLK1(36MHz)的32分频,约为1.125MHz;
  1. SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
  2.     SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
  3.     SPI_InitStructure.SPI_DataWidth = 8;
  4.     SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
  5.     SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
  6.     SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
  7.     SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;
  8.     SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
  9.     SPI_Init(SPI2, &SPI_InitStructure);
  10.     exSPI_DataEdgeAdjust(SPI2, SPI_DataEdgeAdjust_FAST);
  11.     SPI_BiDirectionalLineConfig(SPI2, SPI_Direction_Tx);
  12.     SPI_BiDirectionalLineConfig(SPI2, SPI_Direction_Rx);
  13. SPI_Cmd(SPI2, ENABLE);

(2)编写SPI发送和读取数据的函数;
  1. uint32_t writeAndReadData(uint8_t data)
  2. {
  3.     SPI_SendData(SPI2, data);
  4.     while (1) {
  5.         if(SPI_GetFlagStatus(SPI2, SPI_FLAG_TXEPT)) {
  6.             break;
  7.         }
  8.     }
  9.     while (1) {
  10.         if(SPI_GetFlagStatus(SPI2, SPI_FLAG_RXAVL)) {
  11.             return SPI_ReceiveData(SPI2);
  12.         }
  13.     }
  14. }

(3)获取W25Q80的设备ID;
  1. SPI_CSInternalSelected(SPI2, ENABLE); // 片选
  2.     writeAndReadData(0x9F); // 读ID指令
  3.     ID |= writeAndReadData(0x00) << 16;
  4.     ID |= writeAndReadData(0x00) << 8;
  5.     ID |= writeAndReadData(0x00);
  6.     SPI_CSInternalSelected(SPI2, DISABLE); // 取消片选
  7.     printf("\n\nread device id: %X", ID);

(4)全片擦除W25Q80,并检查状态等待擦除完成;
  1. SPI_CSInternalSelected(SPI2, ENABLE);
  2.     writeAndReadData(0x06); // 写使能
  3.     SPI_CSInternalSelected(SPI2, DISABLE);
  4.   
  5.     SPI_CSInternalSelected(SPI2, ENABLE);
  6.     writeAndReadData(0xC7); // 全片擦除指令
  7.     SPI_CSInternalSelected(SPI2, DISABLE);
  8.    
  9.     SPI_CSInternalSelected(SPI2, ENABLE);
  10.     writeAndReadData(0x05); // 读状态寄存器
  11.     while(1) {
  12.         temp = writeAndReadData(0x00);
  13.         if((temp & 0x01) == 0x0) // 擦除完成
  14.             break;
  15.     }
  16.     SPI_CSInternalSelected(SPI2, DISABLE);
  17. printf("\n\nerase complete");

(5)写数据,并检查状态等待写完;
  1. for (i = 0; i < 256; i++) txData[i] = i;
  2.     SPI_CSInternalSelected(SPI2, ENABLE);
  3.     writeAndReadData(0x06);// 写使能
  4.     SPI_CSInternalSelected(SPI2, DISABLE);
  5.    
  6.     SPI_CSInternalSelected(SPI2, ENABLE);
  7.     writeAndReadData(0x02); // 页编程
  8.     writeAndReadData(0x00); // 页地址
  9.     writeAndReadData(0x00);
  10.     writeAndReadData(0x00);
  11.     for (i = 0; i < 256; i++)
  12.     {
  13.         writeAndReadData(txData[i]);
  14.     }
  15.     SPI_CSInternalSelected(SPI2, DISABLE);
  16.    
  17.     SPI_CSInternalSelected(SPI2, ENABLE);
  18.     writeAndReadData(0x05); // 读状态寄存器
  19.     while(1) {
  20.         temp = writeAndReadData(0x00);
  21.         if((temp & 0x01) == 0x0) // 页编程完成
  22.             break;
  23.     }
  24.     SPI_CSInternalSelected(SPI2, DISABLE);
  25.     printf("\n\npage programm complete");

(6)读数据,并打印出来。
  1.     SPI_CSInternalSelected(SPI2, ENABLE);
  2.     writeAndReadData(0x03); // 读数据
  3.     writeAndReadData(0x00); // 页地址
  4.     writeAndReadData(0x00);
  5.     writeAndReadData(0x00);
  6.     for (i = 0; i < 10; i++)
  7.     {
  8.         rxData[i] = writeAndReadData(0x00);
  9.     }
  10.     SPI_CSInternalSelected(SPI2, DISABLE);
  11.     printf("\n\nread data completely\n");
  12.     for (i = 0; i < 10; i++)
  13.         printf("\nrxDate[%d] = %d", i, rxData[i]);

3.串口输出结果

下图为串口输出数据,基本符合程序流程和结果。

385956510fc92e89ca.png

4.逻辑分析仪

下图为逻辑分析仪截取的发送接收图片,可以清楚的看到当前SCK速度接近1.125MHz,以及发送数据也符合预期。

477516510fcb2ca346.png
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:上海灵动微电子股份有限公司
简介:上海灵动微电子股份有限公司成立于 2011 年,是中国本土通用 32 位 MCU 产品及解决方案供应商。 灵动股份的 MCU 产品以 MM32 为标识,基于 Arm Cortex-M 系列内核,自主研发软硬件和生态系统。目前已量产近 300 多款型号,累计交付超 4 亿颗,在本土通用 32 位 MCU 公司中位居前列。

93

主题

111

帖子

10

粉丝
快速回复 在线客服 返回列表 返回顶部