spi.h文件

#ifndef __SPI_H
#define __SPI_H
#include <stdio.h>
#include"stm32f10x.h"
/**
 * -模拟SPI:是通过CPU生成波形
 *  PA4:片选信号
 *  PA5:SCK时钟信号-通用推挽
 *  PA6:MISO输入信号-通用推挽
 *  PA6:MISO输入信号-浮空输入
 *  PA7:MOSI输出信号-通用推挽
 * 
 *  SPI工作模式:模式3
 * ——极性:1 空闲状态时钟信号处于高电平
 * ——相位:1 在偶数沿采样
 */
//定义时钟宏
#define SCK_H (GPIOA->ODR |= GPIO_ODR_ODR5)
#define SCK_L (GPIOA->ODR &= ~GPIO_ODR_ODR5)
//MISO 输入,读输入的值
#define SPI_READ (GPIOA->IDR & GPIO_IDR_IDR6)
//MOSI 输出高低电平
#define SPI_H (GPIOA->ODR |= GPIO_ODR_ODR7)
#define SPI_L (GPIOA->ODR &= ~GPIO_ODR_ODR7)
//延时  构建时钟信号
#define DELAY_US5 delay_us(5)
void SPI_Init(void);
void SPI_Start(void);
void SPI_Stop(void);
uint8_t SPI_SwapByte(uint8_t);
#endif

spi.c文件

#include "spi.h"
void SPI_Init(void)
{
    // 使能GPIOA
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
    // 配置引脚工作模式
    GPIOA->CRL |= GPIO_CRL_MODE4 | GPIO_CRL_MODE5 | GPIO_CRL_MODE7;
    GPIOA->CRL &= ~(GPIO_CRL_CNF4 | GPIO_CRL_CNF5 | GPIO_CRL_CNF7);
    // 浮空输入
    GPIOA->CRL &= ~GPIO_CRL_MODE6;
    GPIOA->CRL &= ~GPIO_CRL_CNF6;
    GPIOA->CRL |= GPIO_CRL_CNF6_0;

    // 片选信号
    GPIOA->ODR |= GPIO_ODR_ODR4;
    // 因为采用模式3,所以时钟信号拉高
    GPIOA->ODR |= GPIO_ODR_ODR5;
    DELAY_US5;

}

void SPI_Start(void)
{
    GPIOA->ODR &= ~GPIO_ODR_ODR4;
}
void SPI_Stop(void)
{
    GPIOA->ODR |= GPIO_ODR_ODR4;
}
uint8_t SPI_SwapByte(uint8_t byte)
{
    for (uint8_t i = 0; i < 8; i++)
    {
        uint8_t msb = byte & 0x80;
        SCK_L;
        if (msb)
        {
            SPI_H;
        }
        else
        {
            SPI_L;
        }
        DELAY_US5;
        SCK_H;
        byte<<=1;
        if(SPI_READ){
            byte |= 0x1;
        }
        
        DELAY_US5;
    }
    return byte;
}

w25q32.h文件

#ifndef __W25Q32_H
#define __W25Q32_H

#include "stm32f10x.h"
#include <stdio.h>
#include "spi.h"
/**
 *  1.W25Q32初始化
 *      - 就是SPI的初始化
 * 
 *  2.读取设备信息
 *      - 指令:0x9F
 *      - 工厂id
 *      - 设备id
 *      - 第一交换确定指令 - 发0x9F
 *      - 第二次交换返回工厂id - 相当于是读,所以发什么不重要
 *      - 第三次交换返回设备id高8位
 *      - 第四次交换返回设备id低8位
 * 
 *  3.写使能
 *      - 指令:0x06
 *  4.写失能
 *      - 指令:0x04
 *  
 *  5.等繁忙状态-查询状态寄存器
 *      - 指令:0x05
 *      - 寄存器S0,1繁忙0空闲
 *      - 等待空闲 while()
 * 
 * 
 *  6.擦除sector段
 *      - 0X ** ** ** **
 *      - 内存地址的格式,8位的块+8位的段+8位的页+8位页内部数据地址
 *      - 发起指令前,需要发送写使能
 *      - 块、段的顺序和地址之间关系
 *          - block:15 -> 0x0Fxxxxxx
 *          - block:31 -> 0x1F
 *          - 序号 * 0x01000000 
 *              - 15 * 0x01000000 = 0x0f0000000
 *              - 15 * 0x01000000 = 0x1f0000000
 * 
 *          - sector:15 -> 0x**0F****
 * 
 * 
 *   7.写数据
 *      - 指令:0x02
 *      - 内存地址 0x** ** ** **
 *      - 写使能
 *      - 设置地址(块、段、页)
 *      - 交换数据
 *      - 写失能
 *  
 *   8.读数据
 *      - 指令:0x03
 *      - 内存地址 0x** ** ** **
 *      - 写使能
 *      - 设置地址(块、段、页)
 *      - 交换数据
 *      - 写失能
 */ 

 void W25Q32_Init(void);
 void W25Q32_ReadID(uint8_t *mid, uint16_t *did);
 void W25Q32_WriteEnable(void);
 void W25Q32_WriteDisable(void);
 void W25Q32_WaitNotBusy(void);
 void W25Q32_SectorErase(uint8_t block, uint8_t sector);
 void W25Q32_Write(uint8_t block, uint8_t sector, uint8_t page, uint8_t *data, uint16_t len);
 void W25Q32_Read(uint8_t block, uint8_t sector, uint8_t page, uint8_t *data, uint16_t len);
 
#endif

w25q32.c文件

#include "w25q32.h"


void W25Q32_Init(void) {
    SPI_Init();
}
void W25Q32_ReadID(uint8_t *mid, uint16_t *did) {
    // 启动SPI
    SPI_Start();
    // 交换数据
    // 发指令
    SPI_SwapByte(0x9F);
    // 接收工厂id
    *mid = SPI_SwapByte(0xFF);
    // 接收设备id-高位
    *did |= SPI_SwapByte(0xFF);
    *did <<= 8;
    // 接收设备id-低位
    *did |= SPI_SwapByte(0xFF);
    // 停止SPI
    SPI_Stop();
}

void W25Q32_WriteEnable(void) {
    // 启动SPI
    SPI_Start();
    // 交换数据
    // 发指令
    SPI_SwapByte(0x06);
    // 停止SPI
    SPI_Stop();
}

void W25Q32_WriteDisable(void) {
    // 启动SPI
    SPI_Start();
    // 交换数据
    // 发指令
    SPI_SwapByte(0x04);
    // 停止SPI
    SPI_Stop();
}

void W25Q32_WaitNotBusy(void) {
    // 启动SPI
    SPI_Start();
    // 交换数据
    // 发指令
    SPI_SwapByte(0x05); 
    // 状态检测
    // 只要获取的值为1则处于等待状态,0退出
    while(SPI_SwapByte(0xFF) & 0x01);
    // 停止SPI
    SPI_Stop();
}


void W25Q32_SectorErase(uint8_t block, uint8_t sector) {
    // 确定不繁忙
    W25Q32_WaitNotBusy();
    // 写使能
    W25Q32_WriteEnable();
    // 启动SPI
    SPI_Start();
    // 交换数据
    // 发指令
    SPI_SwapByte(0x20); 
    // 将序号转换为地址
    // 被擦除段的起始地址
    uint32_t addr = block * 0x01000000 + sector * 0x00010000;
    SPI_SwapByte(addr >> 24 & 0xff); // block
    SPI_SwapByte(addr >> 16 & 0xff); // sector
    SPI_SwapByte(addr >> 8 & 0xff); // page

    // 停止SPI
    SPI_Stop();
    // 写失能
    W25Q32_WriteEnable();
}


void W25Q32_Write(uint8_t block, 
    uint8_t sector, 
    uint8_t page, 
    uint8_t *data, 
    uint16_t len) {
    // 确定不繁忙
    W25Q32_WaitNotBusy();
    // 写使能
    W25Q32_WriteEnable();
    // 启动SPI
    SPI_Start();
    // 交换数据
    // 发指令
    SPI_SwapByte(0x02); 
    // 将序号转换为地址
    // 被擦除段的起始地址
    uint32_t addr = block * 0x01000000 + sector * 0x00010000 + page *0x00000100;
    SPI_SwapByte(addr >> 24 & 0xff); // block
    SPI_SwapByte(addr >> 16 & 0xff); // sector
    SPI_SwapByte(addr >> 8 & 0xff); // page
    // 交换数据
    for(uint16_t i=0; i<len; i++) {
        SPI_SwapByte(data[i]);
    }
    // 停止SPI
    SPI_Stop();
    // 写失能
    W25Q32_WriteEnable();
}


void W25Q32_Read(uint8_t block, uint8_t sector, uint8_t page, uint8_t *data, uint16_t len) {
    // 确定不繁忙
    W25Q32_WaitNotBusy();
    // 写使能
    W25Q32_WriteEnable();
    // 启动SPI
    SPI_Start();
    // 交换数据
    // 发指令
    SPI_SwapByte(0x03); 
    // 将序号转换为地址
    // 被擦除段的起始地址
    uint32_t addr = block * 0x01000000 + sector * 0x00010000 + page *0x00000100;
    SPI_SwapByte(addr >> 24 & 0xff); // block
    SPI_SwapByte(addr >> 16 & 0xff); // sector
    SPI_SwapByte(addr >> 8 & 0xff); // page
    // 交换数据
    for(uint16_t i=0; i<len; i++) {
        data[i] = SPI_SwapByte(0xFF);
    }
    // 停止SPI
    SPI_Stop();
    // 写失能
    W25Q32_WriteEnable();
}
————————————————
版权声明:本文为CSDN博主「~」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/2302_77824372/article/details/158315376

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐