STM32 进阶SPI案例1:软件模拟SPI读写FLASH

需求描述

基于寄存器操作,使用软件模拟SPI协议,完成读写FLASH。

硬件电路设计

寄存器代码书写

main.c

cs 复制代码
#include "usart1.h"
#include "string.h"
#include <stdio.h>
#include "m24c02.h"
#include "soft_spi.h"
#include "W25Q32.h"
​
int main(void){
    Usart1_Init();
    W25Q32_Init();
    uint8_t buffer[1001] = {0};
    W25Q32_EraseSetor(0x00,0x00);
    W25Q32_PageWrite(0x00,0x00,0x00,0x00,"abcde",5);
    W25Q32_ReadData(0x00,0x00,0x00,0x00,buffer,1000);
    printf("%s\n",buffer);
    while (1)
    {
    }
        
}

soft_spi.h

cs 复制代码
#ifndef __SOFT_SPI_H__
#define __SOFT_SPI_H__
​
#include "stm32f10x.h"
​
#define SCK_HIGH (GPIOA->ODR |= GPIO_ODR_ODR5)
#define SCK_LOW (GPIOA->ODR &= ~GPIO_ODR_ODR5)
​
#define MOSI_HIGH (GPIOA->ODR |= GPIO_ODR_ODR7)
#define MOSI_LOW (GPIOA->ODR &= ~GPIO_ODR_ODR7)
​
#define CS_HIGH (GPIOA->ODR |= GPIO_ODR_ODR13)
#define CS_LOW (GPIOA->ODR &= ~GPIO_ODR_ODR13)
​
#define MISO_READ (GPIOA->IDR & GPIO_IDR_IDR6)
​
void SPI_Init(void);
​
void SPI_Start(void);
​
void SPI_Stop(void);
​
uint8_t SPI_SwapByte(uint8_t byte);
​
#endif /* __SOFT_SPI_H__ */
​

soft_spi.c

cs 复制代码
#include "soft_spi.h"
​
void SPI_Init(void)
{
    // 1. GPIOA
    // 1.1 先放时钟
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
​
    // new * 模式0
    SCK_LOW;
​
    // 1.2 PA5 时钟 通用推挽
    GPIOA->CRL &= ~GPIO_CRL_CNF5;
    GPIOA->CRL |= GPIO_CRL_MODE5;
    // 1.3 PA6 输入信号 浮空输入
    GPIOA->CRL &= ~GPIO_CRL_CNF6_1;
    GPIOA->CRL |= GPIO_CRL_CNF6_0;
    GPIOA->CRL &= ~GPIO_CRL_MODE6;
    // 1.4 PA7 数据输出 通用推挽
    GPIOA->CRL &= ~GPIO_CRL_CNF7;
    GPIOA->CRL |= GPIO_CRL_MODE7;
​
    // 2. GPIOC
    // 2.1 放GPIOC的时钟
    RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
    // new * 片选拉高 表示 不通讯
    CS_HIGH;
​
    // 2.2. PC13 片选使能信号 通用推挽
    GPIOC->CRH &= ~GPIO_CRH_CNF13;
    GPIOC->CRH |= GPIO_CRH_MODE13;
}
​
void SPI_Start(void)
{
    CS_LOW;
}
​
void SPI_Stop(void)
{
    CS_HIGH;
}
​
// mode0 上升沿的时候读数据 下降沿的时候发数据
uint8_t SPI_SwapByte(uint8_t byte)
{
​
    uint8_t receive_byte = 0;
    for (uint8_t i = 0; i < 8; i++)
    {
        // for循环里面 是不是处理的bit
        // 1. 上升沿之前 先把MO的数据偷偷放好
        if (byte & 0x80)
        {
            MOSI_HIGH;
        }
        else
        {
            MOSI_LOW;
        }
        // 2. 一个bit发完之后 左移一下
        byte <<= 1;
        // 3. 拉高一下 创造一个上升沿
        SCK_HIGH;
        receive_byte <<= 1;
        if (MISO_READ)
        {
            receive_byte |= 1;
        }
​
        // 4. 拉低一下 让时钟进入空闲 再偷着读数据
        SCK_LOW;
        // !! 此处违反了协议 因为协议要求 高电平时数据交换
    }
​
    return receive_byte;
}

W25Q32.h

cs 复制代码
#ifndef __W25Q32_H__
#define __W25Q32_H__
​
#include "soft_spi.h"
​
void W25Q32_Init(void);
​
void W25Q32_JEDECID(uint8_t * mfid,uint8_t * memType,uint8_t * cap);
​
void W25Q32_WriteEnable(void);
​
void W25Q32_WriteDisable(void);
​
uint8_t w25Q32_ReadStatus(void);
​
void W25Q32_PageWrite(
    uint8_t block_addr,
    uint8_t sector_addr,
    uint8_t page_addr,
    uint8_t page_inner_addr,
    uint8_t * pData,
    uint8_t len
);
​
​
void W25Q32_EraseSetor(
    uint8_t block_addr,
    uint8_t sector_addr
);
​
​
void W25Q32_ReadData(
    uint8_t block_addr,
    uint8_t sector_addr,
    uint8_t page_addr,
    uint8_t page_inner_addr,
    uint8_t * pData,
    uint8_t len
);
#endif /* __W25Q32_H__ */

W25Q32.c

cs 复制代码
#include "W25Q32.h"
​
#define DUMMY 0xff
​
void W25Q32_Init(void){
    SPI_Init();
}
​
void W25Q32_JEDECID(uint8_t * mfid,uint8_t * memType,uint8_t * cap){
    SPI_Start();
    SPI_SwapByte(0x9f);
    // 制造商ID
    * mfid = SPI_SwapByte(DUMMY);
    // 存储器类型
    * memType = SPI_SwapByte(DUMMY);
    // 容量
    * cap = SPI_SwapByte(DUMMY);
    SPI_Stop();
}
​
void W25Q32_WriteEnable(void){
    SPI_Start();
    SPI_SwapByte(0x06);
    SPI_Stop();
}
​
uint8_t w25Q32_ReadStatus(void){
    SPI_Start();
    SPI_SwapByte(0x05);
    uint8_t status = SPI_SwapByte (DUMMY);
    SPI_Stop();
    return status; 
}
​
void W25Q32_WriteDisable(void){
    SPI_Start();
    SPI_SwapByte(0x04);
    SPI_Stop();
}
​
void W25Q32_WaitBusyFinish(void){
    SPI_Start();
    SPI_SwapByte(0x05);
    while (1)
    {
        uint8_t status = SPI_SwapByte(DUMMY);
        if ((status & 0x01) == 0)
        {
            SPI_Stop();
            return;
        }
    }
}
​
void W25Q32_PageWrite(
    uint8_t block_addr,
    uint8_t sector_addr,
    uint8_t page_addr,
    uint8_t page_inner_addr,
    uint8_t * pData,
    uint8_t len
)
{
    //先判断忙不忙
    W25Q32_WaitBusyFinish();
    //再打开写使能
    W25Q32_WriteEnable();
​
    SPI_Start();
    //1.页写的指令是0x02
    SPI_SwapByte(0x02);
    //2.先发24位地址的高8位
    SPI_SwapByte(block_addr);
    //3.再发中间8位(需要将4位的段地址 和4位的页地址合并)
    SPI_SwapByte(sector_addr << 4 | (page_addr & 0x0f));
    //4.最后低8位地址,业内地址不需要合并
    SPI_SwapByte(page_addr);
    //5.有多少数据发多少数据
    for (uint8_t i = 0; i < len; i++)
    {
        SPI_SwapByte(pData[i]);
    }
    //6. 结束通讯
    SPI_Stop();
}
​
void W25Q32_EraseSetor(
    uint8_t block_addr,
    uint8_t sector_addr
){
    W25Q32_WaitBusyFinish();
    W25Q32_WriteEnable();
​
    SPI_Start();
    SPI_SwapByte(0x20);
    SPI_SwapByte(block_addr);
    SPI_SwapByte(sector_addr << 4);
    SPI_SwapByte(0x00);
    SPI_Stop();
}
​
void W25Q32_ReadData(
    uint8_t block_addr,
    uint8_t sector_addr,
    uint8_t page_addr,
    uint8_t page_inner_addr,
    uint8_t * pData,
    uint8_t len
){
    W25Q32_WaitBusyFinish();
​
    SPI_Start();
    SPI_SwapByte(0x03);
​
    //2.先发24位地址的高8位
    SPI_SwapByte(block_addr);
    //3.再发中间8位(需要将4位的段地址 和4位的页地址合并)
    SPI_SwapByte(sector_addr << 4 | (page_addr & 0x0f));
    //4.最后低8位地址,业内地址不需要合并
    SPI_SwapByte(page_addr);
​
    for (uint8_t i = 0; i < len; i++)
    {
        pData[i] = SPI_SwapByte(DUMMY);
    }
    SPI_Stop();
}
相关推荐
懒惰的bit3 小时前
STM32F103C8T6板子使用说明
stm32·单片机·嵌入式硬件
芯岭技术3 小时前
PY32系列单片机离线烧录器,可配置选项字节和上机台批量烧录
单片机·嵌入式硬件
阿川!6 小时前
嵌入式软件--stm32 DAY7 I2C通讯上
stm32·单片机·嵌入式硬件·mcu
O。o.尊都假都6 小时前
socket套接字的超时控制
单片机·嵌入式硬件·网络协议
欢乐熊嵌入式编程8 小时前
智能手表项目的《项目背景与目标说明书》样本文档
嵌入式硬件·目标跟踪·规格说明书·智能手表
海尔辛8 小时前
学习黑客BitLocker与TPM详解
stm32·单片机·学习
海染棠花9 小时前
vscode+platformIO开发STM32(八)
ide·vscode·stm32
上海合宙LuatOS9 小时前
全栈工程师实战手册:LuatOS日志系统开发指南!
java·开发语言·单片机·嵌入式硬件·物联网·php·硬件工程
公子无缘10 小时前
【嵌入式】记一次解决VScode+PlatformIO安装卡死的经历
vscode·stm32·单片机·mcu·platformio
昊昊昊昊昊明10 小时前
十天学会嵌入式技术之51单片机—day-9
单片机·嵌入式硬件·51单片机