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();
}
相关推荐
沐欣工作室_lvyiyi5 分钟前
基于单片机的智能电表设计(论文+源码)
单片机·嵌入式硬件·电能表·数字电能表
半导体老登39 分钟前
新能源汽车核心元件揭秘:二极管、三极管结构与工作原理解析(2/2)
人工智能·单片机·嵌入式硬件·汽车
【云轩】1 小时前
基于STM32与BD623x的电机控制实战——从零搭建无人机/机器人驱动系统
stm32·机器人·无人机
猿~~~3 小时前
STM32的HAL库开发---多通道ADC采集(DMA读取)实验
stm32·单片机·嵌入式硬件
Freak嵌入式3 小时前
开源一款I2C电机驱动扩展板-FreakStudio多米诺系列
嵌入式硬件·嵌入式·智能硬件·开源硬件·micropython·电机驱动·电子模块
kongba0074 小时前
Cursor提示词模板,开发GD32,C语言开发GD32 ARM单片机编程规范提示词 大厂风格代码规范
c语言·arm开发·单片机
LaoZhangGong1235 小时前
STM32的“Unique device ID“能否修改?
c语言·经验分享·stm32·单片机·嵌入式硬件
1101 11017 小时前
STM32-心知天气项目
stm32·单片机·嵌入式硬件
Ronin-Lotus7 小时前
嵌入式硬件篇---数字电子技术中的触发器
嵌入式硬件·fpga开发·触发器·数字电子技术·上位机知识
Ronin-Lotus7 小时前
嵌入式硬件篇---数字电子技术中的时序逻辑
单片机·嵌入式硬件·蓝桥杯·时序分析·数字电子技术